Entity Framework 4 Абстрактная модель - Как программно загружать навигационные свойства? - PullRequest
4 голосов
/ 25 октября 2010

У меня есть модель EF4, построенная с абстрактными сущностями / классами:

alt text

Обратите внимание, как State сущность имеет навигационное свойство, называемое Страна .

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

Теперь, если у меня есть следующий метод:

public Location FindSingle(int id)
{
   return _repository.Find().WithId(id).SingleOrDefault();
}

Это не возвращает никаких ассоциаций по умолчанию.Но как я могу динамически активно загружать ассоциации, когда я явно этого хочу?

Я не могу этого сделать:

return _repository.Find().WithId(id).Include("Country").SingleOrDefault();

Поскольку я работаю с абстрактным классомназывается Местоположение , которое не имеет навигационного свойства "Страна".Я не знаю , что такое производный тип, пока я на самом деле не выполню запрос с .SingleOrDefault.

Итак, вот что я должен был сделать:

public Location FindSingle(int id, bool includeAssociations = false)
{
    var location = _repository.Find().WithId(id).SingleOrDefault();
    return includeAssociations
                       ? LoadAssociation(location)
                       : location;
}

private Location LoadAssociation(Location location)
{
   // test derived-type, e.g:
   var state = location as State;

   if (state != null) 
      return _repository.Find().OfType<State>().Include("Country").WithId(id).SingleOrDefault();
}

По сути, я делаю 2 одинаковых звонка.Это работает?Да.Это красиво?Нет, и это на самом деле не «жадная загрузка».

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

Даже если .Includeвызывает левое внешнее соединение, проблема в том, что я отрабатываю набор сущностей "Locations".Мне нужно .Include для "State", но "States" принадлежат к набору сущностей "Locations" (производные классы принадлежат набору сущностей их родителей).

Так что я думаю, мой вопрос на самом деле довольно общий- как нам сделать .Включить потомка абстрактной сущности, если мы не знаем заранее, что это за ребенок?

Помните, я не могу сначала использовать .OfType<T>() (и затем .Include для производного типа), так как я не знаю, что такое T (и при этом не вызывает вызывающий код), следовательно, дженерики здесь не могут быть использованы.

1 Ответ

3 голосов
/ 25 октября 2010

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

После загрузки Location из хранилища, как вы узнаете, к какому типу он будет приведен, чтобы получить доступ к соответствующему свойству отношений в нем? Предположительно вы должны использовать as или is с приведением типов, и тогда вы сможете получить доступ к этим свойствам. Опять же, отчасти пахнет плохо.

Наилучшим вариантом здесь будет поддержание Type и Id объекта Location, чтобы вы могли перезагрузить его, используя соответствующий метод хранилища.

Другой вариант - вместо этого переместить отношение вверх в класс Location, чтобы каждый объект Location имел .Parent Location и коллекцию .Children Locations. Теперь вы можете включить их в свой Include, и когда вы обнаружите, что у вас есть State, вы знаете, что нужно смотреть на .Parent, а когда у вас есть Country, вы знаете, что вы смотрите на .Children. Используйте null для отсутствия родителей и пустую коллекцию для детей. Теперь, когда вы добавите континенты или города в свой класс Location, вы будете в отличной форме, чтобы использовать ту же модель.

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

context.Locations.OfType<Country>().Include("States").Cast<Location>().Union(context.Locations.OfType<State>().Include("Countries).Cast<Location>());
...