63 votes

Unité de travail + modèle de référentiel: la chute du concept de transaction commerciale

Combinant Unit of Work et Repository Pattern est quelque chose utilisé assez largement répandue de nos jours. Martin Fowler dit un but de l'utilisation d' UoW est de former une Transaction d'Affaires tout en étant ignorants de la façon dont les référentiels de réellement travail (être persistant ignorants). J'ai passé en revue de nombreuses implémentations, et en ignorant les détails précis (concret/abstrait classe, interface,...), ils sont plus ou moins similaire à ce qui suit:

public class RepositoryBase<T>
{
    private UoW _uow;
    public RepositoryBase(UoW uow) // injecting UoW instance via constructor
    {
       _uow = uow;
    }
    public void Add(T entity)
    {
       // Add logic here
    }
    // +other CRUD methods
}

public class UoW
{
    // Holding one repository per domain entity

    public RepositoryBase<Order> OrderRep { get; set; }
    public RepositoryBase<Customer> CustomerRep { get; set; }
    // +other repositories

    public void Commit()
    {
       // Psedudo code: 
       For all the contained repositories do:
           store repository changes.
    }
}

Maintenant mon problème:

UoW expose méthode publique Commit enregistrer les modifications. Aussi, parce que chaque référentiel a une instance partagée de l' UoWchaque Repository pouvez accéder méthode Commit sur UoW. L'appeler par un référentiel qui rend toutes les autres référentiels de stocker leurs modifications; d'où le résultat de l'ensemble du concept de transaction s'effondre:

class Repository<T> : RepositoryBase<T>
{
    private UoW _uow;
    public void SomeMethod()
    {
        // some processing or data manipulations here
        _uow.Commit(); // makes other repositories also save their changes
    }
}

Je pense que ce ne doit pas être autorisé. Compte tenu de l'objectif de l' UoW (transactions commerciales), la méthode de Commit devraient être exposés à celui qui a commencé une Opération commerciale par exemple de la Couche de gestion. Ce qui m'a surpris, c'est que je ne pouvais pas trouver un article traitant de cette question. Dans tous Commit peut être appelée par n'importe quel pensions être injecté.

PS: je sais que je peux dire à mes développeurs de ne pas appeler Commit en Repository mais faire confiance à l'Architecture est beaucoup plus fiable que de faire confiance aux développeurs!

31voto

Anders Abel Points 36203

Je suis d'accord avec vos préoccupations. Je préfère avoir une ambiante de l'unité de travail, où ultrapériphériques fonction de l'ouverture d'une unité de travail est celle qui décide de valider ou annuler. Les fonctions appelées pouvez ouvrir une unité de la portée du travail qui incorpore automatiquement dans le milieu de l'UoW si il y en a un, ou en crée une nouvelle, si elle n'existe pas.

La mise en œuvre de l' UnitOfWorkScope que j'ai utilisé est fortement inspiré par la façon dont TransactionScope travaux. À l'aide d'un ambient/portée approche supprime également la nécessité pour l'injection de dépendances.

Une méthode qui effectue une requête ressemble à ceci:

public static Entities.Car GetCar(int id)
{
    using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Reading))
    {
        return uow.DbContext.Cars.Single(c => c.CarId == id);
    }
}

Une méthode qui écrit ressemble à ceci:

using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Writing))
{
    Car c = SharedQueries.GetCar(carId);
    c.Color = "White";
    uow.SaveChanges();
}

Notez que l' uow.SaveChanges() appel ne fera qu'une réelle enregistrer dans la base de données si c'est la racine (otermost) champ d'application. Sinon, il est interprété comme un "bon vote" à la racine de la portée serez autorisé à enregistrer les modifications.

L'ensemble de la mise en œuvre de l' UnitOfWorkScope est disponible sur: http://coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/

13voto

Chalky Points 128

Rendez vos référentiels membres de votre UoW. Ne laissez pas vos dépôts "voir" votre UoW. Laissez UoW gérer la transaction.

4voto

Colin Points 5006

Ne passe pas dans l' UnitOfWork, passer une interface qui dispose de méthodes que vous avez besoin. Vous pouvez toujours implémenter cette interface dans le béton d'origine UnitOfWork mise en œuvre si vous voulez:

public interface IDbContext
{
   void Add<T>(T entity);
}

public interface IUnitOfWork
{
   void Commit();
}

public class UnitOfWork : IDbContext, IUnitOfWork
{
   public void Add<T>(T entity);
   public void Commit();
}

public class RepositoryBase<T>
{
    private IDbContext _c;

    public RepositoryBase(IDbContext c) 
    {
       _c = c;
    }

    public void Add(T entity)
    {
       _c.Add(entity)
    }
}

MODIFIER

Après cette annonce, j'ai eu une réflexion. Exposer la méthode Add de l' UnitOfWork moyens de mise en œuvre, il est une combinaison des deux modèles.

J'utilise Entity Framework dans mon propre code et l' DbContext utilisé, il est décrit comme "une combinaison de l'Unité De Travail et d'un Dépôt de modèle".

Je pense qu'il est préférable de fractionner les deux, et cela signifie que j'ai besoin de deux wrappers autour DbContext l'un pour l'Unité De Travail de bits et l'autre pour le Référentiel bits. Et je ne le référentiel d'emballage en RepositoryBase.

La principale différence est que je ne passe pas de l' UnitOfWork sur les Dépôts, je passe l' DbContext. Cela ne signifie pas que l' BaseRepository a accès à un SaveChanges sur le DbContext. Et puisque l'intention est que la coutume dépôts devrait hériter BaseRepository, ils obtiennent l'accès à un DbContext trop. Il est donc possible qu'un développeur peut ajouter du code personnalisé dans un référentiel qui utilise cette DbContext. Donc je suppose que ma "wrapper" est un peu baveur...

Donc, est-il la peine de créer un autre wrapper pour l' DbContext qui peut être transmise par le référentiel de constructeurs de proximité qui éteint? Pas sûr que c'est...

Exemples de passer le DbContext:

La mise en œuvre du Référentiel et de l'Unité de Travail

Le référentiel et l'Unité de Travail dans le Cadre de l'Entité

John Papa du code source d'origine

2voto

lightbricko Points 1953

Dans .NET, composants d'accès aux données généralement automatiquement inscrire à la température ambiante transactions. Par conséquent, l'enregistrement des modifications intra-point de vue transactionnel est séparée de comitting à la transaction, pour conserver les modifications.

En d'autres termes - si vous créez une opération portée, vous pouvez laisser les développeurs enregistrer autant qu'ils le veulent. Pas jusqu'à ce que la transaction est validée, les observables de l'état de la base de données(s) sera mis à jour (enfin, ce qui est observable dépend du niveau d'isolation de transaction).

Cela montre comment créer une transaction champ d'application en c#:

using (TransactionScope scope = new TransactionScope())
{
    // Your logic here. Save inside the transaction as much as you want.

    scope.Complete(); // <-- This will complete the transaction and make the changes permanent.
}

0voto

Tengiz Points 2800

Oui, cette question est une préoccupation pour moi, et voici comment je l'ai manipuler.

Tout d'abord, dans ma compréhension du Modèle de Domaine ne devraient pas connaître de l'Unité de Travail. Modèle de domaine se compose d'interfaces (ou de classes abstraites) qui n'impliquent pas l'existence de la de stockage transactionnel. En fait, il ne connaît pas l'existence de tout un stockage à tous. D'où le terme de Domaine Modèle.

L'unité de Travail est présente dans le Modèle de Domaine de la mise en Œuvre de la couche. Je suppose que c'est mon terme, je veux dire une couche qui implémente le Modèle du Domaine des interfaces en incorporant la Couche d'Accès aux Données. Habituellement, j'utilise un ORM comme DAL et donc il vient avec construit-dans UoW en elle (Entity Framework SaveChanges ou méthode SubmitChanges de commettre les modifications en attente). Cependant, celle-ci appartient à DAL et n'a pas besoin de l'inventeur de la magie.

D'autre part, vous faites référence à l'UoW que vous devez avoir dans le Domaine de l'Implémentation de la couche, parce que vous avez besoin de faire abstraction de la part de la "appliquer les modifications à DAL". Pour cela, je voudrais aller avec Anders Abel est solution de (récursif scropes), parce que les adresses deux choses que vous devez résoudre d'un seul coup:

  • Vous avez besoin de prendre en charge l'enregistrement d'agrégats, d'une transaction, si la somme est l'un des initiateurs de la portée.
  • Vous avez besoin de prendre en charge l'enregistrement des agrégats dans le cadre de la parent de la transaction, si le total n'est pas l'initiateur de la portée, mais en fait partie.

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