Шаблон для извлечения графов сложных объектов с шаблоном репозитория с платформой Entity Framework

У нас есть сайт ASP.NET MVC, который использует абстракции Entity Framework с шаблонами репозитория и UnitOfWork. Мне интересно, как другие реализовали навигацию по сложным объектным графам с этими шаблонами. Позвольте мне привести пример от одного из наших контроллеров:

var model = new EligibilityViewModel
   {
       Country = person.Pathway.Country.Name,
       Pathway = person.Pathway.Name,
       Answers = person.Answers.ToList(),
       ScoreResult = new ScoreResult(person.Score.Value),
       DpaText = person.Pathway.Country.Legal.DPA.Description,
       DpaQuestions = person.Pathway.Country.Legal.DPA.Questions,
       Terms = person.Pathway.Country.Legal.Terms,
       HowHearAboutUsOptions = person.Pathway.Referrers
   };

Это процесс регистрации и почти все зависает от Person класса POCO. В этом случае мы кэшируем человека через процесс регистрации. Теперь я начал внедрять последнюю часть процесса регистрации, которая требует более глубокого доступа к данным в графе объектов. В частности, данные DPA, которые зависают от Legal внутри страны.

Приведенный выше код просто отображает информацию о модели в более простой формат для ViewModel. Мой вопрос заключается в том, что вы рассматриваете эту довольно глубокую навигацию по хорошей графике графика или можете абстрагировать извлечение объектов дальше по графику в репозитории?

Ответ 1

На мой взгляд, важный вопрос здесь - отключить LazyLoading?

Если вы ничего не сделали, по умолчанию он включен.

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

После того, как пользователь C получил информацию из M, он должен быть готов выполнить проецирование (если необходимо) и перейти на V, не возвращайтесь к M.

Вот почему в нашей реализации (мы также используем репозиторий, ef4 и единицу работы) мы отключим Lazy Loading и разрешаем пройти через навигационные свойства через наш сервисный уровень (a серии инструкций "Включить", сделанные слаще посредством перечислений и методов расширения).

Затем мы выполняем eager-load эти свойства, как того требуют контроллеры. Но важно то, что контроллер должен явно запросить их.

Что в основном говорит о пользовательском интерфейсе - "Эй, вы получаете только основную информацию об этом объекте. Если вам нужно что-нибудь еще, попросите его".

У нас также есть Service Layer, обеспечивающий посредничество между контроллерами и репозиторием (наши репозитории возвращаются IQueryable<T>). Это позволяет репозиторию выйти из бизнеса обработки сложных ассоциаций. Желательная загрузка выполняется на уровне обслуживания (а также в таких вещах, как пейджинг).

Преимущество сервисного уровня прост - более свободная связь. Репозиторий обрабатывает только Add, Remove, Find (который возвращает IQueryable), Unit of Work обрабатывает "новички" DC и Commiting of changes, Service layer обрабатывает материализацию объектов в конкретные коллекции.

Это хороший, 1-1 стековый подход:

personService.FindSingle(1, "Addresses") // Controller calls service
 |
 --- Person FindSingle(int id, string[] includes) // Service Interface
      |
       --- return personRepository.Find().WithIncludes(includes).WithId(id); // Service calls Repository, adds on "filter" extension methods
           |
            --- IQueryable<T> Find() // Repository
                |
                 -- return db.Persons; // return IQueryable of Persons (deferred exec)

Мы еще не встали на уровень MVC (мы делаем TDD), но уровень сервиса может быть другим местом, в котором вы могли бы гидратировать основные объекты в ViewModels. И снова - это будет до контроллера, чтобы решить, сколько информации он хочет.

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

Что касается количества репозиториев, это тема с высокой дискуссией. Некоторым нравится иметь по одному на сущность (overkill, если вы спросите меня), некоторым нравится группироваться на основе функциональности (имеет смысл с точки зрения функциональности, с ней легче работать), однако у нас есть один для каждого заполнителя.

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

Следовательно, не имеет смысла иметь другой репозиторий для обработки "Пути", когда путь всегда связан с конкретным "Лицом". Репозиторий Person должен обрабатывать это.

Опять же - возможно, если вы закроете свой EDMX, мы сможем дать вам больше советов.

Этот ответ может распространяться слишком далеко, основываясь на объеме вопроса, но подумал, что я дам подробный ответ, поскольку мы имеем дело с этим точным сценарием прямо сейчас.

НТН.

Ответ 2

Это зависит от того, сколько информации вы используете в любой момент.

Например, если вы просто хотите получить имя страны для человека (person.Pathway.Country.Name), в чем смысл убрать все остальные объекты из базы данных?

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

Не рекомендуется извлекать весь объект и все, что связано с этим объектом, каждый раз, когда вы хотите получить доступ к некоторым свойствам. Что делать, если вы делаете это после каждой обратной передачи или даже несколько раз? Делая это, вы можете сделать жизнь проще в краткосрочной перспективе за счет того, что вы делаете приложение менее масштабируемым в долгосрочной перспективе.

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