Где я должен создать экземпляр Unit of Work в приложении ASP.Net MVC 3? - PullRequest
12 голосов
/ 02 февраля 2012

Я прочитал столько постов в Stackoverflow, сколько смог найти относительно использования шаблона Unit of Work в приложение 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 должно быть не в контроллере, а в уровне обслуживания вместо. Причина, по которой я не уверен в этом подходе, заключается в том, что моему контроллеру, возможно, придется использовать несколько разных Уровни обслуживания в одной бизнес-транзакции, и если в каждой службе создается экземпляр Unit of Work, это приведет к нескольким Создаются экземпляры единиц работы, что противоречит цели, т. Е. Одна единица работы на бизнес-операцию.

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

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

1 Ответ

18 голосов
/ 02 февраля 2012

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

  1. Разрешить вашему DI-контейнеру внедрить экземпляр UnitOfWork в ваши классы Service в их конструкторах и полностью исключить его из вашего контроллера.

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

  3. Если ваш 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, поскольку он использует свойство static 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);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...