LINQ to Entity Framework - множественные пути включения приводят к паршивому TSQL - PullRequest
0 голосов
/ 14 ноября 2011

Учитывая следующий запрос LINQ to Entities (EF4) ...

var documents =
    from doc in context.Documents
        .Include(d => d.Batch.FinancialPeriod)
        .Include(d => d.Batch.Contractor)
        .Include(d => d.GroupTypeHistory.Select(gth => gth.GroupType))
        .Include(d => d.Items.Select(i => i.Versions))
        .Include(d => d.Items.Select(i => i.Versions.Select(v => v.ProductPackPeriodic.ProductPack.Product.HomeDelivery)))
        .Include(d => d.Items.Select(i => i.Versions.Select(v => v.ProductPackPeriodic.ProductPack.Product.Manufacturer)))
        .Include(d => d.Items.Select(i => i.Versions.Select(v => v.ProductPackPeriodic.ProductPeriodic)))
        .Include(d => d.Items.Select(i => i.Versions.Select(v => v.ProductPackPeriodic.SpecialContainerIndicator)))
        .Include(d => d.Items.Select(i => i.Versions.Select(v => v.Endorsements.Select(e => e.Periodic))))
        where doc.ID == this.documentID
        select doc;

Document document = documents.FirstOrDefault();

Где ...

  • Документ всегда будет иметь Пакет, который в свою очередь всегда будет иметь финансовый период и подрядчик
  • Документ всегда будет иметь один или подробнее GroupTypeHistory, каждый из которых всегда будет иметь GroupType
  • Документ будет иметь ноль или более элементов, которые в свою очередь будут иметь один или более версии
  • Версия будет иметь ноль или один ProductPackPeriodic
  • ProductPackPeriodic всегда будет иметь один ProductPack и один ProductPeriodic, вместе с нулем или одним SpecialContainerIndicator
  • ProductPack всегда будет иметь один Product
  • Товар будет иметь ноль или одного производителя, а ноль или один HomeDelivery
  • версия будет иметь ноль или более Одобрений, каждая из которых будет иметь одну Периодическую

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

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

Кто-нибудь может помочь мне найти лучшее решение?

Если бы мне было легче понять, будь я пишу TSQL напрямую, я бы посмотрел на что-то вроде следующего ...

select *
from Document d
    inner join Batch b 
        inner join FinancialPeriod fp on b.FinancialPeriodID = fp.FinancialPeriodID
        inner join Contractor c on b.ContractorID = c.ContractorID
    on d.BatchID = b.BatchID
    inner join DocumentGroupType dgt on d.DocumentID = dgt.DocumentID
    left join Item i
        left join ItemVersion iv
            left join ProductPackPeriodic ppp
                inner join ProductPack pack 
                    inner join Product p 
                        left join Manufacturer m on p.ManufacturerID = m.ManufacturerID
                        left join HomeDeliveryProduct hdp on p.ProductID = hdp.ProductID
                    on pack.ProductID = p.ProductID
                on ppp.ProductPackID = pack.ProductPackID
                inner join ProductPeriodic pp on ppp.ProductPeriodicID = pp.ProductPeriodicID
                left join SpecialContainerIndicator sci on ppp.SpecialContainerIndicatorCode = sci.SpecialContainerIndicatorCode
            on iv.ProductPackPeriodicID = ppp.ProductPackPeriodicID
            left join ItemVersionEndorsement ive 
                inner join EndorsementPeriodic ep on ive.EndorsementPeriodicID = ep.EndorsementPeriodicID
            on iv.ItemVersionID = ive.ItemVersionID
        on i.ItemID = iv.ItemID
    on d.DocumentID = i.DocumentID
where d.DocumentID = 33 -- example value

Это также может прояснить требования к отношениям.

Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 15 ноября 2011

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

0 голосов
/ 14 ноября 2011

У меня нет хорошего ответа для EF, но вам может пригодиться использование микро ORM для определенных сложных запросов.Micro ORM - это по сути низкоуровневые оболочки над SQL, которые позволяют получать строго типизированные объекты наряду с другими удобными функциями.Например, вы можете взглянуть на Dapper , который используется именно этим сайтом для некоторых узких мест.Он должен работать очень близко к собственной производительности SQL.

...