4

Nous développons une application .Net avec l'architecture suivante: couche de présentation (en utilisant le modèle MVC avec ASP.Net MVC 2), couche de service, couche d'accès aux données (en utilisant le modèle de référentiel sur Entity Framework).Comment gérer les transactions dans la couche de service?

Nous avons décidé de mettre la gestion des transactions dans la couche de service, mais nous ne savons pas comment l'implémenter. Nous voulons contrôler la transaction entièrement au niveau de la couche de service. C'est-à-dire qu'à chaque fois qu'un contrôleur appelle une méthode dans la couche de service, il doit s'agir d'une opération atomique concernant les mises à jour de la base de données. S'il n'y avait pas de relation entre différents services fournis dans la couche service, alors ce serait simple: chaque méthode devrait valider les changements à la fin de son exécution (c'est-à-dire, appeler la méthode save sur le contexte qu'elle utilise) . Mais parfois, les services au niveau de la couche de service fonctionnent ensemble. Par exemple: nous fournissons un service d'expédition qui dispose d'une méthode de confirmation qui reçoit les paramètres suivants: l'identifiant d'expédition, un drapeau indiquant s'il correspond à un nouveau client ou un existant, l'identifiant du client (en cas de confirmation d'expédition est pour un client existant) et un nom de client (dans le cas où c'est pour un nouveau client). Si l'indicateur est défini sur "nouveau client", la couche de service doit (a) créer le client et (b) confirmer l'expédition. Pour (a) le service d'expédition appelle le service client (qui implémente déjà les validations et la logique nécessaires pour créer un nouveau client et le stocker dans la base de données).

Qui devrait valider les modifications dans ce scénario?

  • Le service client doit-il le faire? il ne peut pas valider les modifications après la création du nouveau client, car quelque chose peut mal se passer plus tard dans la méthode de confirmation d'expédition, mais il doit valider ses modifications dans le cas contraire (dans un autre cas, créer un client).
  • Le contrôleur appelant la méthode de service doit-il le faire? mais le contrôleur ne devrait rien savoir sur les transactions, nous avons décidé de mettre tout le knowladge de transaction dans la couche de service.
  • Un gestionnaire de transactions dans la couche de services? Comment le concevoir? Qui l'appelle et quand?

Y a-t-il un modèle de conception que nous devrions suivre?

Répondre

5

J'ai un Commit() sur mon service, ceci ne valide que si UnitOfWork est créé par le service, s'il est passé dans le constructeur, la validation ne fait rien.

J'ai utilisé un second constructeur (interne) pour le 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(); 
} 
} 
+0

Merci John! Exactement le genre de réponse que je cherchais. – mmutilva

1

Dans une architecture similaire avec WCF et L2S au lieu de EF, nous avons choisi d'utiliser des transactions dans la classe d'implémentation de l'interface de service principale. Nous avons utilisé TransactionScope pour y parvenir:

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

Le principal inconvénient est que la transaction peut devenir gros. Dans ce cas, si par exemple l'un des appels de service dans le bloc de transaction nécessite uniquement un accès en lecture seule, nous l'enveloppons dans un bloc TransactionScope(TransactionScopeOption.Suppress) imbriqué pour empêcher le verrouillage des lignes/tables dans la durée de vie de la transaction.

Questions connexes