Attach entities to the context (Dynamics CRM 2015)

 

Updated: January 15, 2016

Applies To: Dynamics CRM 2015

For the OrganizationServiceContext to properly track its changes to entities and relationships, the entities and relationships need to be attached to the data context. When you follow the typical pattern of querying for entities, updating the entities, and saving the changes, it is not necessary to explicitly control the attaching and detaching of entities. That is, entities are automatically attached to the context when they are retrieved. One behavior to be aware of is that all entities are detached by the OrganizationServiceContext after calling the SaveChanges method. To continue using the data context against previously retrieved entities, the entities need to be reattached. This can be accomplished by re-querying a new set of entities or by explicitly calling the Attach method on the entities. Developer Extensions for Microsoft Dynamics CRM 2015 provides a ReAttach method that ensures that the entities are in the correct state when reattached. This is shown in the following example.

using Microsoft.Xrm.Client;

using (var service = new OrganizationService(connection))
using (var context = new OrganizationServiceContext(service))
{
var contact = context.CreateQuery<Contact>().First(c => c.FirstName == "Bob");
contact.JobTitle = "Developer";
context.UpdateObject(contact);
context.SaveChanges();

// contact is no longer attached at this point so reattach it
context.Reattach(contact);

contact.EMailAddress1 = "bob@contoso.com";
context.UpdateObject(contact);
context.SaveChanges();
}

The preference is to apply all modifications under a single call to SaveChanges, and then dispose the context, to avoid the need to reattach.

The CrmOrganizationServiceContext simplifies this scenario by automatically reattaching entities after calling SaveChanges. Note that only the entities that are used as input parameters of an AddObject or UpdateObject operation are flagged to be reattached.

using (var service = new OrganizationService(connection))
using (var context = new CrmOrganizationServiceContext(service))
{
var contact = context.CreateQuery<Contact>().First(c => c.FirstName == "Bob");
contact.JobTitle = "Developer";
context.UpdateObject(contact);
context.SaveChanges();
contact.EMailAddress1 = "bob@contoso.com";
context.UpdateObject(contact);
context.SaveChanges();
}

Another scenario that requires careful management of entity tracking is having multiple data contexts handling a common entity object. An example is having one context retrieve an entity and having a second context update the entity. Before the entity can be modified by the second context, it needs to be attached to it. Because an entity can only be attached to a single context, it is necessary to detach the entity from the source context before attaching to the second context.

using (var service = new OrganizationService(connection))
using (var context1 = new OrganizationServiceContext(service))
{
var contact = context1.CreateQuery<Contact>().First(c => c.FirstName == "Bob");

using (var context2 = new OrganizationServiceContext(service))
{
context1.Detach(contact);
context2.Attach(contact);

contact.EMailAddress1 = "bob@contoso.com";
context2.UpdateObject(contact);
context2.SaveChanges();
}
}

Sometimes it is undesirable to detach the entity from the original context because it may already be involved in a complex graph of changes that would be broken when detached. A safer approach is to have the second context simply re-retrieve a distinct instance of the entity and leave the original entity untouched. A variation of this approach is to deep clone the original entity and to attach the cloned entity to the second context. The AttachClone<T> and MergeClone<T> helper methods take the latter approach, where MergeClone<T> performs extra checks to ensure the entity is not already attached to the target context.

using (var service = new OrganizationService(connection))
using (var context1 = new OrganizationServiceContext(service))
{
var contact = context1.CreateQuery<Contact>().First(c => c.FirstName == "Bob");

using (var context2 = new OrganizationServiceContext(service))
{
var contact2 = context2.MergeClone(contact);

contact2.EMailAddress1 = "bob@contoso.com";
context2.UpdateObject(contact2);
context2.SaveChanges();
}
}

© 2016 Microsoft. All rights reserved. Copyright

Show: