Где я должен создать экземпляр Unit of Work в приложении ASP.Net MVC 3?

Я прочитал столько сообщений в Stackoverflow, сколько я могу найти в отношении использования шаблона Единицы работы внутри приложение ASP.Net MVC 3, которое включает бизнес-уровень. Тем не менее, у меня все еще есть пара вопросов с рассматривает эту тему и будет очень признателен за любую обратную связь, которую люди могут мне дать.

Я разрабатываю веб-приложение ASP.Net MVC 3, которое использует EF 4.1. Я буду использовать как репозиторий, так и Единицы работы с этим проектом аналогичны тому, как они используются в этом отличном учебнике

Разница в моем проекте заключается в том, что мне также необходимо включить бизнес-уровень (отдельный проект в моем решении), чтобы выполнять различные бизнес-правила для приложения. Упомянутый выше урок не имеет уровня "Бизнес" и поэтому создает экземпляр класса Unit of Work из контроллера.

public class CourseController : Controller
{
    private UnitOfWork unitOfWork = new UnitOfWork();

Однако, мой вопрос: где я должен создать экземпляр класса Unit of Work, если у меня есть бизнес-уровень?

Я лично думаю, что он должен быть создан в моем контроллере, а затем введен в бизнес-слот следующим образом:

public class PeopleController : Controller
{
    private readonly IUnitOfWork _UoW;
    private IPersonService _personService;

    public PeopleController()
    {
        _UoW = new UnitOfWork();
        _personService = new PersonService(_UoW);
    }

    public PeopleController(IUnitOfWork UoW, IPersonService personService)
    {
        _UoW = UoW;
        _personService = personService;

    }

    public ActionResult Edit(int id)
    {
        Person person = _personService.Edit(id);
        return View(person);
    }

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private BlogEntities _context = new BlogEntities();
    private PersonRepository personRepository = null;

    public IPersonRepository PersonRepository
    {
        get
        {

            if (this.personRepository == null)
            {
                this.personRepository = new PersonRepository(_context);
            }
            return personRepository;
        }
    }

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


public class PersonService : IPersonService
{
    private readonly IUnitOfWork _UoW;

    public PersonService(IUnitOfWork UoW)
    {
        _UoW = UoW;
    }

    public Person Edit(int id)
    {
         Person person = _UoW.PersonRepository.GetPersonByID(id);
         return person;
    }

public class PersonRepository : IPersonRepository
{
    private readonly BlogEntities _context;

    public PersonRepository(BlogEntities context)
    {
        _context = context;
    }

    public Person GetPersonByID(int ID)
    {
        return _context.People.Where(p => p.ID == ID).Single();
    }

Я читал других, говоря, что экземпляр Unit of Work не должен находиться в контроллере, но создан на уровне обслуживания вместо. Причина, по которой я не настолько уверен в этом подходе, заключается в том, что моему Контролеру, возможно, придется использовать несколько разных Service Layers в одной бизнес-транзакции, и если экземпляр Unit of Work был создан внутри каждой службы, это приведет к нескольким Созданы экземпляры Единицы работы, которые побеждают цель, т.е. Одну Единицу работы для каждой транзакции.

Возможно, то, что я объяснил выше, неверно, но если это так, я был бы очень признателен, если бы кто-то мог меня поправить.

Еще раз спасибо за вашу помощь.

Ответ 1

Думаю, у вас есть пара изменений:

  • Позвольте вашему контейнеру DI вставить экземпляр UnitOfWork в свои классы обслуживания в своих конструкторах и полностью исключить его из вашего контроллера.

  • Если ваш контейнер DI поддерживает его (например, Ninject), настройте управление UnitOfWork на основе запроса; таким образом ваши сервисы будут переданы отличным UnitOfWork для каждого запроса, и вы все закончите. Или...

  • Если ваш контейнер DI не поддерживает время жизни каждого запроса, настройте его для управления UnitOfWork как одноэлементный, поэтому каждый класс Service получает один и тот же экземпляр. Затем обновите свой UnitOfWork, чтобы сохранить его объект Entities в хранилище данных, которое хранит объекты по запросу, например, в HttpContext.Current.Items, как описано .

Изменить 1

Относительно того, куда следует ввести UnitOfWork; Я бы сказал, что уровень сервиса - это правильное место. Если вы представляете свою систему в виде ряда слоев, в которых внешние слои взаимодействуют с пользовательскими взаимодействиями, а нижние уровни имеют отношение к хранению данных, каждый уровень должен быть менее озабочен пользователями и больше связан с хранением данных. UnitOfWork представляет собой концепцию из одного из уровней "нижнего уровня", а контроллер - с уровня более высокого уровня; ваш слой Service находится между ними. Поэтому имеет смысл положить UnitOfWork в класс Service, а не Controller.

Изменить 2

Чтобы узнать о создании UnitOfWork и его отношении к HttpContext.Current.Items:

Ваш UnitOfWork больше не будет ссылаться на объект Entities, который будет выполняться через объект HttpContext, введенный в UnitOfWork за интерфейсом, подобным этому:

public interface IPerRequestDataStore : IDisposable
{
    bool Contains(string key);

    void Store<T>(string key, T value);

    T Get<T>(string key);
}

Затем объект HttpContext реализует IPerRequestDataStore следующим образом:

public class StaticHttpContextPerRequestDataStore : IPerRequestDataStore
{
    public bool Contains(string key)
    {
        return HttpContext.Current.Items.Contains(key);
    }

    public void Store<T>(string key, T value)
    {
        HttpContext.Current.Items[key] = value;
    }

    public T Get<T>(string key)
    {
        if (!this.Contains(key))
        {
            return default(T);
        }

        return (T)HttpContext.Current.Items[key];
    }

    public void Dispose()
    {
        var disposables = HttpContext.Current.Items.Values.OfType<IDisposable>();

        foreach (var disposable in disposables)
        {
            disposable.Dispose();
        }
    }
}

В стороне, я назвал его StaticHttpContextPerRequestDataStore, поскольку он использует статическое свойство HttpContext.Current; что не идеально подходит для модульного тестирования (другая тема вообще), но, по крайней мере, имя указывает на характер его зависимости.

Затем ваш UnitOfWork передает IPerRequestDataStore его каждому объекту Repository, чтобы они могли получить доступ к Entities; это означает, что независимо от того, сколько экземпляров UnitOfWork вы создаете, вы будете использовать один и тот же объект Entities во время запроса, потому что он хранится и извлекается в IPerRequestDataStore.

У вас будет абстрактная база Repository, которая будет использовать свой IPerRequestDataStore для ленивой загрузки своего объекта Entities следующим образом:

public abstract class RepositoryBase : IDisposable
{
    private readonly IPerRequestDataStore _dataStore;

    private PersonRepository personRepository;

    protected RepositoryBase(IPerRequestDataStore dataStore)
    {
        this._dataStore = dataStore;
    }

    protected BlogEntities Context
    {
        get
        {
            const string contextKey = "context";

            if (!this._dataStore.Contains(contextKey))
            {
                this._dataStore.Store(contextKey, new BlogEntities());
            }

            return this._dataStore.Get<BlogEntities>(contextKey);
        }
    }

    public void Dispose()
    {
        this._dataStore.Dispose();
    }
}

Ваш PeopleRepository (например) будет выглядеть так:

public class PeopleRepository : RepositoryBase, IPersonRepository
{
    public PeopleRepository(IPerRequestDataStore dataStore) 
        : base(dataStore)
    {
    }

    public Person FindById(int personId)
    {
        return this.Context.Persons.FirstOrDefault(p => p.PersonId == personId);
    }
}

И, наконец, создание вашего PeopleController:

IPerRequestDataStore dataStore = new StaticHttpContextDataStore();
UnitOfWork unitOfWork = new UnitOfWork(dataStore);
PeopleService service = new PeopleService(unitOfWork);
PeopleController controller = new PeopleController(service);

Одно из центральных понятий здесь состоит в том, что объекты имеют свои зависимости, вводимые в них через их конструкторы; это общепринято как хорошая практика и более легко позволяет создавать объекты из других объектов.