Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework associations with multiple (separate) keys on view

I'm having problems setting up an Entity Framework 4 model.

A Contact object is exposed in the database as an updateable view. Also due to the history of the database, this Contact view has two different keys, one from a legacy system. So some other tables reference a contact with a 'ContactID' while other older tables reference it with a 'LegacyContactID'.

Since this is a view, there are no foreign keys in the database, and I'm trying to manually add associations in the designer. But the fluent associations don't seem to provide a way of specifying which field is referenced.

How do I build this model?

public class vwContact
{
  public int KeyField { get; set; }
  public string LegacyKeyField { get; set; }
}

public class SomeObject
{
  public virtual vwContact Contact { get; set; }
  public int ContactId { get; set; } //references vwContact.KeyField
}

public class LegacyObject
{
  public virtual vwContact Contact { get; set; }
  public string ContactId { get; set; } //references vwContact.LegacyKeyField
}

ModelCreatingFunction(modelBuilder)
{
  // can't set both of these, right?
  modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
  modelBuilder.Entity<vwContact>().HasKey(x => x.LegacyKeyField);

  modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).??? 
  //is there some way to say which key field this reference is referencing?
}
like image 815
Clyde Avatar asked Jan 25 '12 21:01

Clyde


People also ask

Does Entity Framework support foreign keys?

When you change the relationship of the objects attached to the context by using one of the methods described above, Entity Framework needs to keep foreign keys, references, and collections in sync.

How do I create a composite key in Entity Framework?

You can also configure multiple properties to be the key of an entity - this is known as a composite key. Composite keys can only be configured using the Fluent API; conventions will never set up a composite key, and you can not use Data Annotations to configure one.

Can multiple entities use the same primary key?

Yes. You can have same column name as primary key in multiple tables. Column names should be unique within a table. A table can have only one primary key, as it defines the Entity integrity.


1 Answers

EDIT 2: "New things have come to light, man" - His Dudeness

After a but more experimentation and news, I found using a base class and child classes with different keys will not work by itself. With code first especially, base entities must define a key if they are not explicitly mapped to tables.

I left the suggested code below because I still recommend using the base class for your C# manageability, but I below the code I have updated my answer and provided other workaround options.

Unfortunately, the truth revealed is that you cannot accomplish what you seek without altering SQL due to limitations on EF 4.1+ code first.


Base Contact Class

public abstract class BaseContact
{
   // Include all properties here except for the keys
   // public string Name { get; set; }
}

Entity Classes

Set this up via the fluent API if you like, but for easy illustration I've used the data annotations

public class Contact : BaseContact
{
   [Key]
   public int KeyField { get; set; }
   public string LegacyKeyField { get; set; }
}

public class LegacyContact : BaseContact
{
   public int KeyField { get; set; }
   [Key]
   public string LegacyKeyField { get; set; }    
}

Using the Entities

  1. Classes that reference or manipulate the contact objects should reference the base class much like an interface:

    public class SomeCustomObject
    {
       public BaseContact Contact { get; set; }
    }
    
  2. If later you need to programmatically determine what type you are working with use typeof() and manipulate the entity accordingly.

    var co = new SomeCustomObject(); // assume its loaded with data
    if(co.Contact == typeof(LegacyContact)
        // manipulate accordingly.
    

New Options & Workarounds

  1. As I suggested in comment before, you won't be able to map them to a single view/table anyway so you have a couple options:

    a. map your objects to their underlying tables and alter your "get/read" methods on repositories and service classes pull from the joined view -or-

    b. create a second view and map each object to their appropriate view.

    c. map one entity to its underlying table and one to the view.

Summary

Try (B) first, creating a separate view because it requires the least amount of change to both code and DB schema (you aren't fiddling with underlying tables, or affecting stored procedures). It also ensures your EF C# POCOs will function equivalently (one to a view and one to table may cause quirks). Miguel's answer below seems to be roughly the same suggestion so I would start here if it's possible.

Option (C) seems worst because your POCO entities may behave have unforseen quirks when mapped to different SQL pieces (tables vs. views) causing coding issues down the road.

Option (A), while it fits EF's intention best (entities mapped to tables), it means to get your joined view you must alter your C# services/repositories to work with the EF entities for Add, Update, Delete operations, but tell the Pull/Read-like methods to grab data from the joint views. This is probably your best choice, but involves more work than (B) and may also affect Schema in the long run. More complexity equals more risk.

like image 164
one.beat.consumer Avatar answered Sep 28 '22 03:09

one.beat.consumer