Как лучше: сделать собственную оболочку для DbContext или использовать DbContext в контроллере

В моем проекте я использую entity framework 7 и asp.net mvc 6 \ asp.net 5. Я хочу создать CRUD для собственных моделей

Как я могу сделать лучше:

  1. Используйте dbcontext из контроллера. В следующей ссылке автор объясняет, что этот способ лучше, но подходит ли он для контроллеров?
  2. Сделать собственную обертку. Некоторые лучшие практики пишут о том, что лучше всего сделать собственный репозиторий.

Я не собираюсь менять ef на что-то другое, так что не против, даже если есть сильная связность для доступа к данным из конкретной реализации и я знаю, что в ef7 dbcontext сразу реализованы паттерны Unit Of Work и Repository.


person netwer    schedule 06.01.2016    source источник


Ответы (4)


Ответ на ваш вопрос в первую очередь основан на мнении. Никто не может однозначно сказать «один способ лучше другого», пока не будет дан ответ на множество других вопросов. Каков размер/масштаб/бюджет вашего проекта? Сколько разработчиков будет над этим работать? Будут ли у него только контроллеры MVC (на основе представлений) или также будут контроллеры API (на основе данных)? Если второе, то насколько перекрываются методы действий MVC и API, если таковые имеются? Будут ли у него какие-либо невеб-клиенты, такие как WPF? Как вы планируете тестировать приложение?

Entity Framework — это инструмент уровня доступа к данным (DAL). Контроллеры — это инструменты обработки запросов и ответов HTTP-клиента. Если ваше приложение не является чистым CRUD (что сомнительно), вероятно, вам понадобится какая-то обработка бизнес-логики, которую вам нужно будет выполнять между получением веб-запроса по HTTP и сохранением данных этого запроса в базе данных с помощью EF ( поле X является обязательным, если вы предоставляете данные для поля Y, вы также должны предоставить данные для поля Z и т. д.). Таким образом, если вы используете код EF непосредственно в своих контроллерах, это означает, что логика бизнес-процессов почти наверняка будет присутствовать в контроллерах вместе с ней.

Те из нас, кто имеет приличный опыт разработки нетривиальных приложений с помощью .NET, склонны к мнению, что ни бизнес-логика, ни логика доступа к данным не должны присутствовать в контроллерах из-за определенных трудностей, возникающих при реализации такого дизайна. Например, когда вы помещаете логику веб-/http-запроса и ответа, а также бизнес-логику и логику доступа к данным в контроллер, вам в конечном итоге приходится тестировать все эти аспекты приложения из самих действий контроллера (что является вопиющим нарушением Единого Принцип ответственности, если вы заботитесь о SOLID дизайне). Также предположим, что вы разрабатываете традиционное приложение MVC с контроллерами, которые возвращают представления, а затем решаете, что хотите расширить приложение для других клиентов, таких как iOS/android/WPF/или какой-либо другой клиент, который не понимает ваши представления MVC. Если вы решите внедрить дополнительный набор действий контроллера на основе данных WebAPI, вы будете дублировать бизнес-логику и логику доступа к данным как минимум в двух местах.

Тем не менее, это не означает, что вся бизнес-логика и логика доступа к данным в контроллерах должны быть хуже, чем в альтернативном дизайне. Любое решение, которое вы принимаете при разработке архитектуры веб-приложения, будет иметь преимущества и недостатки. Всегда будут компромиссы, независимо от того, какой маршрут вы выберете. Преимущества хранения всего кода вашего приложения в контроллерах могут включать более низкую стоимость, сложность и, следовательно, время выхода на рынок. Нет смысла перепроектировать сложную архитектуру для очень простых приложений. Как бы то ни было, к сожалению, лично я никогда не имел удовольствия разрабатывать простое приложение, поэтому я придерживаюсь «общего мнения» о том, что сохранение кода для бизнеса и доступа к данным в контроллерах «вероятно» не является хорошим долгосрочным проектным решением.

Если вас действительно интересуют альтернативы, я бы порекомендовал прочитать это две статьи. Они являются хорошим примером того, как можно реализовать шаблон команды и запроса (CQRS), который могут использовать контроллеры. EF реализует как репозиторий, так и шаблоны единиц работы из коробки, но это не обязательно означает, что вам нужно «обернуть» его, чтобы переместить код доступа к данным за пределы ваших контроллеров. Желаем удачи в принятии таких решений для вашего проекта.

public async Task<ActionResult> Index() {
    var user = await query.Execute(new UserById(1));
    return View(user);
}
person danludwig    schedule 07.01.2016
comment
Спасибо за ваш ответ и ваше время. Проект простой и небольшой. Я разделяю логику на уровне бизнес-логики. Изначально это будет только обертка над CRUD EF, возможно позже я добавлю специфические функции. - person netwer; 09.01.2016

Обычно я предпочитаю использовать шаблон репозитория вместе с шаблоном UnitOfWork (http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/реализацияшаблоноврепозиторияиединицыработывприложенииasp-net-mvc) - Я создаю экземпляр DbContext в объекте экземпляра UnitOfWork и добавляю этот DbContext в репозитории. После этого я создаю экземпляр UnitOfWork в контроллере, а контроллер ничего не знает о DbContext:

public ActionResult Index()
{
    var user = unitOfWork.UsersRepository.GetById(1); // unitOfWork is dependency injected using Unity or Ninject or some other framework
    return View(user);
}
person George Findulov    schedule 06.01.2016
comment
Спасибо за ваш ответ. Но я думаю, что вы делаете много логики контроллером или я ошибаюсь? - person netwer; 06.01.2016
comment
На самом деле все с точностью до наоборот — я максимально минимизирую логику в контроллере, не позволяя ему знать и обрабатывать инициализацию DbContext и напрямую запрашивать базу данных. Наличие одного экземпляра unitOfWork позволяет вам обрабатывать все операции с базой данных из одного места, объединяя все ваши репозитории в одном месте. Вы несете ответственность за создание репозиториев для каждого типа сущностей (или групп сущностей) и вызов их методов в контроллере через переменную unitOfWork. Методы в ваших репозиториях выполняют фактические операции с базой данных. - person George Findulov; 06.01.2016
comment
Просто хочу отметить, что DbContext реализует шаблон UnitOfWork по умолчанию —> наш UnitOfWork используется для инкапсуляции экземпляров DbContext и репозитория. - person George Findulov; 06.01.2016
comment
теперь, я действительно понял вас. Спасибо за подробный ответ - person netwer; 07.01.2016
comment
хм... зачем вам единица работы, чтобы GetById? Сохраняете ли вы что-нибудь в базе данных, чтобы выполнить запрос? - person danludwig; 07.01.2016

Это зависит от жизненного цикла вашего приложения.

Если он будет использоваться, расширяться и изменяться в течение много лет, то я бы сказал, что создание оболочки — хороший выбор.

Если это небольшое приложение и, как вы сказали, вы не собираетесь менять EntityFramework на другой ORM, тогда избавьте себя от работа по созданию оболочки и ее использование непосредственно в контроллере.

person Domysee    schedule 06.01.2016
comment
Спасибо за ваш ответ. Я с тобой согласен. Но я хочу понять, почему в ef7 в DbContext добавлены шаблоны UoW/Repository? Чтобы меньше работать разработчику? - person netwer; 06.01.2016

На это нет однозначного ответа. Все зависит от того, что вы пытаетесь сделать. Если вы собираетесь поддерживать код, я бы предложил использовать оболочку.

person sander    schedule 06.01.2016