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("BookingCustomers")
.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 inUnchanged
state andChangeObjectState
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.