1

Disons que j'ai 2 tables. ProductCategory et Product. J'ai 1 référentiel générique qui peut gérer les deux tables:Plusieurs référentiels génériques dans unitofwork?

public class GenericRepository<T> : IRepository<T> 

Mais lors de l'utilisation de l'unité modèle de travail, je suis obligé de créer un référentiel pour toutes les tables dans ma base de données?

public interface IUnitOfWork : IDisposable 
{ 
    int SaveChanges(); 

    IRepository<ProductCategory> ProductCategoryRepository { get; } 
    IRepository<Product> ProductRepository { get; } 
} 

Est-ce que je ne peux pas ajouter le référentiel générique à la classe d'unité de travail?

+0

Vous souhaitez donc utiliser l'unité de motif de travail, mais vous ne souhaitez pas ajouter les référentiels à l'unité de travail? Hmmm ... ne le pense pas. Mais si le problème est le nombre de changements que vous devez apporter à chaque nouvelle entité du système, jetez un oeil à [cet article] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php? id = 84). – Steven

+0

@Steven donc vous dites que c'est ok pour créer un référentiel pour chaque nouvelle entité? –

+1

C'est pourquoi je suis contre les dépôts génériques, ils servent la base de données au lieu de l'application. Si vous avez besoin d'enregistrer un produit, vous disposez d'un référentiel de produit avec une méthode 'Enregistrer (produit p)' et laissez le service repo gérer à partir de là. ProductCategories, montre que l'activité du repo. L'application ne connaît que l'abstraction de la pension, pas EF, nhibernate, les tables et autres détails de persistance. – MikeSW

Répondre

4

Vous pouvez ajouter une méthode générique à l'interface IUnitOfWork:

public interface IUnitOfWork : IDisposable 
{ 
    int SaveChanges(); 

    IRepository<T> Repository<T>(); 
} 

Mais je ne le recommande pas. Ça sent l'anti-modèle de Service Locator et la violation de SRP. Le meilleur moyen est de supprimer tous les dépôts de l'interface IUnitOfWork, car fournir l'accès au référentiel n'est pas la responsabilité d'UnitOfWork. Je recommande de séparer le dépôt de UnitOfWork et d'injecter le dans le consommateur par lui-même.

public class Consumer 
{ 
    private readonly IUnitOfWork _unitOfWork; 
    private readonly IRepository<Product> _products; 

    public Consumer(IUnitOfWork unitOfWork, IRepository<Product> products) 
    { 
     _unitOfWork = unitOfWork; 
     _products = products; 
    } 

    public void Action() 
    { 
     var product = _products.GetOne(); 

     product.Name = "new name"; 
     _products.Update(product); 

     _unitOfWork.SaveChanges(); 
    } 
} 

udate:

UnitOfWork et Repository peuvent partager exemple de contexte. Voici l'exemple de code:

public class EfUnitOfWork : IUnitOfWork 
{ 
    private readonly DbContext _context; 

    public EfUnitOfWork(DbContext context) 
    { 
     _context = context; 
    } 

    public void SaveChanges() 
    { 
     _context.SaveChanges(); 
    } 
} 

public class EfRepository<T> : IRepository<T> where T : class 
{ 
    private readonly DbContext _context; 

    public EfRepository(DbContext context) 
    { 
     _context = context; 
    } 

    //... repository methods... 
} 

public class Program 
{ 
    public static void Main() 
    { 
     //poor man's dependency injection 
     var connectionString = "northwind"; 

     var context = new DbContext(connectionString); 
     var unitOfWork = new EfUnitOfWork(context); 
     var repository = new EfRepository<Product>(context); 
     var consumer = new Consumer(unitOfWork, repository); 
     consumer.Action(); 
    } 
} 
+0

Comment connecter le contexte de l'unitofwork au référentiel –

+1

pourriez-vous afficher le code de l'implémentation unitofwork et comment la dépendance injecter le contexte à partir du référentiel s'il vous plaît? –

+0

J'ai ajouté l'exemple de code. –

0

Il existe plusieurs façons de mettre en œuvre l'unité de travail. Je préfère que les dépôts prennent une Unité de Travail dans son constructeur (qui est passé via l'Injection de Dépendance), alors vous créez seulement des dépôts pour vos besoins.

+0

pourriez-vous donner des exemples de code? –

+0

@Mystere Man J'aimerais aussi un échantillon de code. J'ai regardé UnitOfWork et j'ai détesté parce que je pense qu'il transforme votre UnitOfWork en un localisateur de services glorifié. Uow.UserRepo, Uow.EmployeeRepo, Uow.ManagerRepo, Uow.DepartmentRepo semble juste à propos de la chose la plus stupide que je puisse faire. J'aimerais voir ce que vous aviez en tête. – uriDium

+0

@uriDium - voici un exemple, peut-être pas le meilleur, mais le premier que j'ai trouvé .. http://elegantcode.com/2009/12/15/entity-framework-ef4-generic-repository-and-unit-of- work-prototype/ –

1

Démontrant une solution avec une seule classe serait

public class Session : ISession 
{ 
    private readonly DbContext _dbContext; 
    public Session(DbContext dbContext) 
    { 
     _dbContext = dbContext; 
    } 

    public TEntity Single<TEntity>(Expression<Func<TEntity, bool>> expression) where TEntity : class 
    { 
     return _dbContext.Set<TEntity>().SingleOrDefault(expression); 
    } 

    public IQueryable<TEntity> Query<TEntity>() where TEntity : class 
    { 
     return _dbContext.Set<TEntity>().AsQueryable(); 
    } 

    public void Commit() 
    { 
     try { _dbContext.SaveChanges(); } 
     catch (DbEntityValidationException ex) 
     { 
      var m = ex.ToFriendlyMessage(); 
      throw new DbEntityValidationException(m); 
     } 
    } 

    public void Dispose() 
    { 
     _dbContext.Dispose(); 
    } 

    public void Add<TEntity>(IEnumerable<TEntity> items) where TEntity : class 
    { 
     items.ToList().ForEach(Add); 
    } 

    public void Add<TEntity>(TEntity item) where TEntity : class 
    { 
     _dbContext.Set<TEntity>().Add(item); 
    } 

    public void Remove<TEntity>(TEntity item) where TEntity : class 
    { 
     _dbContext.Set<TEntity>().Remove(item); 
    } 

    public void Remove<TEntity>(Expression<Func<TEntity, bool>> expression) where TEntity : class 
    { 
     var items = Query<TEntity>().Where(expression); 
     Remove<TEntity>(items); 
    } 

    public void Remove<TEntity>(IEnumerable<TEntity> items) where TEntity : class 
    { 
     items.ToList().ForEach(Remove); 
    } 
} 

et votre utilisation peut être

public class User 
{ 
    public int? Id { get; set; } 
    public string Name { get; set; } 
    public DateTime Dob { get; set; } 
} 
public class Usage 
{ 
    private readonly ISession _session; 
    public Usage(ISession session) { _session = session; } 

    public void Create(User user) 
    { 
     _session.Add(user); 
     _session.Commit(); 
    } 
    public void Update(User user) 
    { 
     var existing = _session.Single<User>(x => x.Id == user.Id); 

     // this gets cumbursome for an entity with many properties. 
     // I would use some thing like valueinjecter (nuget package) 
     // to inject the existing customer values into the one retreived from the Db. 
     existing.Name = user.Name; 
     existing.Dob = user.Dob; 

     _session.Commit(); 
    } 
} 

J'ai délibérément inclus un dépôt classe. Avoir une classe encapsulant à la fois des requêtes et des commandes pour chaque entité est une over-kill et une abstraction inutile. C'est presque un défaut de conception à un niveau fondamental. Les requêtes et les commandes sont des préoccupations fondamentalement différentes. Les requêtes de la manière la plus simple peuvent être créées en tant que méthodes d'extensions sur l'interface ISession. Les commandes peuvent être effectuées en utilisant quelques classes comme tels ..

public interface ICommand<in TSource> 
{ 
    void ApplyTo(TSource source); 
} 
public interface ICommandHandler<out TSource> 
{ 
    void Handle(ICommand<TSource> command); 
} 
public class LinqCommandHandler : ICommandHandler<IStore> 
{ 
    private readonly ISession _session; 

    public LinqCommandHandler(ISession session) 
    { 
     _session = session; 
    } 
    public void Handle(ICommand<IStore> command) 
    { 
     command.ApplyTo(_session); 
     _session.Commit(); 
    } 
} 
public class UpdateDobForUserName : ICommand<IStore> 
{ 
    public string UserName { get; set; } 
    public DateTime Dob { get; set; } 
    public void OnSend(IStore store) 
    { 
     var existing = store.Query<User>().SingleOrDefault(x => x.Name == UserName); 
     existing.Dob = Dob; 
    } 
} 

public class Usage 
{ 
    private readonly ICommandHandler<IStore> _commandHandler; 

    public Usage(ICommandHandler<IStore> commandHandler) 
    { 
     _commandHandler = commandHandler; 
    } 

    public void Update() 
    { 
     var command = new UpdateDobForUserName {UserName = "mary", Dob = new DateTime(1960, 10, 2)}; 
     _commandHandler.Handle(command); 
    } 
} 

Le IStore ci-dessus est la même que la classe Session, sauf qu'il ne met pas en œuvre l'interface IDisposable et ne dispose pas d'une méthode Commit(). Le ISession hérite alors évidemment d'un IStore et implémente également IDisposable et a une méthode Commit(). Cela garantit un ICommand<IStore> ne peut jamais ouvrir ou disposer des connexions et ne peut pas valider. Sa responsabilité est de définir une commande et de définir comment elle est appliquée. Qui l'applique et ce qui se passe et ce qui ne se passe pas sur l'application de commande est une responsabilité différente qui est avec le ICommandHandler<IStore>.

+0

Oren Eini a longuement parlé des maux du modèle Repository. Consultez http://ayende.com/blog/153701/ask-ayende-life-without-repositories-are-they-worth-living pour les débutants – afif

Questions connexes