Помощь в рефакторинге Entity Framework 4 Query - PullRequest
2 голосов
/ 08 декабря 2010

У меня есть два объекта:

  • Местоположение
  • Сообщение

Это 1 .. * между Местоположение и Сообщение .

Местоположение является абстрактным, у меня есть много производных объектов, таких как Город .Я использую наследование Table-Per-Type для моей модели.

Я пытаюсь написать следующий запрос: (упрощенно)

  • Получить 20 лучших городов и включить "TopRated Post "(наивысший рейтинг).

Итак, Местоположение имеет навигационное свойство, называемое Posts .

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

Итак, вот запрос, который у меня сейчас есть.Имейте в виду, мне нужно вернуть ICollection<Location> из этого метода:

public ICollection<Location> FindTopTwentyLocations()
{
    var results = new List<Location>();

    var cities = locationRepository
                   .Find()
                   .OfType<City>()
                   .Select(x => new
                    {
                       Location = x,
                       TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
                    }).Take(20).ToList();

    foreach (var city in cities)
    {
       var aggregatedCity = city.Location;
       aggregatedCity.Posts = new List<Post> { aggregatedCity.TopPost };
       results.Add(city);
    }

    return results;
}

Итак, по сути, я собираю первые 20 городов, проецируясь в анонимный тип, чтобы я мог взять верхний пост, а затем циклчерез эту коллекцию анонимных типов, чтобы отправить сообщение обратно в объект " City ", чтобы добавить его к типу возврата List<Location>.

  • I can 't использовать .Include, поскольку это вернет все сообщения
  • Я не хочу выполнять 2 запроса
  • Я должен использовать проекцию анонимного типа, в противном случае он выдаст EFошибка (невозможно перевести запрос)

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

Есть идеи?

РЕДАКТИРОВАТЬ

Я также заметил, что проекция на анонимный тип теряет загруженный Местоположение ассоциаций, которые я получаю.

Например

var query = locationRepository.Find().OfType<City>().Include("State").ToList();

работает - все ассоциации "State" возвращены.

но:

var query = locationRepository.Find().OfType<City>().Include("State").Select(x => new {
   Location = x,
   TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
}).ToList();

В результате все ассоциации "State" будут нулевыми.

Bizarre!

Ответы [ 2 ]

0 голосов
/ 09 декабря 2010

Возможно, я что-то упускаю, но разве вы не можете сделать что-то подобное?

var cities = locationRepository 
                   .Find() 
                   .OfType<City>() 
                   .Select(x => 
                    { 
                       x.Posts = x.Posts.OrderByDescending(r => x.Rating).Take(1);
                       return x;
                    }).Take(20).ToList(); 

или если EF жалуется на изменение свойства Posts, то создайте новый объект Location и добавьте сообщение в операцию выбора.

0 голосов
/ 08 декабря 2010

Рад держать это открытым некоторое время и видеть другие ответы, но я придерживаюсь того, что у меня есть сейчас.

Один момент, который я хотел бы упомянуть относительно моей РЕДАКТИРОВАТЬ выше.

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

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

var cities = locationRepository
                   .Find()
                   .OfType<City>()
                   .Select(x => new
                    {
                       Location = x,
                       State = x.State, // include association in anon type
                       TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
                    }).Take(20).ToList();

    foreach (var city in cities)
    {
       var aggregatedCity = city.Location;
       aggregatedCity.State = city.State; // copy anon type association over
       aggregatedCity.Posts = new List<Post> { aggregatedCity.TopPost };
       results.Add(city);
    }

И это, похоже, работает.

С точки зрения рефакторинга кода, я не думаю, что запрос var cities можно оптимизировать.Я попытался переместить foreach копирование слева направо в другую проекцию .Select (например, после того, как запрос был материализован - после .ToList()), но для этого мне пришлось бы слева направоПравильно скопируйте ВСЕ свойства, например:

var cities = locationRepository
                   .Find()
                   .OfType<City>()
                   .Select(x => new
                    {
                       Location = x,
                       State = x.State, // include association in anon type
                       TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
                    }).Take(20).ToList().Select(x => new City
                    {
                       CityName = Location.Name,
                       State = State,
                       // etc etc
                    });

Учитывая, что у меня более 20 свойств на моих объектах, я не хочу этого делать.Возвращает меня в болезненный мир Linq-To-Sql и POCO.

Как я уже сказал - открыт для других предложений.

РЕДАКТИРОВАТЬ

Я закончил с использованием хранимой процедуры здесь.Мой код работает, но это более 100 строк.Я бы предпочел, чтобы эти 100+ строк абстрагировались в хранимую процедуру.

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

...