DI с шаблонами UnitOfWork и Repository

В качестве учебного процесса я попытался реализовать UoW с шаблонами репозитория, и у меня есть следующая настройка:

public interface IUnitOfWork : IDisposable
{
  MyEntities Context { get; set; }

  string GetConnectionString()
  void Complete();
}

public abstract class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
  protected MyEntities context;

  protected DbSet<TEntity> dbSet;

  protected RepositoryBase(IUnitOfWork unitOfWork)
  {
     context = unitOfWork.Context;
     dbSet = context.Set<TEntity>();
  }
}

public class ItemRepository : RepositoryBase<Item>, IItemRepository
{
  public ItemRepository(IUnitOfWork unitOfWork)
     : base(unitOfWork)
  {
  }

  public Item GetById(int id)
  {
     // return a item
  }
 }

Чтобы на самом деле использовать ItemRespoitory, я делаю следующее:

using (var unitOfWork = new UnitOfWork())
{
    var ItemRepository = new ItemRepository(unitOfWork);

    var item = ItemRepository.GetById(1);
}

который отлично работает, и теперь я хочу сделать это, используя IoC / DI с простым инжектором:

var container = new Container();

container.Register<IUnitOfWork, UnitOfWork>();
container.Register<IItemRepository, ItemRepository>();

container.Verify();

DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

Но теперь у меня вопрос, поскольку я ввожу ItemRepository в конструктор контроллера.

public MyController(IItemRepository itemRepository)
{
   _itemRepository = itemRepository;
}

как мне избавиться от единицы работы, когда я закончил с репозиторием? потому что мне больше не нужно создавать unitofwork и заключать его в оператор using. Я правильно это делаю? Каким должен быть правильный способ применения DI с UoW и шаблонами репо в моем случае? Спасибо.


person Community    schedule 26.04.2014    source источник
comment
Я думаю, что вы можете обойти это, тщательно контролируя время жизни всего, что задействовано. Я бы сказал, что это должно быть время жизни запроса, но из-за ограниченного опыта время жизни не всегда то, что они кажутся / претендуют на предложение.   -  person brumScouse    schedule 27.04.2014


Ответы (2)


Вам необходимо зарегистрировать свои службы данных как на запрос - в приведенном выше примере это будет либо по веб-запросу или по запросу веб-API

Найдите и установите SimpleInjector.Integration.Web на nuget и измените это:

 container.Register<IUnitOfWork, UnitOfWork>();
 container.Register<IItemRepository, ItemRepository>();

к этому (при условии MVC):

 container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
 container.RegisterPerWebRequest<IItemRepository, ItemRepository>();

и вы сделали.

person qujck    schedule 27.04.2014
comment
Причина, по которой это работает из коробки, заключается в том, что методы расширения RegisterPerWebRequest используют WebRequestLifestyle. WebRequestLifestyle наследуется от базового класса ScopedLifestyle, который позволяет детерминированно удалять кэшированные экземпляры. Другими словами, все стили жизни с заданной областью действия (такие как LifetimeScopeLifestyle, WebApiRequestLifestyle и WebRequestLifestyle) будут гарантировать, что созданные экземпляры для этого образа жизни будут удалены, когда область действия закончится. - person Steven; 28.04.2014

Если вы используете единицу работы (UoW) в сочетании с репозиториями, лучшим способом было бы настроить UoW для хранения репозиториев как свойств, а не вводить Uow в каждый репозиторий.

Пример:

Единица работы (интерфейс):

public interface IUnitOfWork
{
    void Commit();
    IRepository<Store> Stores { get; }
    IRepository<Product> Products { get; }
}

Репозиторий (интерфейс):

public interface IRepository<T> where T : class
{
    IQueryable<T> GetAll();
    T GetById(Guid id);
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
    void Delete(Guid id);
}

Теперь в вашем контроллере у вас будет:

public class ProductsController: Controller
{

    protected IUnitOfWork Uow { get; set; }

    public ProductsController(IUnitOfWork uow)
    {
        if (uow == null)
            throw new ArgumentNullException("Unit of Work");

        Uow = uow;
    }


}

А затем настройте свой IoC следующим образом:

public static void RegisterIoc(HttpConfiguration config)
{
     var container = new Container();

     container.Register<IUnitOfWork, UnitOfWork>();
     container.Register<IRepository<Store>, Repository<Store>>();
     container.Register<IRepository<Product>, Repository<Product>>();

}

Теперь, когда вам понадобится Product от вашего DbContext, вы можете просто получить доступ к единственному Uow, введенному в ваши контроллеры. Также обратите внимание на метод Commit, который в вашей реализации будет означать не что иное, как вызов _dbContext.SaveChanges(). В этом и заключается суть Unit of Work: выполнение некоторых вызовов обновления вашего DbContext и однократное обращение к базе данных. Итак, у вас есть транзакция, такая как настройка (это означает, что если что-то пойдет не так в коде, в базе данных ничего не обновится).

Обратите внимание, что это не полностью рабочий пример, но он должен дать вам представление о том, как все настроить. Если вам нужен учебник о том, как вы могли бы заставить это работать, вы можете проверить одностраничное приложение на PluralSight (на котором основан этот код). Но у него довольно сложная настройка (хотя и очень гибкая), которая может показаться немного сложной для начала.

person Aage    schedule 26.04.2014
comment
Недостаток этого способа состоит в том, что он нарушает принцип открытости \ закрытости, потому что вы необходимо изменять UnitOfWork каждый раз, когда вы добавляете новый объект данных. См. здесь для рабочего примера. введения UnitOfWork в Repository. - person qujck; 28.04.2014
comment
@qujck Я никогда этого не осознавал. Спасибо. - person Aage; 30.04.2014