#tsql #entity-framework #entity-framework-4 #linq-to-entities
#tsql #entity-framework #entity-framework-4 #linq-to-entities
Вопрос:
Задан следующий запрос 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
- пакет продуктов всегда будет содержать один продукт
- у продукта будет ноль или один производитель и ноль или одна доставка на дом
- версия будет иметь ноль или более подтверждений, каждое из которых будет иметь одно периодическое
Приведенный выше запрос 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
Это также может прояснить требования к взаимосвязи.
Заранее спасибо.
Ответ №1:
Для подобных сценариев я пишу специальные хранимые процедуры, а затем использую EFExtensions с пользовательским материализатором, чтобы не только получить отличную производительность, но и правильно материализовать объекты.
Комментарии:
1. Отличный ответ; это значительно улучшило производительность, как я надеялся, и (как вы предложили) позволяет мне указать мой собственный TSQL. Спасибо.
Ответ №2:
У меня нет хорошего ответа для EF, но вам может подойти использование micro ORM для определенных сложных запросов. Микро-ORM — это, по сути, низкоуровневые оболочки поверх SQL, которые позволяют вам получать строго типизированные объекты наряду с другими удобными функциями. Вы можете взглянуть, например, на Dapper, который используется этим самым сайтом для некоторых запросов с узким местом. Он должен работать очень близко к производительности собственного SQL.