Pogo69's Blog

May 20, 2011

CRM 2011 LINQ – Entity Instance Tracking

Filed under: Cutting Code — pogo69 @ 10:11

An interesting query came up in the CRM Development Forum on MSDN recently:

http://social.msdn.microsoft.com/Forums/en-US/crmdevelopment/thread/83d49d47-1fc0-4181-99c6-229b658c99f3

which I will repeat here for context:

I currently have a plugin that triggers anytime a user creates, updates, or deletes a record out of a custom entity named Conflict of Interest. The plugin will update a flag on the contact record that lets a user know if there is an active conflict of interest. When I try to run the linq update I get an error “The context is not currently tracking the ‘contact’ entity“.

This sets up the IOrganizationService:

IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

// This function creates the linq query to update the contact record
public void SetActor_ConflictOfInterest(IOrganizationService service, Guid ActorGUID)
    {
      Utilities util = new Utilities();
      Guid BillingHeader = Guid.Empty;
      var xrm = new XrmServiceContext(service);

      var Actor = new Xrm.Contact
      {
        ContactId = ActorGUID,
        new_ConflictofInterest = true,
      };
      xrm.UpdateObject(Actor);
      xrm.SaveChanges();
    }

I’ve tried updating the record with a sql query as well, but CRM is locking the contact record for some reason. Any ideas on the linq error and why CRM is locking the contact record?

I’d never seen the error before, so I set off to see if I could replicate it.  It was not difficult:

var xrm = new Xrm2011.XrmServiceContext("Xrm");

Guid ActorGUID = (from c in xrm.ContactSet where c.FullName == "aa test 2" select c.Id).FirstOrDefault();

xrm.ClearChanges();

Xrm2011.Contact Actor = new Xrm2011.Contact
{
	Id = ActorGUID,
	DoNotEMail = true
};

xrm.UpdateObject(Actor);
xrm.SaveChanges();</pre>
<div>
<pre>

And bingo!!  As soon as I execute the .UpdateObject() method, I receive the error:

The context is not currently tracking the ‘contact’ entity.

According to my  reading through the SDK, we’re being told that the XRM LINQ Context is not currently keeping track of the changes in our ‘contact’ entity instance (identified by the GUID that we have assigned to our new Xrm2011.Contact object).

Ordinarily when interacting with the CRM via the XRM context, we follow a pattern:

  1. Retrieve entity instance(s)
  2. Update entity instance(s)
  3. Save

OrganizationServiceContext.Attach()

But in this case we’re trying to update an entity instance by creating its representation from scratch; so the context knows nothing about it.  The solution is the context.Attach() method:

Guid ActorGUID = (from c in xrm.ContactSet where c.FullName == "aa test 2" select c.Id).FirstOrDefault();

Xrm2011.Contact Actor = new Xrm2011.Contact
{
	Id = ActorGUID,
	DoNotEMail = true
};

xrm.Attach(Actor);
xrm.UpdateObject(Actor);
xrm.SaveChanges();

which attaches our ‘contact’ entity instance to the context, so that it can track the changes.  So we run the code and we’re greeted with this:

The context is already tracking a different ‘contact’ entity with the same identity.

What’s going on?  First, we’re not tracking the ‘contact’ and now we are?  But not?

What happens is this:

  1. Our initial LINQ query retrieves the GUID identifier for the ‘contact’ entity instance we’re interested in.  This tracks the ‘contact’ in context so that the context can keep up to date with any changes.  However, because we are only extracting the GUID, we’re throwing away our reference to the ‘contact’ entity instance that it being tracked.
  2. We construct our ‘contact’ entity instance and set whichever attributes are of relevance.
  3. We attempt to attach our ‘contact’ entity instance to the context.  However, this entity refers to the same entity instance (that is, identified by the same GUID) that was already added to context by our query in (1).  Hence, the error.

So, what do we do now?

There are two solutions in this case:

  1. Keep hold of the original entity instance reference; return the instance and not just the GUID.
  2. Remove the original entity instance reference from context.  We can do this with either the .Detach() method or .ClearChanges() method.

OrganizationServiceContext.Detach()

This method will remove an entity instance from the context and stop tracking changes.  To use this method, you must have a reference to the entity instance.  So this would require a combination of options (1) and (2) above:
  1. Hold onto the original entity instance reference
  2. .Detach() it
  3. Create your new entity instance reference and .Attach()
  4. Update
  5. Save

OrganizationServiceContext.ClearChanges()

This method will remove ALL entity instances from context and stop tracking changes for ALL entity instances.  This will allow us to .Attach and .Update without holding onto the original entity instance:

Guid ActorGUID = (from c in xrm.ContactSet where c.FullName == "aa test 2" select c.Id).FirstOrDefault();

xrm.ClearChanges();

Xrm2011.Contact Actor = new Xrm2011.Contact
{
	Id = ActorGUID,
	DoNotEMail = true
};

xrm.Attach(Actor);
xrm.UpdateObject(Actor);
xrm.SaveChanges();

Conclusion

Which combination of tracking methods you use in your application will depend entirely upon what you need to accomplish, but armed with this knowledge at least you’ll know which tools you have at your disposal.

5 Comments »

  1. Good post…It helped me alot :)

    Comment by Rajeev Pentyala — January 3, 2012 @ 17:17

  2. I had this exact issue… but your solution didn’t fix it.

    Exception:
    An error occured while processing this request.
    at Microsoft.Xrm.Sdk.Client.OrganizationServiceContext.SaveChanges(SaveChangesOptions options)
    at CrmPlugins.Opportunity.UpdateContactFromDetails(IOrganizationService service, Guid id, String strLogFile)
    An unexpected error occurred.

    Still getting this. Any other ideas?

    Comment by Mark McGookin — March 7, 2012 @ 01:04

    • Hi Mark,

      Apologies for the late reply.

      If you are still experiencing the issue:

      1. Enable tracing – it will give you more in-depth error information
      2. Interrogate resultant trace log files
      3. Debug as required

      There is insufficient information in the snippet you provided to elicit the root cause of the error.

      Comment by pogo69 — March 12, 2012 @ 12:12

  3. Try stick to this approach:
    1) select just the attributes you want to change from context, along with id (trick here, see below)
    2) update that attributes
    3) updateobject()
    4) savechanges()

    at step 1), create the object you’re changing with just the attributes you have selected populated.
    ie.
    var obj = (from o in ctx.object where (…) select new object() { attr1=o.attr1, attr2=o.attr2, id=o.id}).FirstOrDefault();
    obj.attr1 = attr1valueyouwant
    obj.attr2 = attr2valueyouwant
    ctx.updateobject(obj)
    ctx.SaveChanges();

    This way, you don’t have to detach the original read object and then create another object, attach it, then update it.

    Comment by Kenneth Leong — April 11, 2012 @ 01:25

    • Thanks Kenneth.

      I have been using much the same mechanisms in my recent work with the early-bound objects and *ServiceContext.

      This technique allows you to update attributes not originally retrieved; however, once the entity instance is being tracked, subsequent requests for attributes not originally retrieved return null, as they are being cached. Usage of the early-bound mechanisms is much more complex than it appears on the surface; and really does require planning ahead.

      I really should re-write and/or update this posting based on some other information gleaned since originally writing it, but it’s been tough enough lately for me to motivate myself into writing new posts. To be continued; but for now, hopefully people will read the comments as well as the post!

      Comment by pogo69 — April 11, 2012 @ 07:55


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: WordPress Classic. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.