Entity Framework, свойства навигации и шаблон репозитория - PullRequest
31 голосов
/ 06 марта 2012

Я изо всех сил пытаюсь выяснить идеальную реализацию Entity Framework и шаблона хранилища.Я использую Entity Framework 4.3, сначала код, и я просто не могу понять, как правильно использовать Entity Framework.

Мне нравятся вещи, которые EF приносит в таблицу, такие как отслеживаемые сущности, ленивыйзагрузка, свойства навигации и т. д. Но некоторые из них не очень подходят для шаблона репозитория, насколько я понимаю.Давайте рассмотрим несколько примеров, и, может быть, вы, ребята, можете мне помочь.

Универсальный репозиторий против неуниверсального репозитория

Мое первоначальное впечатление от универсального репозитория заключается в том, что мне это не нравится, потому чтоМне не нужна одинаковая функциональность для каждой сущности.Например, у меня есть хранилище, в котором хранятся простые переменные (пары ключ / значение) в базе данных.Мне не нужен метод Add или Delete, потому что это статические переменные.Мне нужен только метод Update и метод Get.Универсальный репозиторий не выглядит достаточно надежным и не допускает большого количества пользовательского кода на уровне данных.Я также ненавижу, когда универсальные репозитории возвращают IQueryable<T>, потому что это дает верхним уровням возможность писать выражения непосредственно к хранилищу данных, а верхние уровни должны предполагать, что используемая технология доступа к данным должным образом реализует IQueryable, так что она запрашивает базу данныхи не тянет все в память и не запрашивает их оттуда.

Похоже, что общие репозитории, особенно те, которые возвращают IQueryable, на самом деле не придерживаются хорошего разделения интересов.Может быть, вы, ребята, можете это понять, но сейчас я использую репозитории с явно именованными именами и только возвращаю IEnumerable или IList.

Свойства навигации

Мне нравится концепция свойств навигации,но кажется, что я редко использую их при реализации шаблона хранилища.Например, у меня есть пользователь со свойством навигации под названием «Псевдонимы».Если я хочу добавить псевдоним для пользователя, было бы очень легко добавить его через свойство навигации.

myUser.Aliases.Add(new Alias { Name="cls", Value="ClearScreen" });

Но тогда где мне позвонить dbContext.SaveChanges()?Мне передали myUser, и я использовал свойство навигации, чтобы избежать необходимости вставлять мой IAliasRepository в класс, в котором я учусь. Однако у меня теперь нет возможности сохранить мой новый псевдоним в базе данных, потому что мои верхние уровнине знает о Entity Framework.Теперь я все равно должен ввести свой IAliasRepository, чтобы я мог все _aliasRepository.SaveChanges().Ну, теперь это похоже на полную трату.Я чувствую, что должен был просто использовать _aliasRepository.AddAlias(newAlias) вместо этого, так как мне все равно нужно вводить хранилище.

Само-отслеживающиеся сущности

Само-отслеживающиеся сущности - это круто, но они не дают взаймыЭто хорошо подходит для приложений, где вы пытаетесь скрыть детали слоя доступа к данным от остальной части приложения.Например, если бы я писал репозитории и совершенно не знал, что они будут использовать EF, я бы определенно добавил метод Update(Entity entity).Однако в EF вам не нужно этого делать, потому что вы можете просто внести изменения в сущность и затем вызвать SaveChanges().Сущность отслеживает все, что было изменено, и сохраняет эти изменения в базе данных.

var myEntity = _entityRepository.GetEntity("some unique ID");
myEntity.SomeProperty = "new value";
_entityRepository.SaveChanges();

Это заставляет меня исключить мои методы обновления, которые я бы включил, если бы я не знал, что EF не нуждается в них.Это усложняет ре-факторинг в будущем, потому что мне, возможно, придется вернуться и добавить правильные методы обновления.Мой единственный вариант - все равно включать методы, а потом просто ничего не делать с ними при реализации моих репозиториев.

public void UpdateEntity(Entity entity)
{
    // Do nothing. EF is tracking changes and they will be persisted when
    // SaveChanges() is called.
}

Так что мой код будет выглядеть так, даже если он совершенно не нужен.

var myEntity = _entityRepository.GetEntity("some unique ID");
myEntity.SomeProperty = "new value";
_entityRepository.UpdateEntity(myEntity);
_entityRepository.SaveChanges();

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

Сохранение DbContextсинхронно

Еще одна странная особенность этого паттерна заключается в том, что вы должны быть очень осторожны с вашим DbContext. Один и тот же экземпляр должен быть введен во все репозитории. В противном случае, если вы извлекаете сущности из одного хранилища и пытаетесь связать их с сущностями из другого хранилища, они не будут хорошо играть вместе, потому что они из разных экземпляров DbContext. Контейнеры IoC упрощают управление, но это странная проблема для разработчиков, только начинающих с EF. Здесь на самом деле не проблема, а просто еще одна странность с Entity Framework и шаблоном хранилища.

Как правильно реализовать шаблон репозитория с EF? Как вы преодолеваете эти препятствия?

1 Ответ

14 голосов
/ 07 марта 2012

Универсальный репозиторий против неуниверсального репозитория

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

Свойства навигации

Репозиторийсам должен использоваться с совокупным корнем.Агрегированный корень - это агрегация нескольких связанных сущностей, где у вас есть хранилище только для принципала, потому что зависимости не могут существовать без родителя.Сам репозиторий обрабатывает загрузку и сохранение всех типов сущностей в агрегации.

Даже с агрегатными корнями вы столкнетесь с некоторыми проблемами.Например, как обрабатывать отношения «многие ко многим»?Отношение многие ко многим всегда представляет сценарий, в котором нет реального главного или зависимого объекта = они не находятся в агрегации.Если вы устанавливаете связь между этими двумя объектами только через свойство навигации, все в порядке, но EF также позволяет вам создавать связанный объект через свойство навигации, и это каким-то образом нарушает назначение хранилища.Вы можете принудительно использовать свой репозиторий для проверки существования отношений, но это может вызвать множество дополнительных запросов, поэтому вы, скорее всего, оставите его как утечку абстракции своей реализации.

Само-отслеживающиеся сущности

Из вашего кода я думаю, что вы перепутали само-отслеживающиеся сущности с присоединенными сущностями.То, что вы описываете, это разница между прикрепленными и отсоединенными сущностями.Если вы хотите поддерживать оба сценария в одном коде, ваш метод UpdateEntity имеет значение, потому что вы должны проверить, присоединен ли объект, и присоединить его + установить состояние, если нет.

Поддержание синхронизации DbContext

Здесь вам не хватает единицы работы.Сам репозиторий оборачивает только запросы и сохраняет объекты в контекст, но единица работы обрабатывает создание / удаление контекста и постоянные изменения в базе данных.В качестве примера можно взять DbSet (реализация репозитория EF) и DbContext (реализация единицы работы EF).

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