WCF RIA Services DomainService life-cycle and adding Transactions

Note: This blog post will use the WCF RIA Services PDC Beta and VS2010 Preview, changes to the framework can me made before it hits RTM.

Some questions that pops up about WCF RIA Services is how to enable transactions. So I decided to write a blog post about it, but to not make this post just a few lines I also decided to write about the DomainService life-cycle so you will know what will happen step-by-step in the DomainService after a DomainContext’s SubmitChanges is called on the client-side.

The following images will show you the part of the DomainService life-cycle when a SubmitChanges takes place:

image

The first thing that will happen is the creation of the DomainService, this is done through a Domain Service Factory, you can read about it here. The Domain Service Factory will make a call to the DomainService Initialize method. Within this method you can for example set the ConnectionString on the ObjectContext’s Connection if you are using the LintToEntitesDomainService, or the DataContext if you are using the LintToSqlDomainService.

Here is an example of changing the ObjectContext’s Connection’s ConnectionString:

[EnableClientAccess()]
public class CustomerDomainService : LinqToEntitiesDomainService<NORTHWNDEntities>
{
     public override void Initialize(DomainServiceContext context)
     {
         this.ObjectContext.Connection.ConnectionString = "";            
         base.Initialize(context);
      }

     ...
}

After the Initialization the Submit method of the DomainService will be executed. This method will execute the following DomainService methods in the following order:

1) AuthorizationChangeSet

2) ValidateChangeSet

3) ExecuteChangeSet

4) PersistChangeSet

5) ResolveChangeSet


Note: I will only focus on the virtual method which you can overwrite.

You can override the Submit method and do something before the Submit will take place, and also do things after the Submit is executed, for example do some pre- and post conditions like logging etc.

Note: If you want to log exceptions that will take place during a Submit, it’s recommended that you override the OnError method and log the exception within that method.

public override bool Submit(ChangeSet changeSet)
{
   //do something here pre submit

   var submitResult = base.Submit(changeSet);
            
    //do something here post submit

    return submitResult;
}


The base.Submit will make a call to the OnError if there was any error during the process of the Submit method, so it will not take place after the Submit method. There is a try and catch block around all the above mentioned methods within the base.Submit method so if anyone of them will fail the Submit method will make a call to the OnError an then throw the exception up the chain.

Note: The PersistChangeSet methods of the LinqToEntitiesDomainService and LinqToSqlDomainService can also make a call to the OnError method if a concurrency conflict occurs during the persistent of the entities.

As I mentioned earlier the Submit method will make a call to some methods in a specific order, the first method is the AuthorizeChangeSet, but before taking a look at the AuthorizeChangeSet method I will focus on the ChangeSet object, which every xxxxChangeSet methods will take as an argument.


The ChangeSet object

The ChangeSet object is like a Unit of Work (things that should be done in a specific order). The ChangeSet will contains a list of Domain Operations that should take place and also the entity which is part of that operation, for example if you do the following operation on the client-side:
updateCustomer.Name = "Jane Doe";

var newCusotmerc = new Customer() { CustomerID = 2, Name = "Fred Doe" };

domainContext.Customers.Add(newCustomer);

domainContext.Customers.Remove(removeCustomer);

domainContext.SubmitChanges();

The ChangeSet’s ChangeSetEntries will have three ChangeSetEntry:


Operation = Insert, Entity = newCustomer

Operation = Update, Entity = updateCustomer

Operation = Delete, Entity = removeCustomer


The Operation property of the ChangeSetEntry has the name of the operation that should take place. A operation method is the method which you add to the DomainService to Insert, Update or Delete an entity. The method use a naming convention where the prefix of the method name is the name of the operation, the method can only take an Entity as argument, here is an example of three operation methods added to the DomainService:

public void InsertCustomer(Customer customer)
        
public void UpdateCustomer(Customer customer)
       
public void DeleteCustomer(Customer customer)

The following name can be used for the Insert operation: Insert, Create and Add, or the InsertAttribute on the method if you don’t want to use the naming convention.

The following name can be used for the Update operation: Update. Modify and Edit, or the UpdateAttribute on the method.

The following for the Delete operation: Delete and Remove or the DeleteAttribute.

There is something called a “custom” method. A Custom method can’t return anything and the first argument must be an entity the rest a valid RIA Services type. The name of the method doesn’t matter. Here is an example of a Rebate custom method:


public
void Rebate(Customer customer)


When a “custom” method is called on the client-side, it will not be added to the ChangeSet as a Operation will. Instead it will be added as an EntityAction to the ChangeSet’s Entity.

The Entity property holds the Entity that should be passed to the specific Operation and also the “custom” methods that should be executed.

I will not dig any deeper into the ChangeSet class, it has more properties and method for Entity associations and error etc.

Now to the different xxxxxChangeSet methods.

AuthorizeChangeSet

The AuthorizeChangeSet method is the first method that will be executed when the Submit method is called. This method will validate and see if the current user are allowed to make a call to the DomainService operations. The AuthorizeChangeSet method will iterate through the ChageSet and see if any of the Operations in ChangeSet or the “custom” methods has any of the the AuthorizeAttribute specified (ReuqiredAuthenticationAttribute or RequiredRoleAttrtibute). The AuthorizeAttribute is used to prevent non-authorized user to make a call to an operation method. If the current user isn’t allowed to execute any of the operations in the ChangeSet an exception will be thrown and the OnError method of the DomainService will be called, and the execution of the Submit will ended. Here is an example where the AuthroizateAttribute is used:

[RequiresAuthentication(), RequiresRole("Admin")]
public void AddCustomer(CustomerDto customer)


If the current user isn’t authenticated or the authenticated user don’t belong to the role “Admin”,  the AuthorizeChangeSet will fail and an exception is thrown.


ValidateChangeSet

The next method that will be executed if the authorize validation passed during the Submit is the ValdiateChangeSet. This method will perform the server-side validation when the validation annotation is used on entities. If the validation fails on a entity in the ChangeSet the OnError method will be called and the Submit method will break. The following is an example about how to use the validation annotation on a Entity Framework or Linq to SQL generated class:

[MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]
public partial class Customer
{
    internal sealed class CustomerMetadata
    {
         private CustomerMetadata()
         {
         }

         [Required]
         [RegularExpression("[A-Z][A-Za-z0-9]*")]
         [StringLength(32)]
         public string CompanyName;

         ...
        }
    }
}

If you use a DTO/”Presentation Model” no meta data types are needed, instead you can just add the validation attributes to the property of your DTO/”Presentation Model”:

public class Customer
{
      [Required]
      [RegularExpression("[A-Z][A-Za-z0-9]*")]
      [StringLength(32)]
      public string CompanyName { get; set;}

         ...
}

Note: The validation will take place on the client-side, so the WCF RIA Services validation annotation will do both a client-side validation and server-side. It’s the ValidateChangeSet method that will do the validation on the server-side.

ExecuteChangeSet

The ExecuteChangeSet will take place after the validation is passed. This method will make two internal execution, it will first iterate through the ChangeSet and execute the CUD (Create, Update and Delete) Operations, and then the “custom” methods will be executed if there are any in the ChangeSet. If the ChangeSet has the following ChangeSetEntries:

Operation = Insert, Entity = newCustomer

Operation = Update, Entity = updateCustomer

Operation = Delete, Entity = removeCustomer

and the following operation methods in the DomainService:

public void InsertCustomer(Customer customer)
        
public void UpdateCustomer(Customer customer)
       
public void DeleteCustomer(Customer customer)

The first method to be executed during the ExecuteChangeSet will be the InsertCustomer then the UpdateCustomer and last the DeleteCustomer. When the ExecuteChangeSet method is completed the PersitChangeSet method will be executed.


PersistChangeSet

The PersistChangeSet method will make sure to save the inserted, updated and deleted entities to a data source. If the LintToEntityDomainService is used the PersistChangeSet will make a call to the ObjectContext’s SaveChanges method, if LinqToSqlDomainService is used the PersistChangeSet will call the DataContext’s SubmitChanges method. If a normal DomainService is used (when you use DTO/”Presentation Model”), the PresistChangeSet will do nothing if you don’t implement it. If you for example uses nHibernate you can call the nHibernate’s Session’s Save method in the PersistChangeSet, if the other operation methods just add entities to or remove entities from the nHibernate’s session. If you simply use the Repository pattern, Business Logic Components or Data Access components, you don’t need to use the PersistChangeSet, instead the operations method will work against the components instead (of course it’s up to you and based on the way you want to persist the model).

The last method that will be executed by the Submit method is the ResolveChangeSet, it will only be executed if the ChangeSet’s Entities got any conflicts during the process of the Submit method.

ResolveChangeSet

The ResolveChangeSet will try to resolve any conflicts appeared during the execution of the Submit method. If the conflicts are solved the method will return true otherwise false. If LinqToEntityDomainService or the LinqToSqlEntityDomainService is used, the ResolveChangeSet will make a call to the ObjectContext’s SaveChanges or the DataContext’s SubmitChanges after it succeeded to resolve the entities.

Now when you have got the basic understanding about what the Submit method in the DomainService does, it’s time to see where to begin and end a transaction.


Using Transactions within the DomainService

I have seen some examples where people starts an transaction within the Submit method. A transaction should be short lived and by starting a Transaction within the Submit method like this:

public override bool Submit(ChangeSet changeSet)
{
     using (var trans = new TransactionScope())
     {
        var result = base.Submit(changeSet);
trans.Complete();


return result; } }

can make sure the transaction will be up an running during the time the Submit method will call the AuthorizationChangeSet, ValidateChangeSet, ExecuteCHangeSet and the PersistChangeSet. It can take time for the methods to be executed and there is basically no reason to have a open transaction during the execution of some of the methods. The only methods that may need the Transaction is the ExecuteChangeSet or the PersistChangeSet and maybe the ResolveChangeSet method, but still it's based on how you have implemented your own DomainSerivce. If you use the LinqToEntityDomainService or the LinqToSqlDomainServices the SaveChanges and SubmitChanges uses an implicit transaction or enlist in any ambient transaction and does all the necessary work in that transaction. The LinqToEntityDomainService and the LinqToSqlDomainServices will try to Save- or SubmitChanges after it tries to resolve some of the conflict during the ResolveChangeSet method.

The reason why you may want to being the transaction within the Submit method is to make sure the ResolveChangeSet will be using the same transaction as the PersistChangeSet and also the other methods, it's depends on the Business Layer and Data Access Layer you are using. Another solution to avoid having an transaction open before the PersistChangeSet is called, is by starting a transaction within the PersistChangeSet and Commit or rollback the exception within the Submit method, then the ResolveChangeSet will also reuse the same transaction if you need to do that:

private TransactionScope _trans = null;

protected override bool PersistChangeSet(ChangeSet changeSet)
{
     if (_trans == null )
_trans = new TransactionScope();



return base.PersistChangeSet(changeSet); } public override bool Submit(ChangeSet changeSet) { bool submitResult = false; try { var submitResult = base.Submit(changeSet); if (trans != null) trans.Complete(); } catch { if (trans != null) { trans.Dispose(); trans = null; } } return submitResult; }

EDIT: In most case you don't need to reuse the same Transaction used within the PersistChangeSet and the ResolveChangeSet, it depends on the Business Logic layer and Data Access Layer you are using. If you use a Transaction for the PersistChangeSet it will do a rollback if there was any conflicts or Commit if there wasn't. In the case where the transaction will Commit there will be no conflicts so the ResolveChangeSet will not be called. If there was a conflict, the transaction will do a rollback and the ResolveChangeSet will then be executed. The ResolveChangeSet can then use its own Transaction when it try to resolve the conflicts and submit changes while using the LinqToSqlDomainService or LinqToEntiesDomainService.

If you are using DTO/”Presentation Model” and your operation methods are working directly against your Business layer from the DomainService, you can start the transaction within the ExecuteChangeSet method, but it will also depends on your DAL.

Summary

You have now seen the life-cycle of the DomainService and what methods it will execute during a Submit. You have also seen where you can start a transaction.

If you want to know when I post a new blog post you can follow me on twitter: http://www.twitter.com/fredrikn

8 Comments

  • The other option is to have two seperate transaction scopes for PersistChangeSet and ResolveChangeSet. That allows you to use the standard "using" syntax for the TransactionScopes. If you have many operations you have to do before you call the base.PersistChangeSet or base.ResolveChangeSet this may not be the best solution but if you don't then, as Fredrik said, shorter transactions are best.


  • @Colin Bliar:
    Yes, we could also use two seperate Transactions, one for the PersistChangeSet and one for the ResolveChangeSet. Have updated the blog post

  • Great article. It saved me quite some time to figure out how to unit-test a custom domainservice I'm building.

  • Hi Fredrik

    This is probably one of the best explanation of what is going on inside of DomaintService.

    Supper job!


    Thanks

  • I believe that in case of LinqToSqlDomainService all CRUD operations in DataContext are executed in a transaction by default, so what would be a case of using Transactions on DomainService level?

    Thanks


  • @Alex:
    Thanks!
    If you take a look at the post again you will see this line: "If you use the LinqToEntityDomainService or the LinqToSqlDomainServices the SaveChanges and SubmitChanges uses an implicit transaction or enlist in any ambient transaction and does all the necessary work in that transaction."

  • I tried the above solution, however I was getting the following exception when I deployment my application to my stage server:
    "The underlying provider failed on Open".

    I am not completely sure why this was happening but I found some comments on the web that said it might be that my MS-DTC (Distributed transaction co-ordinator) wasn't working correctly. My database is on a different machine then the Silverlight application.

    So I found the following solution. The transaction lives for the life of the Submit method, but it is the only solution I found without having to figure out MS-DTC.

    public override bool Submit(ChangeSet changeSet) {

    bool submitResult = false;

    // Open the connection if it isn't already opened.
    if( this.ObjectContext.Connection.State != ConnectionState.Open )
    this.ObjectContext.Connection.Open();

    // Create a new transaction to handle the ObjectContext.
    // NOTE: Not using TransactionScope has this causes complexity if the database is on another server and a firewall is blocking
    // or MS-DTC (Distributed transaction co-ordinator) isn't setup correctly.
    using( IDbTransaction trans = this.ObjectContext.Connection.BeginTransaction() ) {

    // Submit the ChangeSet.
    submitResult = base.Submit( changeSet );

    // Ensure that the Submit was successful.
    if( submitResult ) {

    // TODO - do other processing.

    // Complete the transaction since the submit was successful.
    trans.Commit();
    }
    else
    trans.Rollback();
    }

    return submitResult;
    }

  • I would like to validate a changeset after an add to ensure all required fields have been updated. Do I use the ValidateChangeSet? If I understand correctly however, this is a server side event. I would rather catch this on the client side, and force the user through these fields. Thoughts?

Comments have been disabled for this content.