문제

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