POCO in Entity Framework with snapshot change detection
I have a Visual Studio 2010 generated set of POCO classes (just out of the box POCO templates generated from DB Schema). For a given use case I let the user load an entity (a CRM contact) and act on it - add Phone Numbers (which in itself is a separate entity related by foreign key) and address (also a separate entity) etc. In between postbacks I store the modified entities in ViewState (I don't want to save the changes to the database right away). The problem arises when the user hits the Save button. The main CRM Contact will be saved fine (any changes are detected and saved) but none of the related properties get saved - no matter if it's a new addition or modified EF just ignores it.
How do I force Entity Framework to detect that I have changes in my related properties? I'm using this to save my main contact:
//contact is an instance of CRMContact retrieved from ViewState
if (contact.Id == 0) {
CRMEntities.CRMContacts.AddObject(contact);
} else {
CRMContact orig = CRMEntities.CRMContacts.Where(c => c.Id == contact.Id).FirstOrDefault();
CRMEntities.CRMContacts.ApplyCurrentValues(contact);
}
CRMEntities.SaveChanges(SaveOptions.DetectChangesBeforeSave | SaveOptions.AcceptAllChangesAfterSave);
开发者_开发问答
This works fine for the contact entity but not for my related ones. What do I need to add for phone numbers and emails to be added and/or updated?
Note that I don't want to use proxy-based change tracking.
Thanks
After a lot of trial and error I managed to put together something that works. Note that I have no idea if this is the proper way of doing this. This is a code from a different part of the project. IAHeader
is a main entity, IAAttachment
and IAComment
are both linked to the header by a foreign key:
public static void Save(IAHeader head) {
IAHeader orig = new IAHeader();
if (head.Id == 0) {
IAData.Entities.IAHeaders.AddObject(head);
} else {
orig = IAData.Entities.IAHeaders.Where(h => h.Id == head.Id).FirstOrDefault();
IAData.Entities.IAHeaders.ApplyCurrentValues(head);
foreach (IAComment comm in head.Comments.ToList()) {
if (comm.Id == 0) {
comm.IAHeader = null; //disassociate this entity from the parent, otherwise parent will be re-added
comm.IAId = head.Id;
IAData.Entities.IAComments.AddObject(comm);
} else {
IAComment origComm = orig.Comments.Where(c => c.Id == comm.Id).First();
IAData.Entities.IAComments.ApplyCurrentValues(comm);
}
}
foreach (IAAttachment att in head.Attachments.ToList()) {
if (att.Id == 0) {
att.IAHeader = null; //disassociate this entity from the parent, otherwise parent will be re-added
att.IAId = head.Id;
IAData.Entities.IAAttachments.AddObject(att);
} else {
IAAttachment origAtt = orig.Attachments.Where(a => a.Id == att.Id).First();
IAData.Entities.IAAttachments.ApplyCurrentValues(att);
}
}
}
IAData.Entities.SaveChanges(SaveOptions.DetectChangesBeforeSave | SaveOptions.AcceptAllChangesAfterSave);
}
Lots of improvements can be made, obviously, but this is what I came up with so far that works for my scenario. The important part that was mostly messing me up is having to disassociate my navigation properties from my main entity, otherwise I would either get the "entity key already exists" error or the main entity would be saved twice.
I don't see where you've changed any related values. The docs for ObjectContext.ApplyCurrentValues say:
Copies the scalar values from the supplied object into the object in the ObjectContext that has the same key.
(Emphasis added.)
Since you've made no other changes, I'd say there is nothing to detect.
精彩评论