Assume I have following database tables with 1:1 mapping
Table dbo.Foo with PrimaryKey FooRowId
Table dbo.Bar with PrimaryKey BarRowId
No foreign keys exist on either table.
Using EF, I defined models as follows.
Modeling Foo table
public class Foo
{
[Key]
public long FooRowId { get; set; }
// Navigation
public virtual Bar Bar { get; set; }
}
Modeling Bar table
public class Bar
{
[Key]
public long BarRowId { get; set; }
// Navigation
public virtual Foo Foo { get; set; }
}
This gives me navigation property related error as follows.
Unable to determine the principal end of an association between the types 'MyOrg.Models.Foo'
and 'MyOrg.Models.Bar'. The principal end of this association must be explicitly
configured using either the relationship fluent API or data annotations.
One way to fix is by standardizing property names as follows.
On Foo
public class Foo
{
[Key]
[Column("FooRowId")]
public long FooId { get; set; }
// Navigation
public virtual Bar Bar { get; set; }
}
On Bar
public class Bar
{
[Key]
[Column("BarRowId")]
public long BarId { get; set; }
// Navigation
public virtual Foo Foo { get; set; }
}
However, requirement states I must keep the original properties FooRowID and BarRowId. Given this constraint, how to make the navigation properties work?
Although Attributes seem easy to use, it limits reuse of your classes in other databases.
Suppose you have created a class BillingAddress according to the standards of your company (country maybe). You want to use this class in two different DbContexts, each to represent their own database. The BillingAddress in database 1 has a primary key in column "MyPrimaryKey", the BillingAddress in database 2 has a primary key in column "Id". You can't solve that using attributes.
The names of columns and tables, the relation between tables is part of the database. Therefore this ought to be described in the DbContext. If you don't use Attributes in the DbSet classes, you'll be able to use the same class in different databases.
So let's write your table design in fluent Api
See:
Fluent API to configure tables, like table name
Fluent API to configure properties, like primary key, column names, foreign keys
Fluent API to configure relations between tables, like one-to-many
Below I give examples for all three categories:
Configure the table name and the primary key of the table
Configure one of the properties: column name
Configure relation between tables, for example one-to-many
Keep in mind: if you use code first, and you stick to the entity framework code first conventions, none of this is needed. As long as you stick to these conventions, Entity Framework will be very capable in detecting primary keys, foreign keys, relations between tables.
But if your tables are different, the following fluent API is needed in your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure entity table FOO:
var entityFoo = modelBuilder.Entity<Foo>();
// example: configure Name and primary key of foo table
entityFoo.ToTable("MySpecialFooTable");
entifyFoo.HasKey(foo => foo.FooRowId);
// example: configure property Name is in column FooName:
entityFoo.Property(foo => foo.Name).HasColumnName("MyFooName");
// example: one-to-many relation between foo and bar
// every Foo has zero or more Bars in property MyBars,
// every Bar belongs to exactly one For in property MyFoo
// using foreign key MyFooId:
entityFoo.HasMany(foo => foo.MyBars) // Foo has zero or more Bars in MyBars
.WithRequired(bar => bar.MyFoo) // every Bar belongs to one Foo
.HasForeignKey(bar => bar.MyFooId); // using foreign key MyFooId
}
Most posts around the ObjectStateManager are true-duplicate issues based on unique primary keys. My problem is that my table does Not have a primary key, but it does have multiple foreign keys, one of which is Nullable.
class MyObject
{
int Key1;
int? Key2;
}
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = null; });
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = 2000; }); ****
It blows up on the second call, even though this is a unique row in the database.
Any thoughts on how to get around this? or enforce checking of BOTH keys?
As #BenAaronson mentioned, you should have a surrogate, primary key in your table in this instance. Entity Framework quite simply cannot deal with entities that have no primary key defined—in fact, I'm surprised your code even compiled/ran. Perhaps your real code with real class and property names caused EF to infer a primary key using its default conventions. For example:
public class MyClass
{
public int MyClassId { get; set; }
public int MyOtherClassId { get; set; }
}
In the code above, even without explicitly declaring it, EF would assume that the MyClassId property is the primary key for the class MyClass, even if that may not have been your intention.
If EF can't infer a primary key and one is not explicitly provided, then your code wouldn't compile (or at most, it wouldn't run).
So looking at your code, what appears to be happening is that EF inferred a primary key somehow (in your example above, Key1). You then tried to attach a new object to your context:
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = null; });
This results in the context adding a new MyObject instance whose primary key value is 100 and whose Key2 property is null.
Next, you attempt to attach another item to the context:
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = 2000; });
What this does is attempt to add a new item to the context whose primary key is 100, and this fails. This is because you already have an object being tracked by the context whose primary key value is 100 (executed by the first statement above).
Since you need to allow possibly null values for the Key2 property, you can't use a composite primary key, as you already stated. So you will need to follow #BenAaronson's advice and add a surrogate primary key:
public class Object
{
// Alternatively, you can use a mapping class to define the primary key
// I just wanted to make the example clear that this is the
// surrogate primary key property.
[Key]
private int ObjectID { get; set; } // IIRC, you can make this private...
public int Key1 { get; set; }
public int Key2 { get; set; }
}
Now, you can do the following:
context.MyTable.Add(new MyObject() { Key1 = 100, Key2 = null; });
context.MyTable.Add(new MyObject() { Key1 = 100, Key2 = 2000; });
Notice I used the Add method and not Attach. That's because when using Attach, the context is assuming that you're adding an object to the context which already exists in the database, but which was not brought into the context via a query; instead, you had a representation of it in memory, and at this point, you want the context to start tracking changes made to it and update the object in the database when you call context.SaveChanges(). When using the Attach property, the context adds the object in the Unmodified state. That's not what we want. We have brand new objects being added to the context. So we use Add. This tells the context to add the item in the Added state. You can make any changes you want to it. Since it's a new item, it will be in the Added state until you call context.SaveChanges() and the item is persisted to your data store, at which time, it's state will be updated to Unmodified.
One more thing to note at this point. If this is a "many-to-many" table, you should never need to manually add rows to this type of join table in EF (there are some caveats to this statement, see below). Instead, you should setup a mapping between the two objects whose relationship is many-to-many. It's possible to specify an optional many-to-many relationship, too. If the first object has no relationship to the second, there should be no row in the join table for the first object, and vice versa.
Regarding join table caveats as alluded to above: if your join-tables (i.e. many-to-many mapping tables) are simple (meaning the only columns in the table are those columns mapping one ID to the related ID), then you won't even see the join-table as part of your object model. This table is managed by EF in the background through navigation properties on the related objects. However, if the join-table contains properties other than just the ID properties of the related objects (and, this implies you have an existing database or explicitly structured your object model this way), then you will have an intermediate entity reference. For example:
public class A
{
public int ID { get; set; }
}
public class B
{
public int ID { get; set; }
}
public class AToB
{
// Composite primary key
[Key]
public int IdA { get; set; }
[Key]
public int IdB { get; set; }
public A SideA { get; set; }
public B SideB { get; set; }
// An additional property in the many-to-many join table
public DateTime Created { get; set; }
}
You would also have some mappings to tell EF how to wire up the foreign key relationships. What you'd wind up with in your object model then, is the following:
myA.AToB.SideB // Accesses the related B item to this A item.
myA.AToB.Created // Accesses the created property of AToB, telling you
// when the relationship between A and B was created.
In fact, if you have non-trivial join tables such as this example, EF will always include them in your object model when generating its model from an existing database.
I would strongly suggest that you check out Julie Lerman's and Rowan Miller's books on programming Entity Framework.
i have the following problem:
I have 2 classes that i wan't to use with code first to generate a database.
public class Chart
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public State Initial { get; set; }
public virtual ICollection<State> States { get; set; }
}
and
public class State
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
}
I also want the Initial property of the Chart class to point to it's initial State.
But whatever i try i won't get it to work. I've used the data annotations, fluent api and foreignkey properties but keep running into problems like:
"Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values."
To make this even more interessting i would also like the initial state to be one of the states of the chart class.
Can someone please help me?
UPDATE:
I found a piece of the puzzle:
The code first framework tries to create 2 tables.
Charts: with columns -> Id, Initial_Id
States: with columns -> Id, Name, Chart_Id
Chart.Initial_Id references to the States table and State.Chart_Id references to the Charts table. Now this introduces the circular problem when cascading deleting or insert etc...
Hmmm...
This is just a shot in the dark, but do you think it has something to do with the fact that the properties for both classes (presumably, what you want for your primary key) has a guid called id? What about renaming it to StateId and ChartId? If nothing else, I think this is just better naming practice...
Full disclosure: I don't use CF EF, but just stumbled across this as an interesting post
EF can't currently perform this mapping exactly as you have it written because it ends up with a combination of circular dependencies and database-generated keys for which update/insert orderings cannot be determined.
However, reading between the lines I'll make the assumption that maybe State entities are supposed to be shared between multiple Chart entities. In other words, multiple Charts can refer to the same State. In this case, what you really have is a many-to-many relationship between Chart and State, and you can override OnModelCreating context to tell EF this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Chart>()
.HasMany(s => s.States)
.WithMany();
}
Now something like this will work:
using (var context = new ChartContext())
{
var state1 = new State { Name = "State1" };
var state2 = new State { Name = "State2" };
var chart =
new Chart
{
Initial = state1,
States = new List<State> { state1, state2 }
};
context.Charts.Add(chart);
context.SaveChanges();
}
As you can see, Chart now has an initial state and a collection of states, and the initial state is one of the collection of states.
If actually each State is associated with one and only Chart entity, then you'll need to figure out something else--there are probably workarounds to make this work, but not as easy to come up with as the solution above.
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?
}
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
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; }
}
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
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.
Edit I'm not sure this is actually possible, and this is why:
The assumption is that a foreign key references a primary key. What you've got is two fields which are both acting as primary keys of vwContact, but depending on which object you ask it's a different field that's the primary key. You can only have one primary key at once, and although you can have a compound primary key you can't do primary key things with only half of it - you have to have a compound foreign key with which to reference it.
This is why Entity Framework doesn't have a way to specify the mapping column on the target side, because it has to use the primary key.
Now, you can layer some more objects on top of the EF entities to do some manual lookup and simulate the navigation properties, but I don't think you can actually get EF to do what you want because SQL itself won't do what you want - the rule is one primary key per table, and it's not negotiable.
From what you said about your database structure, it may be possible for you to write a migration script which can give the contact entities a consistent primary key and update everything else to refer to them with that single primary key rather than the two systems resulting from the legacy data, as you can of course do joins on any fields you like. I don't think you're going to get a seamlessly functional EF model without changing your database though.
Original Answer That Won't Work
So, vwContact contains a key KeyField which is referenced by many SomeObjects and another key LegacyKeyField which is referenced by many LegacyObjects.
I think this is how you have to approach this:
Give vwContact navigation properties for SomeObject and LegacyObject collections:
public virtual ICollection<SomeObject> SomeObjects { get; set; }
public virtual ICollection<LegacyObject> LegacyObjects { get; set; }
Give those navigation properties foreign keys to use:
modelBuilder.Entity<vwContact>()
.HasMany(c => c.SomeObjects)
.WithRequired(s => s.Contact)
.HasForeignKey(c => c.KeyField);
modelBuilder.Entity<vwContact>()
.HasMany(c => c.LegacyObjects)
.WithRequired(l => l.Contact)
.HasForeignKey(c => c.LegacyKeyField);
The trouble is I would guess you've already tried this and it didn't work, in which case I can't offer you much else as I've not done a huge amount of this kind of thing (our database is much closer to the kinds of thing EF expects so we've had to do relatively minimal mapping overrides, usually with many-to-many relationships).
As for your two calls to HasKey on vwContact, they can't both be the definitive key for the object, so it's either a compound key which features both of them, or pick one, or there's another field you haven't mentioned which is the real primary key. From here it's not really possible to say what the right option there is.
You should be able to do this with two different objects to represent the Contact view.
public class vwContact
{
public int KeyField { get; set; }
public string LegacyKeyField { get; set; }
}
public class vwLegacyContact
{
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 vwLegacyContact Contact { get; set; }
public string ContactId { get; set; } //references vwLegacyContact.LegacyKeyField
}
ModelCreatingFunction(modelBuilder)
{
// can't set both of these, right?
modelBuilder.Entity<vwContact>().HasKey(x => x.KeyField);
modelBuilder.Entity<vwLegacyContact>().HasKey(x => x.LegacyKeyField);
// The rest of your configuration
}
I have tried everything that you can imagine, and found that most solutions won't work in this version of EF... maybe in future versions it supports referencing another entity by using an unique field, but this is not the case now. I also found two solutions that work, but they are more of a workaround than solutions.
I tried all of the following things, that didn't work:
Mapping two entities to the same table: this is not allowed in EF4.
Inheriting from a base that has no key definitions: all root classes must have keys, so that inherited classes share this common key... that is how inheritance works in EF4.
Inheriting from base class that defines all fields, including keys, and then use modelBuilder to tell wich base-properties are keys of the derived types: this doesn't work, because the methos HasKey, Property and others that take members as parameters, must reference members of the class itself... referencing properties of a base class is not allowed. This cannot be done: modelBuilder.HasKey<MyClass>(x => x.BaseKeyField)
The two things that I did that worked:
Without DB changes: Map to the table that is source of the view in question... that is, if vwContact is a view to Contacts table, then you can map a class to Contacts, and use it by setting the key to the KeyField, and another class mapping to the vwContacts view, with the key being LegacyKeyField. In the class Contacts, the LegacyKeyField must exist, and you will have to manage this manually, when using the Contacts class. Also, when using the class vwContacts you will have to manually manage the KeyField, unless it is an autoincrement field in the DB, in this case, you must remove the property from vwContacts class.
Changing DB: Create another view, just like the vwContacts, say vwContactsLegacy, and map it to a class in wich the key is the LegacyKeyField, and map vwContacts to the original view, using KeyField as the key. All limitations from the first case also applies: the vwContacts must have the LegacyKeyField, managed manually. And the vwContactsLegacy, must have the KetField if it is not autoincrement idenitity, otherwise it must not be defined.
There are some limitations:
As I said, these solutions are work-arounds... not real solutions, there are some serious implications, that may even make them undesirable:
EF does not know that you are mapping two classes to the same thing. So when you update one thing, the other one could be changed or not, it depends if the objects is cached or not. Also, you could have two objects at the same time, that represents the same thing on the backing storage, so say you load a vwContact and also a vwContactLegacy, changes both, and then try to save both... you will have to care about this yourself.
You will have to manage one of the keys manually. If you are using vwContacts class, the KeyFieldLegacy is there, and you must fill it. If you want to create a vwContacts, and associate is with a LegacyObject, then you need to create the reference manually, because LegacyObject takes a vwContactsLegacy, not a vwContacts... you will have to create the reference by setting the ContactId field.
I hope that this is more of a help than a disillusion, EF is a powerfull toy, but it is far from perfect... though I think it's going to get much better in the next versions.
I think this may be possible using extension methods, although not directly through EF as #Matthew Walton mentioned in his edit above.
However, with extension methods, you can specify what to do behind the scenes, and have a simple call to it.
public class LegacyObject
{
public virtual vwContact Contact { get; set; }
public string ContactId { get; set; } //references vwContact.LegacyKeyField
}
public class LegacyObjectExtensions
{
public static vwContact Contacts(this LegacyObject legacyObject)
{
var dbContext = new LegacyDbContext();
var contacts = from o in legacyObject
join c in dbContext.vwContact
on o.ContactId == c.LegacyKeyField
select c;
return contacts;
}
}
and
public class SomeObject
{
public virtual vwContact Contact { get; set; }
public int ContactId { get; set; } //references vwContact.KeyField
}
public class SomeObjectExtensions
{
public static vwContact Contacts(this SomeObject someObject)
{
var dbContext = new LegacyDbContext();
var contacts = from o in someObject
join c in dbContext.vwContact
on o.ContactId == c.KeyField
select c;
return contacts;
}
}
Then to use you can simply do like this:
var legacyContacts = legacyObject.Contacts();
var someContacts = someObject.Contacts();
Sometimes it makes more sense to map it from the other end of the relationship, in your case:
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).WithMany().HasForeignKey(u => u.LegacyKeyField);
however this will require that u.LegacyKeyField is marked as a primary key.
And then I'll give my two cents:
if the Legacy db is using LegacyKeyField, then perhaps the legacy db will be read only. In this case we can create two separate contexts Legacy and Non-legacy and map them accordingly. This can potentially become a bit messy as you'd have to remember which object comes from which context. But then again, nothing stops you from adding the same EF code first object into 2 different contexts
Another solution is to use views with ContactId added for all other legacy tables and map them into one context. This will tax performance for the sake of having cleaner context objects, but this can be counteracted on sql side: indexed views, materialized views, stored procs, etc. So than LEGACY_OBJECT becomes VW_LEGACY OBJECT with CONTACT.ContactId brought over, then:
modelBuilder.Entity<LegacyObject>().ToTable("VW_LEGACY_OBJECT");
modelBuilder.Entity<LegacyObject>().HasRequired(x => x.Contact).WithMany().HasForeignKey(u => u.ContactId);
I personally would go with creating "mapper views" with CustomerId on legacy tables, as it's cleaner from c# layer perspective and you can make those views look like real tables. It is also difficult to suggest a solution without knowing what exactly is the scenario that you have a problem with: querying, loading, saving, etc.
I have two tables in a database. One is for a member and one is for a client. The client table has two columns for who created the row, and who has modified the row. Foreign keys were set up from each column to map back to the member table. All of this makes sense until
one runs Entity Framework against the database and I get the following code generated for me.
public Member()
{
public virtual ICollection<Client> Clients { get; set; }
public virtual ICollection<Client> Clients1 { get; set; }
}
public Client()
{
public virtual Member MemberForCreated { get; set; }
public virtual Member MemberForModified { get; set; }
}
My question is why would Entity Framework think to make a backing collection in the member table for each foreign key relationship to the client table? Do I really need this relationship or is this something that I can remove? Any information would be useful.
As a side note: These collections and relationships are found in the .edmx file under the navigation properties collection of the entities.
EF relationships are bidirectional by default. You can remove either direction if you don't need it.
You can also rename them. You might, e.g., want to call them Member.ClientsCreated and Member.ClientsModified.
Julie Lerman has a video examining unidirectional relationships.