views:

567

answers:

2

Hi,

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

A: 

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.

Craig Stuntz
I tried looping through my navigation properties and doing `AddObject` or `ApplyCurrentValues` on each individually, however, with that code in place I get `An item with the same key has already been added` exception when saving changes, so I guess my question is how to **properly** create a "save" function in this situation
Marek Karbarz
And changes are done to the navigation property in various places of the application - what I posted is just a part of my "save" method - there's no doubt that there are changes to be detected
Marek Karbarz
You should show that code, then. All I can see is what you've posted so far; I can't diagnose issues with code I've never seen. Try reducing the problem to a simple test case.
Craig Stuntz
I don't see how posting code that modifies my entities is at all relevant or helpful. What's important is that the entities are modified and need to be saved and that's the code that I was missing (I have since found a solution)
Marek Karbarz
+1  A: 

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.

Marek Karbarz