8 votes

Nhibernate: Qui est responsable de la gestion des transactions dans une application non web

Eh bien, dans une application web, une unité de travail est responsable de la gestion des transactions.

Mais qu'en est-il d'une application Windows?

Autant que je sache, le référentiel est le connecteur entre mon couche d'accès aux données et ma couche métier. Il cache tout le processus d'accès aux données de ma couche métier.

En partant de ce fait, je pense à prendre tout ce qui concerne les transactions dans le référentiel.

Mais j'ai lu que avoir des méthodes Commit/RollBack sur le référentiel viole l'intention du référentiel.

Je me demande qui est responsable de la gestion des transactions dans une application non web et comment cacher les transactions / Nhibernate de la couche métier?

4voto

default.kramer Points 3119

La réponse générale est "Celui qui instancie l'ISession devrait le disposer. Si la transaction n'a pas été validée, il s'agit en effet d'un rollback."

J'ai eu du succès en utilisant le modèle de commande pour définir une opération que je veux effectuer sur une unité de travail. Disons que nous avons une entité Personne et l'une des choses que nous pouvons faire est de changer le nom d'une personne. Commençons par l'entité :

public class Personne
{
   public virtual int Id { get; private set; }
   public virtual string Nom { get; private set; }

   public virtual void ChangerNom(string nouveauNom)
   {
        if (string.IsNullOrWhiteSpace(nouveauNom))
        {
            throw new DomainException("Le nom ne peut pas être vide");
        }

        if (nouveauNom.Length > 20)
        {
            throw new DomainException("Le nom ne peut pas dépasser 20 caractères");
        }

        this.Nom = nouveauNom;
    }
}

Définissez une simple commande POCO comme ceci :

public class CommandeChangerNom : IDomainCommand
{
    public CommandeChangerNom(int idPersonne, string nouveauNom)
    {
        this.IdPersonne = idPersonne;
        this.NouveauNom = nouveauNom;
    }

    public int IdPersonne { get; set; }
    public string NouveauNom { get; set; }
}

...et un gestionnaire pour la commande :

public class GestionnaireCommandeChangerNom : IHandle
{
    ISession session;

    public GestionnaireCommandeChangerNom(ISession session)
    {
        // Vous pourriez demander un IPersonneRepository au lieu d'utiliser directement la session.
        this.session = session;
    }

    public void Handle(CommandeChangerNom commande)
    {
        var personne = session.Load(commande.IdPersonne);
        personne.ChangerNom(commande.NouveauNom);
    }
}

L'objectif est que le code qui existe en dehors d'une portée Session/Work puisse faire quelque chose comme ceci :

public class CertaineClasse
{
    ICommandInvoker invoker;

    public CertaineClasse(ICommandInvoker invoker)
    {
        this.invoker = invoker;
    }

    public void FaireQuelqueChose()
    {
        var commande = new CommandeChangerNom(1, "asdf");
        invoker.Invoke(commande);
    }
}

L'invocation de la commande implique "effectuer cette commande sur une unité de travail." C'est ce que nous voulons qu'il se passe lorsque nous invoquons la commande :

  1. Commencer une portée imbriquée IoC (la portée "Unit of Work")
  2. Démarrer une ISession et une Transaction (cela est probablement implicite dans le cadre de l'étape 3)
  3. Résoudre un IHandle de la portée IoC
  4. Passer la commande au gestionnaire (le domaine fait son travail)
  5. Valider la transaction
  6. Fin de la portée IoC (l'Unité de Travail)

Voici un exemple utilisant Autofac comme conteneur IoC :

public class InvocateurUnitOfWork : ICommandInvoker
{
    Autofac.ILifetimeScope scope;

    public InvocateurUnitOfWork(Autofac.ILifetimeScope scope)
    {
        this.scope = scope;
    }

    public void Invoke(TCommand commande) where TCommand : IDomainCommand
    {
        using (var porteeTravail = scope.BeginLifetimeScope("UnitOfWork")) // étape 1
        {
            var gestionnaire = porteeTravail.Resolve>(); // étape 3 (implique l'étape 2)
            gestionnaire.Handle(commande); // étape 4

            var session = porteeTravail.Resolve();
            session.Transaction.Commit(); // étape 5

        } // étape 6 - Lorsque la "porteeTravail" est disposée, Autofac disposera l'ISession.
          // Si une exception était levée avant la validation, la transaction est annulée.
    }
}

Remarque : L'InvocateurUnitOfWork que j'ai montré ici viole le SRP - il s'agit d'une UsineUnitOfWork, d'un UnitOfWork, et d'un Invokeur tout en un. Dans mon implémentation réelle, je les ai séparés.

1voto

Dave Sims Points 101

Lorsque j'utilise des référentiels, ils sont contenus dans une unité de travail. L'unité de travail suit les modifications apportées aux référentiels et gère la gestion des transactions.

Pourquoi serait-il valide d'utiliser une unité de travail pour gérer la gestion des transactions dans une application web et pas dans une application Windows? Si c'est une application N-Tier, votre couche métier serait en fait partagée entre les deux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X