سؤال

How to create transaction using crm 2011 sdk and XrmServiceContext?

In next example 'new_brand' is some custom entity. I want to create three brands. Third has wrong OwnerID guid. When I call SaveChanges() method, two brands are created and I've got exception. How to rollback creating of first two brands?

Is it possible without using pluggins and workflows?

using (var context = new XrmServiceContext(connection))
{
    SystemUser owner = context.SystemUserSet.FirstOrDefault(s => s.Id == new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));

    // create 3 brands
    new_brand b1 = new new_brand();
    b1.new_brandidentification = 200;
    b1.new_name = "BRAND 200";
    b1.OwnerId = owner.ToEntityReference();
    context.AddObject(b1);

    new_brand b2 = new new_brand();
    b2.new_brandidentification = 300;
    b2.new_name = "BRAND 300";
    b2.OwnerId = owner.ToEntityReference();
    context.AddObject(b2);

    new_brand b3 = new new_brand();
    b3.new_brandidentification = 400;
    b3.new_name = "BRAND 400";
    b3.OwnerId = new EntityReference(SystemUser.EntityLogicalName, new Guid("00000000-0000-0000-0000-000000000000"));
    context.AddObject(b3);

    context.SaveChanges();
}
هل كانت مفيدة؟

المحلول

Is it possible without using plugins and workflows?

No I don't believe that it is. Each context.AddObject() is atomic. If you don't want to use plug-ins then all I think you can do is have some sort of clean-up logic that deletes the created records if your conditions are not met.

نصائح أخرى

Actually this is possible without the use of Plugins.

You can use CRM relationship links to force transactional behaviour:

EntityA primaryEntity = new EntityA() { //initialise me... };
EntityB secondaryEntity = new EntityB() { //initialise me... };

context.AddObject(primaryEntity);
context.AddObject(secondaryEntity);

// This is the key part: explicitly link the two entities
context.AddLink(primaryEntity, 
    new Relationship("relationship_name_here"), secondaryEntity);

// commit changes to CRM
context.SaveChanges();

There are a few drawbacks to this approach:

  • Obviously, a relationship must exist in CRM between the two entities. If no relationship exists you will have to go with a plugin.
  • I personally find the code can become messy if you need to save a large hierarchy of objects all in one go.

An alternative approach might be to consider implementing a command pattern using plugins.

The idea is that you create the CRM objects on the client, serialize them and pass them to CRM via a custom entity. A pre-create plugin is then set-up on this entity to de serialize and create the objects within the plugin transaction scope.

An excellent blog post describing both strategies can be found here: http://crm.davidyack.com/journal/2012/6/26/crm-client-extension-data-access-strategies.html

The only support CRM provides for transactions is within a plugin, and I don't even believe that will help you in this case, since each creation of a brand, will take place in its own transaction.

The easiest way I can think of to implement this sort of logic is adding a new field to new_Brand called new_TransactionGroupId, and use it to delete any records that were created like so:

using (var context = new XrmServiceContext(connection))
{
    SystemUser owner = context.SystemUserSet.FirstOrDefault(s => s.Id == new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));
    var transactionGroupId = Guid.NewGuid();

    // create 3 brands
    new_brand b1 = new new_brand();
    b1.new_brandidentification = 200;
    b1.new_name = "BRAND 200";
    b1.OwnerId = owner.ToEntityReference();
    b1.new_TransactionGroupId = transactionGroupId ;
    context.AddObject(b1);

    new_brand b2 = new new_brand();
    b2.new_brandidentification = 300;
    b2.new_name = "BRAND 300";
    b2.OwnerId = owner.ToEntityReference();
    b2.new_TransactionGroupId = transactionGroupId ;
    context.AddObject(b2);

    new_brand b3 = new new_brand();
    b3.new_brandidentification = 400;
    b3.new_name = "BRAND 400";
    b3.OwnerId = new EntityReference(SystemUser.EntityLogicalName, new Guid("00000000-0000-0000-0000-000000000000"));
    b3.new_TransactionGroupId = transactionGroupId;
    context.AddObject(b3);

    try{
        context.SaveChanges();
    } catch (Exception ex){
        // Since one brand failed, cleanup all brands
        foreach(brand in context.new_brand.where(b => b.new_TransactionGroupId == transactionGroupId)){
            context.Delete(brand);
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top