I'm running into a strange situation:
public Class A
{
public int Id { get; set; }
}
public class B
{
public int Id { get; set; }
[ForeignKey("A1")]
public virtual int A_Id { get; set; }
public virtual A A1 { get; set; }
}
When I update an entity of type B, by modifying A1, A1.Id is updated to the new entity Id, but B.A_Id still remains assigned to the old Id. This causes Entity Framework to throw an error.
I had read that by marking both properties as virtual, EF change tracker would automatically detect the change and update the related foreign key, but this doesn't happen for me. What else can I check?
How you map the relationship between A & B will determine the behavior. You do not need to mark the A_Id as Virtual.
Provided your real schema is mapped out like above, it should just work as a typical many-to-one mapping. (Many B's can reference an A) The Key point is that the A_Id on B will not be updated until you call SaveChanges on the DbContext. Once SaveChanges is called, the FK will be updated to reflect the different A.
For instance:
using (var context = new TestDbContext())
{
var a = context.As.Single(x => x.Id == 1);
var b = context.Bs.Single(x => x.Id == 1);
Assert.AreEqual(2, b.A_id);
b.A = a;
context.SaveChanges();
Assert.AreEqual(1, b.A_Id);
}
When B was loaded it was referencing an A with ID = 2. We load A ID #1 and associate it to B using the reference. Once SaveChanges is called we can assert that B's A_Id now reflects the link to A ID 1.
Beyond that you may be encountering issues depending on how/where your A and B references are loaded. Ensure that they are coming from the same DbContext instance. A big problem I see people having is by passing references to entities around. This often leads to exceptions when trying to update references within the scope of a DbContext using entities that were loaded elsewhere.
If you are still running into issues or suspect something like above, include a copy of your exception message and actual code and we can take it from there.
Related
This could be a duplicate question but a lot of searching for the words in the title only got me a lot of unrelated results.
I have an entity that's roughly set up like this:
public abstract class A
{
public GUID AId { get; set; }
public string SomeProperty { get; set; }
}
public class B : A
{
public string SomeOtherProperty { get; set; }
}
The context has public DbSet<B> BInstances { get; set; } for B objects. In OnModelCreating, the mapping has A set to ignored and B is mapped to a table called TableB.
The AId field is not auto-generated (not an identity field) but it's set to be primary key, both in the database and in the mapping. In the database, the field is defined as a non-null uniqueidentifier with no default.
At runtime, I'm loading an instance of B using its key (_token is just a CancellationToken):
var b = await (dbCtx.BInstances.FirstOrDefaultAsync(e => e.AId), _token));
Then, a property of b is set and I try to save it back to database:
b.SomeOtherProperty = "some new text";
await (dbCtx.SaveChangesAsync(_token));
At this point, I'm getting a Violation of PRIMARY KEY constraint error from the database, stating that the value of AId cannot be inserted because it'd be a duplicate. Of course, the ID is already in the database, I loaded the entity from there, using the ID. For some reason, EF generates an INSERT statement, not an UPDATE and I don't understand why.
When I check dbCtx.Entry(b).State, it's already set to EntityState.Modified. I'm at a loss - can someone point out what I'm doing wrong? I never had issues with updating entities before but I haven't used EF with GUID primary keys (usually I use long primary keys).
I'm using EF 6 and .NET Framework 4.7.1.
Thank you all for the suggestions - this turned out to be a mapping problem that I caused.
In my OnModelCreating() call, I called MapInheritedProperties() on a type that didn't inherit from a base class (other than object, of course) - this seems to have triggered a problem. Other entities that do share a base class worked fine with the mapping call.
I also called ToTable() directly against the entity class - this broke my table mapping for reasons I do not understand. Once I moved that call inside Map(), it started working as expected.
So I went from this:
entity.ToTable("tablename");
to this:
entity.Map(m => m.ToTable("tablename"));
to solve the problem.
Hopefully this will be useful for future readers.
try this
b.SomeOtherProperty = "some new text";
dbCtx.BInstances.AddOrUpdate(b);
await (dbCtx.SaveChangesAsync(_token));
AddorUpdate will update your b instance if it is already added.
I have a mental debate with myself every time I start working on a new project and I am designing my POCOs. I have seen many tutorials/code samples that seem to favor foreign key associations:
Foreign key association
public class Order
{
public int ID { get; set; }
public int CustomerID { get; set; } // <-- Customer ID
...
}
As opposed to independent associations:
Independent association
public class Order
{
public int ID { get; set; }
public Customer Customer { get; set; } // <-- Customer object
...
}
I have worked with NHibernate in the past, and used independent associations, which not only feel more OO, but also (with lazy loading) have the advantage of giving me access to the whole Customer object, instead of just its ID. This allows me to, for example, retrieve an Order instance and then do Order.Customer.FirstName without having to do a join explicitly, which is extremely convenient.
So to recap, my questions are:
Are there any significant disadvantages in
using independent associations? and...
If there aren't any, what
would be the reason of using foreign key associations at all?
If you want to take full advantage of ORM you will definitely use Entity reference:
public class Order
{
public int ID { get; set; }
public Customer Customer { get; set; } // <-- Customer object
...
}
Once you generate an entity model from a database with FKs it will always generate entity references. If you don't want to use them you must manually modify the EDMX file and add properties representing FKs. At least this was the case in Entity Framework v1 where only Independent associations were allowed.
Entity framework v4 offers a new type of association called Foreign key association. The most obvious difference between the independent and the foreign key association is in Order class:
public class Order
{
public int ID { get; set; }
public int CustomerId { get; set; } // <-- Customer ID
public Customer Customer { get; set; } // <-- Customer object
...
}
As you can see you have both FK property and entity reference. There are more differences between two types of associations:
Independent association
It is represented as separate object in ObjectStateManager. It has its own EntityState!
When building association you always need entitites from both ends of association
This association is mapped in the same way as entity.
Foreign key association
It is not represented as separate object in ObjectStateManager. Due to that you must follow some special rules.
When building association you don't need both ends of association. It is enough to have child entity and PK of parent entity but PK value must be unique. So when using foreign keys association you must also assign temporary unique IDs to newly generated entities used in relations.
This association is not mapped but instead it defines referential constraints.
If you want to use foreign key association you must tick Include foreign key columns in the model in Entity Data Model Wizard.
Edit:
I found that the difference between these two types of associations is not very well known so I wrote a short article covering this with more details and my own opinion about this.
Use both. And make your entity references virtual to allow for lazy loading. Like this:
public class Order
{
public int ID { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; } // <-- Customer object
...
}
This saves on unnecessary DB lookups, allows lazy loading, and allows you to easily see/set the ID if you know what you want it to be. Note that having both does not change your table structure in any way.
Independent association doesn't work well with AddOrUpdate that is usually used in Seed method. When the reference is an existing item, it will be re-inserted.
// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);
// New order.
var order = new Order { Id = 1, Customer = customer };
db.Set<Order>().AddOrUpdate(order);
The result is existing customer will be re-inserted and new (re-inserted) customer will be associated with new order.
Unless we use the foreign key association and assign the id.
// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);
// New order.
var order = new Order { Id = 1, CustomerId = customer.Id };
db.Set<Order>().AddOrUpdate(order);
We have the expected behavior, existing customer will be associated with new order.
I favour the object approach to avoid unnecessary lookups. The property objects can be just as easily populated when you call your factory method to build the whole entity (using simple callback code for nested entities). There are no disadvantages that I can see except for memory usage (but you would cache your objects right?). So, all you are doing is substituting the stack for the heap and making a performance gain from not performing lookups. I hope this makes sense.
I have seen numerous questions and advice on calling dbContext.SaveChanges multiple Times in a transaction.
Some say this should be avoided. This in depth post is really worth a read http://mehdi.me/ambient-dbcontext-in-ef6/
In my particular scenario entity B has Id reference to Entity A
During a creation scenario i Create A and call savechanges in order to get A.Id assigned by database.
Then I create Entity B like new B(A.Id,....)
and call savechanges again. It could look like this in pseudo code
using(var tx = dbContext.BeginTransaction())
{
var a = new A();
dbContext.Add(a); //a.Id is null
dbContext.saveChanges(); // a.Id has now been initialized
var b = new B(a.Id); //I want to create b in a valid state so a.Id cannot be null
dbContext.SaveChanges();
tx.Commit();
}
(I know the pseudo code lacks exception handling logic...)
Why is this a problem ?
Is it because the savechanges cannot be rolled back?
I know I could remodel but that is not possible because we share database with some legacy systems, so the database cannot easily be changed, if possible at all!
What is an alternative solution ?
Use a navigation property like this:
public class A
{
[Key]
public int Id { get; set; }
public int BId { get; set; }
[ForeignKey("BId")]
public B B { get; set; }
}
public class B
{
[Key]
public int Id { get; set; }
}
then simply assign the newly created B to A's navigation property:
using (var transaction = dbContext.BeginTransaction())
{
var a = new A();
a.B = new B();
dbContext.Add(a);
dbContext.saveChanges();
transaction.Commit();
}
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 two classes I want to store in EF Code First. A Building has a maintainer, and a list of people working there. The mainainer doesn't have to work in the building.
At my first attempt I just had
public class Person
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
and
public class Building
{
public virtual Guid Id { get; set; }
public virtual List<Person> WorksHere { get; set; }
public virtual Person MaintainedBy { get; set; }
public virtual String Address { get; set; }
}
This gives me two tables with the base properties, and a Building_Id on People, and a MaintainedBy_Id on Buildings.
When I run a test program
using (TestContext tc = new TestContext())
{
Person m1 = tc.persons.Create();
m1.Name = "maintainB1";
m1.Id = Guid.NewGuid();
Person m2 = tc.persons.Create();
m2.Name = "maintainB2";
m2.Id = Guid.NewGuid();
Building b1 = tc.buildings.Create();
b1.Address = "building1";
b1.Id = Guid.NewGuid();
tc.buildings.Add(b1);
Building b2 = tc.buildings.Create();
b2.Address = "building1";
b2.Id = Guid.NewGuid();
tc.buildings.Add(b2);
b1.MaintainedBy = m1;
b2.MaintainedBy = m2;
if (b1.WorksHere == null) b1.WorksHere = new List<Person>();
if (b2.WorksHere == null) b2.WorksHere = new List<Person>();
b1.WorksHere.AddRange(new List<String>() { "e11", "e12", "e13" }.Select(s =>
{
Person p = new Person();
p.Id = Guid.NewGuid();
p.Name = s;
return p;
}));
b2.WorksHere.AddRange(new List<String>() { "e21", "e22", "e23" }.Select(s =>
{
Person p = new Person();
p.Id = Guid.NewGuid();
p.Name = s;
return p;
}));
b1.WorksHere.Add(m2);
b2.WorksHere.Add(m1);
tc.SaveChanges();
}
}
I get an exception: "An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details."
with innerexception:
"Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values."
I would prefer not to expose any more properties on my Poco's, because, well, they are Poco's, and I have no use for those properties. If I have to to satisfy Code First model generation, than that'll have to do, but if at all possible, I'd like to have that away from my Poco's in a mapping class.
How do I fix this?
So, this issue arises because you have a circular relationship between your entities, which causes EF to give up when trying to resolve all the inserts in a single call to SaveChanges, and raise the exception you are seeing.
To understand why it can't handle this situation, lets think about what happens in the database when trying to save the entities.
Using your code, you can make it run without errors by commenting out the last line before SaveChanges is called, but then person m1 won't be working in building b2, so this is not what you want.
b1.WorksHere.Add(m2);
//b2.WorksHere.Add(m1); <-- When this is removed it works..
tc.SaveChanges();
However, EF is able to run this by creating the following inserts in the database:
Insert person m1. Leave the FK to the buildings table as null, because m1 works nowhere.
Insert building 'b1'. Use id of ´m1´ as the FK, because m1 maintains b1.
Insert person m2. Use id of b1 as the FK, because b1 is where m2 works.
Insert building b2. Use id of m2 as the FK, because m2 maintains b2.
Now it's pretty easy to see why it doesn't work when you include the line that makes m1 work in b2.
In the first insert above, EF isn't able to leave the FK as null, because you are telling it that it needs to point to a building, but that building has not been inserted yet, so it can't create the FK pointing back to it.
That is always a problem in EF when entities have circular dependencies. When both depend on each other, the inserts can't be created in a single commit.
The solution to your problem is simply to make two calls to SaveChanges. If you call it right before making m1 work in b2, and then again after that, you will get the right kind of behavior.
b1.WorksHere.Add(m2);
tc.SaveChanges(); <-- Create inserts. FK in m1 is null because he works nowhere yet.
b2.WorksHere.Add(m1);
tc.SaveChanges(); <-- Updates FK in m1 to point to b2.
Future support in Entity Framework
It seems like this issue will be resolved in a future version of EF.
It is reasonable to expect from a ORM that it should be able to handle inserting a parent, inserting a child and then updating the parent with the newly created child id.
You can read more about it and vote for the feature to be implemented on Microsoft Connect.
There is also some info on the EF CodePlex site.
Try mapping the relationship. In your context which is derived from DbContext, override the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Building>()
.HasMany(m => m.LivesHere)
.WithRequired()
.Map(n => n.MapKey("Home_Id"));
}
I would also try using [Key] to denote the the primary keys in the Entities, but I'm not exactly sure if you have to do that.
Like so:
public class Building
{
[Key]
public virtual Guid Id { get; set; }
public virtual List<Person> WorksHere { get; set; }
public virtual Person MaintainedBy { get; set; }
public virtual String Address { get; set; }
}
I can't try this out right now, but I hope it helps.