Arhn - архитектура программирования

Где я должен создать свой DbContext среди моих сервисов?

Мое приложение построено вокруг «Сервисов» следующим образом:

public class ProfileService : BaseService {

    private CommService _commService;

    public ProfileService(CommService commService) {
        _commService = commService;
    }

    public ApiResponseDto GetProfile(string profileUsername) {
        using (var db = new appContext()){ db.DoStuff(); }
    }
}

Что я хотел бы сделать, так это вставить db экземпляр в BaseService, но я не хочу создавать dbContext и нести расходы, когда они мне не нужны. Итак, я думаю о том, чтобы сделать что-то вроде этого:

public class BaseService {
    public AppContext _db;

    public AppContext db(){
       return _db ?? new AppContext();
    }
}

И тогда все мои методы получат доступ к базе данных через db().DoStuff().

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

У меня вопрос: если я создам экземпляр DbContext и не буду его использовать, будет ли это какая-то плата? Или объект просто создан для будущего использования? Ненавижу спрашивать здесь мнение, потому что знаю, что это запрещено, но является ли это шагом в правильном направлении к сохранению СУХИХ вещей?


  • Не могли бы вы подумать об использовании второй области (использовать DbContext как свойство) с присущим IDisposable и реализовать Dispose? 12.09.2016
  • @kcwu - Насколько я понимаю, явное удаление контекстов не является проблемой. blog.jongallant.com/ 2012/10 / 12.09.2016

Ответы:


1

Шаблон единицы работы

DbContext фактически является реализацией шаблона «unit of work» - после создания DbContext все изменения, сделанные для DbSet, затем сохраняются за один раз, когда вы вызываете SaveChanges.

Итак, следующий вопрос, на который вам нужно ответить, чтобы правильно ответить на ваш вопрос: каков объем изменений, составляющих вашу единицу работы? Другими словами - какой набор изменений нужно внести атомарно - все удастся или все потерпят неудачу?

Практический (если пример этого - скажем, у вас есть конечная точка API, которая предоставляет операцию, позволяющую клиенту отправить заказ. Контроллер использует OrderService для отправки заказа, а затем InventoryService для обновления инвентаря, связанного с элементами в заказе. Если каждая служба имеет свой собственный DbContext, у вас есть риск, что OrderService удастся сохранить отправку заказа, но InventoryService не сможет сохранить обновление инвентаризации.

Внедрение зависимости

Для борьбы с этим распространенным шаблоном является создание контекста для каждого запроса, позволяющая вашему контейнеру IoC создавать и удалять контекст, а также делать его доступным для внедрения в службы по запросу. Это сообщение в блоге дает несколько вариантов управления DbContext и включает пример настройки Ninject для этого.

Это означает, что ваш ctor будет выглядеть так:

public ProfileService(CommService commService, AppContext context) {
    _commService = commService;
    _context = context;
}

И вы можете безопасно использовать этот контекст, не беспокоясь о том, как он был создан или откуда он взялся.

DbScopeFactory Медхи

Однако я предпочитаю подход к более сложным приложениям - отличная библиотека с открытым исходным кодом, описанная здесь: http://mehdi.me/ambient-dbcontext-in-ef6/. Внедрение DbContext для каждого запроса будет отлично работать для более простых приложений, но по мере того, как ваше приложение становится более задействованным (например, несколько контекстов на приложение, несколько баз данных и т. Д.), Более тонкий элемент управления, предлагаемый его IDbContextScopeFactory, неоценим.

Отредактируйте, чтобы добавить - Плюсы и минусы инъекции и строительства

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

Плюсы и минусы обычно одинаковы для всех экземпляров внедрение зависимости, а не только контекст базы данных, но вот несколько конкретных проблем с построением контекста внутри службы (даже в базовой службе):

  • каждая служба будет иметь свой собственный экземпляр dbcontext - это может привести к проблемам согласованности, когда ваша единица работы охватывает задачи, выполняемые несколькими службами (см. пример выше)
  • Будет намного сложнее провести модульное тестирование ваших сервисов, поскольку они создают свои собственные зависимости. Внедрение dbcontext означает, что вы можете имитировать его в своих модульных тестах и протестировать функциональность без попадания в базу данных
  • It introduces unmanaged state into your services - if you are using dependency injection, you want the IoC container to manage the lifecycle of services. When your service has no per-request dependencies, the IoC container will create a single instance of the service for the whole application, which means your dbcontext saved to the private member will be used for all requests/threads - this can be a big problem and should be avoided.
    • (Note: this is less of an issue if you are not using DI and constructing new instances of the services within controllers, but then you are losing the benefits of DI at the controller level as well...)
  • Все службы теперь заблокированы для использования одного и того же экземпляра DbContext - что, если в будущем вы решите разделить свою базу данных и некоторым службам потребуется доступ к другому DbContext? Вы бы создали две разные BaseServices? Или передать данные конфигурации, чтобы разрешить переключение базовой службы? DI позаботится об этом, потому что вы просто зарегистрируете два разных класса Context, а затем контейнер предоставит каждой службе нужный контекст.
  • Вы куда-нибудь возвращаете IQueryables? Если да, то вы рискуете, что IQueryable вызовет попадание в Db даже после того, как DbContext вышел за пределы области видимости - он мог быть удален сборщиком мусора и не будет доступен.

С точки зрения разработчика, я думаю, что нет ничего проще, чем подход DI - просто укажите DbContext в своем конструкторе, а контейнер контейнера DI позаботится обо всем остальном.

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

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

Как видите, меня гораздо меньше беспокоят вычислительные затраты на создание DbContext, когда он не нужен (насколько я могу судить, это довольно низкая стоимость, пока вы действительно не используете его для попадания в базу данных), и более обеспокоен об архитектуре приложения, которая допускает модульное тестирование и отделение от зависимостей.

12.09.2016
  • Спасибо, Крис, я действительно прочитал пост Мехди, прежде чем писать это. Что вы думаете о подходе, который я изложил в своем вопросе? Не могли бы вы подробно рассказать о плюсах и минусах? Моя цель - избежать использования dbContext там, где он мне не нужен, и упростить детали реализации сервисов, чтобы мои разработчики не беспокоились. 12.09.2016
  • И моя проблема с этой библиотекой в ​​том, что у меня есть другая зависимость, которая может исчезнуть или перестать поддерживаться. 12.09.2016
  • Отредактировано, чтобы добавить дополнительные комментарии о плюсах и минусах. 13.09.2016
  • Это круто. Спасибо. 13.09.2016
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..