Friday, October 16, 2009

RIA Services Change Set Conflict Resolution using ResolveChangeSet

If there are optimistic concurrency errors when a change set is submitted to a RIA Services DomainService, the error information is reported to the client in the resulting SubmitOperation via the Entity.Conflict member. Server side conflict resolution can be added by adding entity specific resolve methods in the DomainService. For example to handle optimistic concurrency errors for the Product entity you can add a ResolveProduct method in the DomainService.

   1: public bool ResolveProduct(Product currentProduct, Product originalProduct,
   2:     Product storeProduct, bool deleteOperation)
   3: {
   4:     return base.Resolve(currentProduct, originalProduct, storeProduct,
   5:                         ResolveOption.KeepChanges);
   6: }

The code snippet listed above is from the Microsoft .NET RIA Service Overview (July 2009 Preview) document.

Global Conflict Resolution


With this method, a separate resolve method is required for each entity, which is not scalable if there happen to be a large number of entities the domain. In such a case we need another way of handling conflict resolution. Global conflict resolution can be implemented by overriding the ResolveChangeSet method of the DomainService. If you are unaware of ResolveChangeSet method, check the Server Side Change Set Processing section listed after this one.

The ResolveChangeSet method is called for every conflict, regardless of entity type and can be overriden to provide global conflict resolution. Listed below is a code snippet that can be used for last one wins type resolution.



   1: protected override bool ResolveChangeSet(ChangeSet changeSet)
   2: {
   3:     bool resolveChangeSetSuccess = true;
   4:     bool resolveSuccess = false;
   5:  
   6:     foreach (EntityOperation entityOperation in changeSet.EntityOperations)
   7:     {
   8:         resolveSuccess = base.Resolve(entityOperation.Entity, entityOperation.OriginalEntity, 
   9:             entityOperation.StoreEntity, ResolveOption.KeepCurrentValues);
  10:         
  11:         if (resolveSuccess)
  12:             entityOperation.ConflictMembers = null;
  13:  
  14:         resolveChangeSetSuccess = resolveChangeSetSuccess && resolveSuccess;
  15:     }
  16:     if (resolveChangeSetSuccess)
  17:         this.Context.SubmitChanges();
  18:  
  19:     return resolveChangeSetSuccess;
  20: }


Line 8-9: For each entity in the change set, we resolve the conflict by specifying the ResolveOption.KeepCurrentValues (last one wins).
Line 12: One would think that if the method returns true, the error will not be reported to the client. However that is not the case. The ConflictMembers has to be set to null to prevent the conflict error from being reported to the client.

Line 17: If all the entities could be successfully resolved, the change set is saved.

Server Side Change Set Processing


When a change set is submitted to the DomainService, a predefined sequence of methods are called to authorize, validate and persist the change set. This sequence is listed below:

  1. Submit – the service entry point that receives the change set and begins the change set processing pipeline.

  2. AuthorizeChangeSet – Verifies Permission/Authorization for each operation. If any operations are not permitted, processing stops and an error is returned to the client.
  3. ValidateChangeSet – Validation for all operations in the change set is run. If any operations fail validation, processing stops and errors are returned to the client.
  4. ExecuteChangeSet – The corresponding method for each operation in the change set is invoked. This is the point in time when your business logic is run. If any operations failed, processing stops and errors are returned to the client.
  5. PersistChangeSet – At this point all the domain operations have been executed, and this method is called to finalize the changes.
  6. ResolveChangeSet – If there were any optimistic concurrency errors, this method is called to resolve them.

Subscribe to my feed in your favorite feed reader