Довольно сложный запрос LINQ к сущностям

#entity-framework #entity-framework-4 #linq-to-entities #entity-framework-4.1

#entity-framework #entity-framework-4 #linq-to-entities #entity-framework-4.1

Вопрос:

У меня есть две сущности, предположим, что они называются Container и Record. У них есть отношения мастер-потомок: «контейнер» может содержать много записей.

Таблица записей в базе данных содержит следующие столбцы:

  • ID
  • Дата
  • Container_Id
  • RecordType_Id

Объект записи не имеет никаких свойств навигации, которые ссылаются на контейнер.

Я пишу запрос LINQ для своего репозитория, который будет извлекать ТОЛЬКО записи для контейнера, которые имеют самую последнюю дату для каждого RecordType_Id. Все старые записи следует игнорировать.

Итак, если в контейнере есть, скажем, 5 записей, по одной для каждого RecordType_Id, с датой 24 / май / 2011. Но также содержит еще 5 записей для каждого RecordType_Id, но с датой 20 / Май / 2011. Тогда будут извлечены и добавлены в коллекцию в контейнере только первые 5 с датой 24 мая.

Я придумал SQL-запрос, который выполняет то, что мне нужно (но, может быть, есть какой-то более эффективный способ?):

 select t.*
from Records t
    inner join (
        select Container_Id, RecordType_Id, max(Date) AS MaxDate
        from Records
        group by Container_Id, RecordType_Id ) g
    on t.Date = g.MaxDate
        and t.Container_Id = g.Container_Id
        and t.RecordType_Id = g.RecordType_Id 
order by t.Container_Id 
    , t.RecordType_Id 
    , t.Date
  

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

Ответ №1:

У меня в голове не укладывается:

 var q = from c in Container
        from r in c.Records
        group r by r.RecordType.RecordType_Id into g
        select new
        {
            Container = c,
            RecordType_Id = g.Key,
            Records = from gr in g
                      let maxDate = g.Max(d => d.Date)
                      where gr.Date == maxDate
                      select gr
        };
  

Комментарии:

1. В строке 6 говорится, что c символ не может быть разрешен. Однако я не уверен, что получение анонимного типа является правильным решением. Мне нужно, чтобы EF возвращал отслеживаемые объекты контейнера в обычном режиме. За исключением того, что коллекция «Записей» каждого контейнера ограничена включением только самых последних записей каждого типа.

2. Возможно, я иду по этому неправильному пути. Возможно, мне следует предоставлять объекты контейнера по мере их поступления. Но сделайте так, чтобы коллекция записей загружалась с задержкой, чтобы потребитель мог добавить свою логику отложенного запроса для выполнения ограничения «только самые последние записи».

3. Вы можете обойти неразрешенный символ, включив его в группировку. Не позволяйте анонимному типу сбить вас с толку — Records свойство представляет собой список сущностей, как вы и просили.

4. Я думаю, возможно, мой вопрос был плохо сформулирован. Это мой контейнерный репозиторий. Поэтому он должен возвращать полностью инициализированные объекты контейнера. Объекты записи должны быть отфильтрованы так, чтобы они были только самыми последними для каждого объекта контейнера.

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

Ответ №2:

Попробуйте использовать LINQPad, это поможет вам легко тестировать запросы linq. Даже против существующей модели EF (которая есть в вашем проекте). Посетите http://www.linqpad.net /