web-dev-qa-db-fra.com

Référentiel et modèles d'unité de travail - Comment enregistrer les modifications

J'ai du mal à comprendre la relation entre les modèles de référentiel et d'unité de travail malgré ce genre de question posée tant de fois. Essentiellement, je ne comprends toujours pas quelle partie enregistrerait/validerait les modifications de données - le référentiel ou l'unité de travail?

Étant donné que chaque exemple que j'ai vu concerne leur utilisation conjointement avec un mappeur de base de données/OR, faisons un exemple plus intéressant - permet de conserver les données dans le système de fichiers dans des fichiers de données; selon les modèles, je devrais être capable de le faire car où les données vont n'est pas pertinent.

Donc pour une entité basique:

public class Account
{
    public int Id { get; set; }
    public string Name { get; set; }
}

J'imagine que les interfaces suivantes seraient utilisées:

public interface IAccountRepository
{
     Account Get(int id);
     void Add(Account account);
     void Update(Account account);
     void Remove(Account account);
}

public interface IUnitOfWork
{
    void Save();
}

Et je pense qu'en termes d'utilisation, cela ressemblerait à ceci:

IUnitOfWork unitOfWork = // Create concrete implementation here
IAccountRepository repository = // Create concrete implementation here

// Add a new account
Account account = new Account() { Name = "Test" };
repository.Add(account);

// Commit changes
unitOfWork.Save();

Sachant que toutes les données seront conservées dans les fichiers, où va la logique pour réellement ajouter/mettre à jour/supprimer ces données?

  1. Va-t-il dans le référentiel via les méthodes Add(), Update() et Remove()? Il me semble logique d'avoir tout le code qui lit/écrit les fichiers en un seul endroit, mais alors quel est l'intérêt de l'interface IUnitOfWork?
  2. Cela va-t-il dans l'implémentation IUnitOfWork, qui pour ce scénario serait également responsable du suivi des modifications de données? Pour moi, cela suggère que le référentiel peut lire des fichiers tandis que l'unité de travail doit écrire des fichiers mais que la logique est maintenant divisée en deux endroits.
31
Peter Monks

Le référentiel peut fonctionner sans unité de travail, il peut donc également avoir la méthode Save.

public interface IRepository<T>
{
     T Get(int id);
     void Add(T entity);
     void Update(T entity);
     void Remove(T entity);
     void Save();
}

L'unité de travail est utilisée lorsque vous avez plusieurs référentiels (peut avoir un contexte de données différent). Il garde une trace de toutes les modifications dans une transaction jusqu'à ce que vous appeliez la méthode Commit pour conserver toutes les modifications dans la base de données (fichier dans ce cas).

Ainsi, lorsque vous appelez Ajouter/Mettre à jour/Supprimer dans le référentiel, cela ne change que le statut de l'entité, marquez-la comme ajoutée, supprimée ou sale ... Lorsque vous appelez Commit, Unit Of Work va parcourir les référentiels et effectuer une persistance réelle:

  • Si les référentiels partagent le même contexte de données, l'unité de travail peut travailler directement avec le contexte de données pour de meilleures performances (ouvrir et écrire un fichier dans ce cas).

  • Si les référentiels ont un contexte de données différent (différentes bases de données ou fichiers), l'unité de travail appellera la méthode Save de chaque référentiel dans un même TransactionScope.

25
phnkha

Je suis en fait assez nouveau dans ce domaine, mais comme personne plus sage n'a posté:

Le code qui CRUD se produit dans les référentiels comme vous vous en doutez, mais lorsque Account.Add (par exemple) est appelé, tout ce qui se passe est qu'un objet Account est ajouté à la liste des choses à ajouter plus tard (le changement est suivi) .

Lorsque unitOfWork.Save () est appelé, les référentiels sont autorisés à parcourir leur liste de ce qui a changé ou la liste de l'UoW de ce qui a changé (selon la façon dont vous choisissez d'implémenter le modèle) et d'agir de manière appropriée - donc dans votre cas, il peut y avoir être un List<Account> NewItemsToAdd champ qui a suivi les éléments à ajouter en fonction des appels à .Add (). Lorsque l'UoW indique qu'il est OK d'enregistrer, le référentiel peut réellement conserver les nouveaux éléments en tant que fichiers et, en cas de succès, effacer la liste des nouveaux éléments à ajouter.

AFAIK le point de l'UoW est de gérer la sauvegarde à travers plusieurs référentiels (qui combinés sont l'unité logique de travail que nous voulons engager).

J'aime vraiment ta question. J'ai utilisé Uow/Repository Pattern avec Entity Framework et cela montre combien EF fait réellement (comment le contexte suit les changements jusqu'à ce que SaveChanges soit finalement appelé). Pour implémenter ce modèle de conception dans votre exemple, vous devez écrire un peu de code pour gérer les modifications.

6
Neil Thompson

L'utilisation du système de fichiers peut compliquer les choses si vous voulez le faire vous-même.

N'écrivez que lorsque l'UoW est validée.

Ce que vous devez faire est de laisser les référentiels mettre en file d'attente toutes les opérations IO dans UnitOfWork. Quelque chose comme:

public class UserFileRepository : IUserRepository
{
    public UserFileRepository(IUnitOfWork unitOfWork)
    {
        _enquableUow = unitOfWork as IEnquableUnitOfWork;
        if (_enquableUow == null) throw new NotSupportedException("This repository only works with IEnquableUnitOfWork implementations.");

    }

    public void Add(User user)
    {
        _uow.Append(() => AppendToFile(user));
    }

    public void Uppate(User user)
    {
        _uow.Append(() => ReplaceInFile(user));
    }
}

Ce faisant, vous pouvez obtenir toutes les modifications écrites dans le (s) fichier (s) en même temps.

La raison pour laquelle vous n'avez pas besoin de le faire avec les référentiels DB est que la prise en charge des transactions est intégrée dans la base de données. Par conséquent, vous pouvez dire à la base de données de démarrer une transaction directement, puis de l'utiliser pour simuler une unité de travail.

Prise en charge des transactions

Sera complexe car vous devez pouvoir annuler les modifications dans les fichiers et également empêcher différents threads/transactions d'accéder aux mêmes fichiers lors de transactions simultanées.

4
jgauffin

Ehe, les choses sont délicates. Imaginez ce scénario: un référentiel enregistre quelque chose dans une base de données, l'autre sur le système de fichiers et le troisième quelque chose sur le cloud. Comment commettez-vous cela?

À titre indicatif, l'UoW devrait valider les choses, mais dans le scénario ci-dessus, la validation n'est qu'une illusion car vous avez 3 choses très différentes à mettre à jour. Entrez la cohérence éventuelle, ce qui signifie que tout sera finalement cohérent (pas au même moment que vous êtes utilisé avec un SGBDR).

Cette UoW est appelée une saga dans une architecture basée sur les messages. Le fait est que chaque bit de saga peut être exécuté à un moment différent. La saga se termine uniquement lorsque les 3 référentiels sont mis à jour.

Vous ne voyez pas cette approche aussi souvent, car la plupart du temps vous travaillerez avec un SGBDR, mais de nos jours NoSql est assez courant, donc une approche transactionnelle classique est très limitée.

Donc, si vous êtes sûr de travailler UNIQUEMENT avec UN rdbms, utilisez une transaction avec l'UoW et passez la connexion associée à chaque référentiel. À la fin, UoW appellera commit.

Si vous savez ou vous attendez à devoir travailler avec plus d'un rdbms ou un stockage qui ne prend pas en charge les transactions, essayez de vous familiariser avec une architecture basée sur les messages et avec le concept de la saga.

3
MikeSW

normalement, les référentiels gèrent toutes les lectures et l'unité de travail gère toutes les écritures, mais vous pouvez certainement gérer toutes les lectures et écritures en utilisant uniquement l'une de ces deux (mais si vous utilisez uniquement le modèle de référentiel, il sera très fastidieux de maintenir peut-être 10 référentiels, pire encore, peuvent entraîner des lectures et des écritures incohérentes écrasées), l'avantage du mixage en utilisant les deux est la facilité de suivi du changement d'état et la facilité de gestion de la simultanéité et des problèmes cohérents. pour une meilleure compréhension, vous pouvez faire référence aux liens: modèle de référentiel avec Entity Framework 4.1 et relations parent/enfant et https://softwareengineering.stackexchange.com/questions/263502/unit-of- travail-concurrence-comment-est-il-géré

1
LIU YUE