I've got the following EntityObject-based EF model (properties written here as fields for keeping code cleaner):
Booking
{
int BookingID;
EntityCollection<BookingCustomer> BookingCustomers;
string Notes;
}
BookingCustomer
{
Customer Customer;
Booking Booking;
int CustomerID;
int BookingID;
string Notes;
}
Customer
{
int CustomerID;
string Name;
}
I'm loading the object graph in a detached manner:
Booking existingBooking;
using(MyContext ctx = new MyContext())
{
ctx.Bookings.MergeOption = MergeOption.NoTracking;
ctx.BookingCustomers.MergeOption = MergeOption.NoTracking;
ctx.Customers.MergeOption = MergeOption.NoTracking;
existingBooking = ctx.Bookings
.Include("BookingCust开发者_如何学Goomers")
.Include("BookingCustomers.Customer")
.First();
}
Modifying booking and customer data:
existingBooking.Notes = "Modified";
existingBooking.BookingCustomers.First().Name += " Edited";
Saving it:
using(MyContext ctx = new MyContext())
{
ctx.Bookings.Attach(existingBooking);
ctx.ObjectStateManager.GetObjectStateEntry(existingBooking ).SetModified();
ctx.Refresh(RefreshMode.ClientWins, existingBooking);
ctx.SaveChanges();
}
I get a new Customer record created (instead of existing record updated).
I've also tried this:
using(MyContext ctx = new MyContext())
{
ctx.Customers.Attach(existingBooking.BookingCustomers.First());
ctx.ObjectStateManager.GetObjectStateEntry(existingBooking.BookingCustomers.First()).SetModified();
ctx.Refresh(RefreshMode.ClientWins, existingBooking.BookingCustomers.First());
ctx.Bookings.Attach(existingBooking);
ctx.ObjectStateManager.GetObjectStateEntry(existingBooking ).SetModified();
ctx.Refresh(RefreshMode.ClientWins, existingBooking);
ctx.SaveChanges();
}
but getting exception on line ctx.Customers.Attach(existingBooking.BookingCustomers.First());
:
System.InvalidOperationException: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
How can i make it work?
It is not about refreshing. If you modify the graph in detached state EF is not able to find what changes you did. You must manually set the state of each modified entity or relation. Attach
will put whole graph in Unchanged
state and ChangeObjectState
affects only single entity in the graph.
You found that the easiest way is to load the whole graph again during saving and manually merge your changes to attached graph. Especially if you want to delete some relations / entities or do complex operations with many to many relation this would be handy.
Ladislav is correct. However, there is one more option that you should consider which would solve this problem and still allow you to retain the detatched model - self tracking POCOs. There's a T4 template (it's either bundled with VS2010 or is available as a download) which allows an object graph to track its state (this includes adding / removing objects into the graph as well as what properties with an object have changed) so that you can reattach the graph having made modifications; EF4 will figure out the changes and apply those to object state manager for you.
精彩评论