Этот вопрос, вероятно, лучше задать в CodeReview, а не в SO, который ориентирован на конкретные вопросы, связанные с кодом. Вы можете задать 10 разных разработчиков и получить 10 разных ответов. :)
Отражение определенно имеет свою стоимость, и я совсем не люблю его использовать.
Я не хочу, чтобы это решение зависело от работы с сущностями,
поскольку не дано, что мы будем использовать это вечно.
Это довольно распространенная тема, которую я вижу в приложениях и средах, с которыми команды разработчиков, с которыми я работаю, пытаются справиться при работе с ORM. Для меня абстрагирование EF от решения - это все равно что пытаться абстрагироваться от частей .Net. Буквально нет смысла, потому что вы лишены доступа к большей части гибкости и возможностей, которые предлагает Entity Framework. Это приводит к более сложному коду для решения задач, которые EF может делать изначально, оставляя место для ошибок при повторном изобретении колеса или оставляя пробелы, которые впоследствии придется обходить. Вы либо доверяете этому, либо не должны его использовать.
Я мог бы проверить дочерние элементы сущности в хранилище сущностей, но
тогда я буду повторяться повсюду, и это решение
тоже не правы.
Это на самом деле шаблон, за который я пришел отстаивать проекты. Многие люди против шаблона Repository, но это отличный шаблон, чтобы служить границей для домена в целях тестирования. (Нет необходимости устанавливать базы данных в памяти или пытаться смоделировать DbContext / DbSets). Однако IMO шаблон Generic Repository является анти-шаблоном. Он разделяет проблемы сущностей друг от друга, однако во многих случаях мы имеем дело с «графами» сущностей, а не с отдельными типами сущностей. Вместо того, чтобы определять репозитории для каждой сущности, я выбираю что-то, что фактически является репозиторием для каждого контроллера. (С репозиториями для действительно общих сущностей, таких как поиск, например.) Это объясняется двумя причинами:
- Меньше ссылок на зависимости для передачи / mock
- Лучше обслуживает SRP
- Избегать операций с базой данных голубя
Самая большая проблема, с которой я сталкиваюсь с общими репозиториями или репозиториями для каждой сущности, заключается в том, что, хотя они, кажется, соответствуют SRP (ответственному за операции для одной сущности), я чувствую, что они нарушают его, потому что SRP имеет только одну причину для изменения. Если у меня есть сущность Order и репозиторий Order, у меня может быть несколько областей приложения, которые загружают и взаимодействуют с заказами. Методы взаимодействия с сущностями Порядка теперь вызываются в нескольких разных местах, что создает много потенциальных причин для корректировки методов. В итоге вы получаете сложный условный код или несколько очень похожих методов для обслуживания конкретных сценариев. (Заказы для перечисления заказов, заказов по клиентам, заказов по магазину и т. Д.) Когда дело касается проверки сущностей, это обычно делается в контексте всего графика, поэтому имеет смысл централизовать это в коде, связанном с графиком, а не с отдельным юридические лица. Это относится к базовым базовым операциям, таким как Add / Update / Delete. В 80% случаев это работает и экономит усилия, но эти оставшиеся 20% либо приходится вкладывать в шаблон, что приводит к неэффективному и / или подверженному ошибкам коду, либо обходным решениям. ПОЦЕЛУЙ. всегда должен превосходить D.N.R.Y когда дело доходит до разработки программного обеспечения. Консолидация в базовые классы и тому подобное - это оптимизация, которая должна выполняться по мере развития кода, когда идентифицируется «идентичная» функциональность. Когда это делается заранее как архитектурное решение, я рассматриваю эту преждевременную оптимизацию, которая создает препятствия для разработки, проблемы с производительностью и ошибки, когда «похожее», но не «идентичное» поведение группируется вместе, что приводит к ползучему условному коду для крайних случаев.
Таким образом, вместо OrderRepository для обслуживания заказов, если у меня есть что-то вроде ManageOrderController, у меня будет ManageOrderRepository для его обслуживания.
Например, мне нравится использовать методы в стиле DDD для управления объектами, где мои репозитории играют роль в построении, так как они относятся к области данных и могут проверять / извлекать связанные объекты.Таким образом, типичный репозиторий будет иметь:
IQueryable<TEntity> GetTEntities()
IQueryable<TEntity> GetTEntityById(id)
IQueryable<TRelatedEntity> GetTRelatedEntities()
TEntity CreateTEntity({all required properties/references})
void DeleteTEntity(entity)
TChildEntity CreateTChildEntity(TEntity, {all required properties/references})
Методы извлечения, включая «По идентификатору», поскольку это распространенный сценарий, возвращают IQueryable, чтобы вызывающие абоненты могли контролировать, как используются данные.Это исключает необходимость пытаться абстрагировать возможности Linq, которые EF может использовать, чтобы вызывающие абоненты могли применять фильтры, выполнять разбиение на страницы, сортировку, а затем использовать данные так, как им нужно.(Select
, Any
и т. Д.) В хранилище применяются основные правила, такие как IsActive и проверки аренды / авторизации.Это служит границей для тестов, так как mocks просто должен возвращать List<TEntity>.AsQueryable()
или обернутый с асинхронным типом коллекции.( Модульное тестирование .ToListAsync () с использованием оперативной памяти ) Хранилище также служит отправной точкой для поиска любых связанных объектов по любым применимым критериям.Это можно рассматривать как потенциальное дублирование, но изменения в этом хранилище понадобятся только тогда, когда необходимо изменить контроллер / представление / область приложения.Обычные вещи, такие как поиски, будут извлекаться из собственного репозитория.Это сокращает необходимость в множестве отдельных зависимостей репозитория.Каждая область заботится о себе, поэтому изменения / оптимизации здесь не должны учитывать или влиять на другие области приложения.
Методы «Создать» управляют правилами вокруг создания и привязки объектов к контексту, чтобы гарантировать, что объектывсегда создаются в минимально полном и действительном состоянии.Это где проверка вступает в игру.Любое значение, которое не имеет нулевого значения, передается вместе с FK (ключами или ссылками), необходимыми для обеспечения того, чтобы, если SaveChanges()
был следующим вызовом после Create, сущность была бы действительной.
«Удалить»здесь также используются методы для управления проверкой состояния данных / авторизации и применения согласованного поведения.(жесткое или мягкое удаление, аудит и т. д.)
Я не использую методы «Обновить».Обновления обрабатываются методами DDD на самом объекте.Контроллеры определяют единицу работы, используют репозиторий для извлечения сущности, вызывают методы сущности, а затем фиксируют единицу работы.Валидация может быть выполнена на уровне сущности или с помощью класса Validator.
В любом случае, это всего лишь краткое изложение одного подхода из 10+, которое вы можете получить, и, надеюсь, выделит некоторые моменты, которые следует учитыватьс любым подходом вы берете.Когда я работаю с EF, я делаю акцент на:
- Сохраняйте это простым.(KISS> DNRY)
- Используйте то, что EF может предложить, вместо того, чтобы пытаться скрыть это.
Сложный, умный код в конечном итоге приводит к большему количеству кода, а больше кода приводит к ошибкам,проблемы с производительностью, и это затрудняет адаптацию к требованиям, о которых вы не задумывались заранее.(Это приводит к большей сложности, большему количеству условных путей и большему количеству головной боли). Такие структуры, как EF, были протестированы, оптимизированы и проверены, поэтому их следует использовать.