Working with the OrganizationServiceContext

When developing .Net code that is working with CRM data through the CRM Organization Service, we use the OrganizationServiceProxy class of the CRM SDK. Alternatively, we can use the OrganizationServiceContext class, which is generated by crmsvcutil.exe, along with early bound proxy classes for each entity in our CRM organization. The OrganizationServiceContext class builds on top of the OrganizationServiceProxy, and adds a lot of extra functionality like tracking changes, managing identities and relationships, and gives you access to the Microsoft Dynamics CRM LINQ provider.

See more information and examples here:

Use the OrganizationServiceContext class

In most typical cases I will use the OrganizationServiceContext class in order to use Linq to query data in CRM. Linq is by far my favorite method to query CRM for the following reasons:

  • Queries can be written type safe and with 100% intellisense support. I.e. no strings in your code to specify attribute names. Resharper is also a big help wriiting Linq to CRM.
  • Compact queries, either with SQL like syntax or Lambda expressions.
  • Prettier and more readable code than FetchXML and QueryExpressions.
  • It is possible to UnitTest your queries, by stubbing the data sets in the CrmServiceContext, using Microsoft Fakes
  • A query can return more than one entity type at the same time, by constructing an anonymously typed container as return object.

Linq to CRM also have some limitations, for which you should use FetchXML or QueryExpressions instead. Most of all, it is missing outer joins and the “in” operator. Other limitations you should be avare of are described here:

Use LINQ to construct a Query

Another useful aspect of the OrganizationServiceContext is when modifying data in CRM. This is done by attaching objects (entities) to the service context, and submitting the changes using the SaveChanges method. However, you need to be aware that CRM records are by default automatically added to the context when performing Linq queries, so saving changes might have unforeseen consequences.

Before you start creating and updating object through the service context, I suggest reading this excellent post by Scott Durow:

Do you understand MergeOptions?

Comparison of OrganizationServiceProxy and OrganizationServiceContext

OrganizationServiceProxy

OrganizationServiceContext

Bulk insert/update supported

No. All Update/Create/Excecute calls are handled as individual web service calls.

Yes. All changes in the service context are processed in a single web service call with SaveChanges. This also support relating objects to each other in the context, and have multiple inter-relationships created in the same service call.

When to use

  • When you need simple CRUD operations on a single or a few records.
  • When you are using Linq and don’t understand merge options, or the differences when modifying objects with the two different approaches
  • When you are on CRM 2011 rollup 11 or earlier.
  • When you need to modify or create many objects at the same time, you should use bulk update for improved performance.
  • When you already tracking objects you have retrieved through the service context, you can reduce the code by just saving changes to the retrieved objects

Creation of objects

OrganizationService


var account = new Account
{
    Name = "Test account",
    Address1_City = "Copenhagen"
};
var accountId = service.Create(account);

NB: The id is returned

OrganizationServiceContext


using (var ctx = new CrmServiceContext(service))
{
    var account = new Account
    {
        AccountId = Guid.NewGuid(),
        Name = "Test account",
        Address1_City = "Copenhagen"
    };
    ctx.AddObject(account);

    ctx.SaveChanges();
}

NB: The id is not returned. Id’s should be assigned manually in code before saving if the id should be used later in the code. Alternatively; use ctx.AddLink() to have references between created objects automatically created in SaveChanges, whithout having to know the Ids beforehand:


using (var ctx = new CrmServiceContext(service))
{
    var account = new Account
    {
        Name = "Test account",
        Address1_City = "Copenhagen"
    };
    ctx.AddObject(account);

    var contact = new Contact
    {
        FirstName = "John",
        LastName = "Doe"
    };
    ctx.AddObject(contact);

    ctx.AddLink(contact, new Relationship("contact_customer_accounts"), account);

    ctx.SaveChanges(); // Creates two objects, and sets a reference between them
}

Update of objects

OrganizationService


var accountUpdate = new Account
{
    AccountId = accountId,
    Name = "Test Account"
};

service.Update(accountUpdate);

OrganizationServiceContext


using (var ctx = new CrmServiceContext(service))
{
    var accountUpdate = new Account
    {
        AccountId = accountId,
        Name = "Test Account"
    };
    ctx.Attach(accountUpdate); // The object is now tracked, and will be evaluated by SaveChanges()

    ctx.UpdateObject(accountUpdate); // Equivalent of OrganizationService.Update()

    ctx.SaveChanges(); // This performs the actual call against CRM 
}

As mentioned earlier, objects that are retrieved from CRM through the service context, are automatically tracked, based on the merge options in effect. This can lead to unexpected errors if you are not aware of the Automatic tracking of objects:

using (var ctx = new CrmServiceContext(service))
{
    var trackedAccount = ctx.AccountSet.First(a => a.AccountId.Value == accountId); // Automatically tracked in context

    var accountUpdate = new Account
    {
        AccountId = accountId,
        Name = "Test Account"
    };
    ctx.Attach(accountUpdate); // Error: System.InvalidOperationException: The context is already tracking a different 'account' entity with the same identity.
    ctx.UpdateObject(accountUpdate);           

    ctx.SaveChanges();
}
Advertisements

Automatically generate Crm Service Context through CRM Developer Tools

I love the CRM Developer Tools that come with the CRM 2013 SDK, even though it has its shortcomings. One of the areas where it is lacking, is the missing Crm Service Context when generating wrappers. To fix this, add a file named CrmSvcUtil.exe.config to the folder C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Dynamics CRM 2011 Developer Tools\1.0 The file should contain the following, which will ensure the service context is generated:

<?xml version="1.0"?>
<configuration>
    <appSettings>
        <add key="servicecontextname" value="CrmServiceContext"/>
    </appSettings>
</configuration>