How to manage transactions at the service level?

.Net applications were developed with the following architecture: presentation layer (using the MVC template with ASP.Net MVC 2), service level, data access level (using the repository template above the Entity Framework).

We decided to transfer transaction management to the service level, but did not know how to implement it. We want to fully control the transaction at the service level. That is, every time a controller calls a method at the service level, it must be an atomic operation regarding database updates.

If there was no connection between the various services provided at the service level, then it would be simple: each method should commit the changes at the end of its execution (that is, call the save method in the context that it uses). But sometimes services at the service level work together.

for example: we provide a dispatch service that has a confirmation method that receives the following parameters: delivery identifier, a flag indicating whether it matches a new client or an existing one, client identifier (in case of confirmation of dispatch for an existing client), and client name (in case if it is for a new customer). If the flag is set to "new client", then the service level should (a) create a client and (b) confirm the sending. For (a) the forwarding service calls the customer service (which already implements the checks and logic necessary to create a new client and store it in the database).

Who should commit the changes to this scenario?

  • Should customer service do this? he cannot commit the changes after creating a new client, because something may not come too late later in the method of confirming the sending, but he must commit his changes if he is called directly (in another case provided for creating the client).
  • Should the controller calling the service method do this? but the dispatcher should not know anything about transactions, we decided to put all transactional information at the service level.
  • Service Level Transaction Manager? How to design it? Who calls it and when?

Is there a design template for this that we should follow?

+4
source share
2 answers

I have Commit () in my service, it only commits, if UnitOfWork is created by the service, if it is passed in the constructor, then commit does nothing.

I used the 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(); } } 
+5
source

In a similar architecture with WCF and L2S, instead of EF, we decided to use transactions in the implementation class of the main service interface. For this, we used TransactionScope :

 public void AServiceMethod() { using(TransactionScope ts = new TransactionScope()) { service1.DoSomething(); service2.DoSomething(); ts.Complete(); } } 

The main disadvantage is that the transaction can become big. In this case, if, for example, one of the service calls in the transaction block requires read-only access, we terminate it in the nested TransactionScope(TransactionScopeOption.Suppress) block TransactionScope(TransactionScopeOption.Suppress) to prevent rows / tables from being locked for the duration of the transaction.

+1
source

Source: https://habr.com/ru/post/1337993/


All Articles