1

Допустим, у меня есть 2 стола. ProductCategory и Product. У меня есть 1 общий репозиторий, который может обрабатывать обе таблицы:Несколько общих хранилищ в unitofwork?

public class GenericRepository<T> : IRepository<T> 

Но при использовании единицы работы шаблона, я вынужден создать хранилище для всех таблиц в моей базе данных?

public interface IUnitOfWork : IDisposable 
{ 
    int SaveChanges(); 

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

Не могу ли я добавить общий репозиторий в единицу рабочего класса?

+0

Итак, вы хотите использовать единицу рабочего шаблона, но не хотите добавлять репозитории в единицу работы? Хммм ... не думаю. Но если проблема заключается в количестве изменений, которые необходимо внести для каждого нового объекта в систему, посмотрите [эту статью] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php? ID = 84). – Steven

+0

@Steven, так что вы говорите, что нормально создавать репозиторий для каждого нового объекта? –

+1

Вот почему я против общих хранилищ, они обслуживают базу данных вместо приложения. Если вам нужно сохранить продукт, у вас есть репозиторий продуктов с помощью метода «Сохранить (продукт p)», и пусть репо обработает его оттуда. ProductCategories, UoW, что бизнес репо. Приложение знает только об абстракции репо, а не EF, nhibernate, таблицах и других подробностях о сохранении. – MikeSW

ответ

4

Вы можете добавить общий метод интерфейса IUnitOfWork:

public interface IUnitOfWork : IDisposable 
{ 
    int SaveChanges(); 

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

Но я не рекомендую. Это пахнет анти-шаблоном Service Locator и SRP. Лучше всего удалить все репозитории из интерфейса IUnitOfWork, поскольку предоставление доступа к хранилищу не является обязанностью UnitOfWork. Я рекомендую отделить хранилище от UnitOfWork и самостоятельно ввести их в потребитель.

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 и Repository можно разделить экземпляр контекста. Вот образец кода:

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

Как я могу подключить контекст в unitofwork к репозиторию –

+1

Вы могли бы показать код реализации unitofwork и как зависить от инъекции контекста из репозитория? –

+0

Я добавил образец кода. –

0

Существует множество способов реализации Единицы работы. Я предпочитаю, чтобы в хранилищах был создан блок работы в его конструкторе (который передается через Injection Dependency), тогда вы создаете хранилища только для своих нужд.

+0

Не могли бы вы привести примеры кода? –

+0

@Mystere Man Мне также понравился бы образец кода. Я смотрел на UnitOfWork, и я ненавидел, потому что думаю, что он превращает ваш UnitOfWork в прославленный локатор сервисов. Uow.UserRepo, Uow.EmployeeRepo, Uow.ManagerRepo, Uow.DepartmentRepo просто кажется о самой глупой вещи, которую я мог бы сделать. Я хотел бы видеть то, что вы имели в виду. – uriDium

+0

@uriDium - вот пример, может быть, не самый лучший, но первый я нашел .. http://elegantcode.com/2009/12/15/entity-framework-ef4-generic-repository-and-unit-of- work-prototype/ –

1

Демонстрируя решение только одного класса будет

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); 
    } 
} 

, а затем ваше использование может быть

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(); 
    } 
} 

Я намеренно не включил Repository класс. Для того, чтобы класс инкапсулировал как запросы, так и команды для каждого объекта, является более убитым и ненужной абстракцией. Его почти дефект дизайна на фундаментальном уровне. Запросы и команды принципиально разные. Запросы самым простым способом могут быть созданы как методы расширений на интерфейсе ISession. Команды можно сделать с помощью нескольких классов, как например ..

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); 
    } 
} 

The IStore выше такой же, как Session класса, за исключением того, что он не реализует интерфейс IDisposable и не имеет метода Commit(). ISession тогда, очевидно, наследуется от IStore, а также реализует IDisposable и имеет один метод Commit(). Это гарантирует, что ICommand<IStore> никогда не сможет открывать или удалять соединения и не может совершать транзакции. Его обязанность заключается в определении команды и определении ее применения. Кто его применяет и что происходит, а что не в командном приложении, - это другая ответственность, которая связана с ICommandHandler<IStore>.

+0

Орен Эини подробно рассказал о зле шаблона репозитория. Посетите http://ayende.com/blog/153701/ask-ayende-life-without-repositories-are-they-worth-living для начинающих – afif

Смежные вопросы