Итак, у вас есть база данных и класс, представляющий эту базу данных: ваш DbContext
, он должен представлять таблицы и отношения между таблицами, которые есть в вашей базе данных, ничего более.
Вы решилиотделить операции в вашей базе данных от самой базы данных.Это хорошо, потому что, если несколько пользователей вашей базы данных хотят сделать то же самое, они могут повторно использовать код, чтобы сделать это.
Например, если вы хотите создать «Заказ наКлиент с несколькими линиями заказа, содержащими заказанные товары, согласованные цены, сумму и т. Д. », Вам нужно сделать несколько вещей с вашей базой данных: проверить, существует ли клиент, проверить, все ли продукты уже существуют, проверить, достаточно ли товаров,и т. д.
Обычно это вещи, которые вы не должны реализовывать в своем DbContext, а в отдельном классе.
Если вы добавите такую функцию, как: CreateOrder
, то несколько пользователей могут-Используй эту функцию.Вам нужно будет проверить это только один раз, и если вы решите что-то изменить в своей модели заказа, есть только одно место, где вам придется изменить создание заказа.
Другие преимуществаотделение класса, представляющего вашу базу данных (DbContext), от класса, который обрабатывает эти данные, заключается в том, что будет проще изменить внутреннюю структуру без необходимости изменения пользователей вашей базы данных.Вы даже можете решить перейти с Dapper на Entity Framework без необходимости изменения использования.Это также облегчает проверку базы данных в целях тестирования.
Такие функции, как CreateOrder, QueryOrder, UpdateOrder, уже указывают, что они не являются общими действиями базы данных, они предназначены для базы данных Ordering, а не для базы данных School.
Это может привести к тому, что единица работы может не являться правильным именем для функциональности, которую вы хотите в отдельном классе.Несколько лет назад единица работы предназначалась главным образом для представления действий с базой данных, а не с конкретной базой данных, в этом я не совсем уверена, потому что довольно скоро увидела, что реальный класс единицы работы будетне улучшают функциональность моего DbContext.
Довольно часто вы видите следующее:
- Класс DbContext, представляющий вашу базу данных: базу данных, которую вы создали, а нелюбая общая идея баз данных
- Класс репозитория, который представляет идею хранения где-то ваших данных, как они хранятся, это может быть DbContext, но также CSV-файл или коллекция классов Dictionary, созданная дляТестовые цели.Этот репозиторий имеет IQueryable и функционирует для добавления / обновления / удаления (по мере необходимости
- Класс, представляющий вашу проблему: модель заказа: Добавить заказ / обновить заказ / получить заказ клиента: этот классдействительно знает все о Order, например, он имеет OrderTotal, который, вероятно, нигде не найден в вашей базе данных Ordering.
За пределами DbContext вам иногда может понадобиться SQL, например, для повышения эффективностиВне репозитория не должно быть видно, что вы используете SQL
Рассмотрите возможность разделения проблем: как сохранить ваши данные (DbContext), как CRUD (создать, извлечь, обновить и т. д.) данные(Репозиторий), как использовать данные (объедините таблицы)
Я думаю, что вы хотите сделать в своей рабочей единице, должно быть сделано внутри репозитория. Ваш класс Ordering должен создать Репозиторий (которыйсоздает DbContext), запросите несколько элементов, чтобы проверить данные, которые он должен добавить / обновить, выполнить добавление и обновление ипройдите изменения.После этого ваш класс упорядочения должен удалить Repository, что, в свою очередь, приведет к удалению DbContext.
Класс Repository будет очень похож на класс DbContext.Он имеет несколько наборов, которые представляют таблицы.Каждый набор будет реализовывать IQueryable<...>
и позволять добавлять / обновлять / удалять все, что нужно.
Из-за этого сходства функций вы можете опустить класс Repository и позволить вашему классу Ordering напрямую использовать DbContext.Тем не менее, имейте в виду, что изменения будут больше, если в будущем вы решите, что больше не хотите использовать инфраструктуру сущностей, а хотите использовать более новую концепцию, или, возможно, вернетесь к Dapper, или даже к более низкому уровню.SQL просочится в ваш класс Ordering
Что выбрать
Я думаю, вы должны ответить на несколько вопросов для себя:
- Действительно ли существует только одна база данных, которая должнабыть представленным вашим DbContext, может быть, тот же DbContext должен использоваться во 2-й базе данных с той же разметкой.Подумайте о тестовой базе данных или базе данных разработки.Разве не было бы проще / лучше тестируемым / лучше изменяемым, позволить вашей программе создать DbContext, который должен использоваться?
- Действительно ли существует одна группа пользователей вашего DbContext: должен ли каждый иметь возможность удалить?создавать?Может быть, некоторые программы хотят запрашивать только данные (программа, которая отправляет заказы по электронной почте), и программы заказов должны добавлять клиентов.А может быть, другая программа должна добавить и обновить продукты, а также количество продуктов на складе.Рассмотрите возможность создания различных классов репозитория для них.Каждый репозиторий получает один и тот же DbContext, поскольку все они обращаются к одной и той же базе данных
- Аналогично: только один класс обработки данных (вышеупомянутый класс заказов): если процесс, который обрабатывает заказы, сможет изменять цены на продукты идобавить элементы на склад?
Причина, по которой вам нужны фабрики, заключается в том, что вы не хотите, чтобы ваша «основная» программа решала, какие элементы она должна создавать для правильной работы.сейчас.Код был бы намного проще, если бы вы сами создали элементы:
Последовательность создания для процесса заказа:
IJwtHelper jwtHelper = ...;
// The product database: all functionality to do everything with Products and Orders
ProductDbContext dbContext = new ProductDbContext(...)
{
JwtHelper = jwtHelper,
...
};
// The Ordering repository: everything to place Orders,
// It can't change ProductPrices, nor can it stock the wharehouse
// So no AddProduct, not AddProductCount,
// of course it has TakeNrOfProducts, to decrease Stock of ordered Products
OrderingRepository repository = new OrderingRepository(...) {DbContext = dbContext};
// The ordering process: Create Order, find Order, ...
// when an order is created, it checks if items are in stock
// the prices of the items, if the Customer exists, etc.
using (OrderingProcess process = new OrderingProcess(...) {Repository = repository})
{
... // check if Customer exists, check if all items in stock, create the Order
process.SaveChanges();
}
Когда процесс утилизирован, репозиторий утилизируется, что, в свою очередь, утилизируетDbContext.
Что-то похожее для процесса отправки заказов по электронной почте: ему не нужно ни проверять продукты, ни создавать клиентов, он должен только получать данные и, возможно, обновлять, что заказ был отправлен по электронной почте.отправлено по почте или ошибка электронной почты.
IJwtHelper jwtHelper = ...;
// The product database: all functionality to do everything with Products and Orders
ProductDbContext dbContext = new ProductDbContext(...) {JwtHelper = jwtHelper};
// The E-mail order repository: everything to fetch order data
// It can't change ProductPrices, nor can it stock the wharehouse
// It can't add Customers, nor create Orders
// However it can query a lot: customers, orders, ...
EmailOrderRepository repository = new EmailOrderRepository(...){DbContext = dbContext};
// The e-mail order process: fetch non-emailed orders,
// e-mail them and update the e-mail result
using (EmailOrderProcess process = new EmailOrderProcess(...){Repository = repository}
{
... // fetch the orders that are not e-mailed yet
// email the orders
// warning about orders that can't be emailed
// update successfully logged orders
repository.SaveChanges();
Посмотрите, насколько проще сделать процесс создания, насколько универсальнее вы его сделаете: предоставьте DbContext другой JwtHelper, и данные будут регистрироваться по-другому,предоставьте репозиторию другой DbContext, и данные будут сохранены в другой базе данных, предоставьте процессу другой репозиторий, и вы будете использовать Dapper для выполнения ваших запросов.
Тестирование будет проще: создайте репозиторий, который используетСписки для сохранения таблиц, и тестирование вашего процесса с тестовыми данными будет легко
Изменения в даТабазам будет легче.Например, если позже вы решите разделить свои базы данных на одну для ваших акций и цен на акции и одну для клиентов и заказов, необходимо изменить только один репозиторий.Ни один из Процессов не заметит это изменение.
Заключение
Не позволяйте классам решать, какие объекты им нужны.Пусть создатель класса скажет: «Эй, тебе нужен DbContext? Используй этот!»Это исключит необходимость в фабриках и т. П.
Отделите вашу фактическую базу данных (DbContext) от концепции хранения и извлечения данных (Repository), используйте отдельный класс, который обрабатывает данные, не зная, как эти данные хранятся илиretrieved (класс процесса)
Создайте несколько репозиториев, которые могут получать доступ только к тем данным, которые им необходимы для выполнения задачи (+ данные, которые можно предвидеть в будущем после ожидаемого изменения).Не создавайте слишком много репозиториев, но не создавайте все, что может делать все.
Создайте классы процессов, которые выполняют только то, для чего предназначены.Не создавайте один класс процессов с 20 различными задачами.Это только усложнит описание того, что он должен делать, сложнее протестировать и сложнее изменить задачу