We’re developing a .Net application with the following architecture: presentation layer (using MVC pattern with ASP.Net MVC 2), service layer, data access layer (using repository pattern over Entity Framework).
We’ve decided to put the transaction management in the service layer but we’re not sure about how to implement it. We want to control the transaction entirely at the service layer level. That is, every time a controller calls a method in the service layer, it has to be an atomic operation regarding database updates.
If there were no relation between different services provided in the service layer, then it would be simple: each method should commit the changes at the end of its execution (that is, call the save method on the context it uses). But sometimes services at the service layer work together.
e.g.: we provide a shipment service that has a confirm method which receives the following parameters: the shipment id, a flag indicating if it corresponds to a new customer or an existing one, the customer id (in case the shipment confirmation is for an existing customer) and a customer name (in case it is for a new customer). If the flag is set to "new customer", then the service layer has to (a) create the customer and (b) confirm the shipment. For (a) the shipment service calls the customer service (which already implements the validations and logic needed to create a new customer and store it in the database).
Who should commit the changes in this scenario?
Is there a design pattern for this that we should follow?
I have a Commit() on my service, this only commits if the UnitOfWork is created by the service, if it is passed in the constructor the commit does nothing.
I used a second (internal) constructor for the service:
public class MyService
{
private IUnitOfWork _uow;
private bool _uowInternal;
public MyService()
{
_uow = new UnitOfWork();
_uowInternal = false;
}
internal MyService(IUnitOfWork uow)
{
_uow = uow;
_uowInternal = true;
}
public MyServiceCall()
{
// Create second service, passing in my UnitOfWork:
var svc2 = new MySecondService(_uow);
// Do your stuff with both services.
....
// Commit my UnitOfWork, which will include all changes from svc2:
Commit();
}
public void Commit()
{
if(!_uowInternal)
_uow.Commit();
}
}
In a similar architecture with WCF and L2S instead of EF, we chose to use transactions in the main service interface implementation class. We used TransactionScope to achieve this:
public void AServiceMethod() {
using(TransactionScope ts = new TransactionScope()) {
service1.DoSomething();
service2.DoSomething();
ts.Complete();
}
}
The main disadvantage is that the transaction may get big. In that case, if for example one of the service calls in the transaction block requires only readonly access, we wrap it in a nested TransactionScope(TransactionScopeOption.Suppress)
block to prevent locking rows/tables further in the transaction lifetime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With