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:
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
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.