Я уже давно использую LINQ to SQL и для сущностей и в целом очень доволен ими.Однако я знаю об их ограничениях, и одно из них становится для меня большой проблемой.Когда вы выполняете сложный вложенный запрос в форме
MyContext.SomeTable
.Select(item=>new{
item.SomeProperty1,
item.SomeProperty2,
item.NavigationProperty1
.Select(nav1=> new {// retrieve some properties}), // This triggers a single query as long as don't have more than one subquery
item.NavigationProperty2
.Select(nav2=> new {// retrieve some properties}) // This triggers one query PER ROW in the original query
});
Протестированные мной провайдеры являются сущностями LINQ TO SQL / LINQ TO (и, что еще хуже, разрабатывают LINQConnect, который работает хуже и генерирует 1 на строку впервое свойство навигации)
То, что я сейчас получаю, генерируется (псевдокод):
select t1.a,t1.b,t2.c,t2.d from mytable as t1
join navproperty1table as t2
и 1 миллион (если в первом наборе 1 миллион результатов) таких запросов:select t3.e,t3.f from navproperty2table as t3 where id = X
(X изменяется при запросе X к следующему элементу, возвращенному первым запросом)
Что я хочу:
select t1.a,t1.b,t2.c,t2.d,t3.e,t3.f from mytable as t1
join navproperty1table as t2
join navproperty2table as t3
Теперь, если в исходной таблице было 3 строки, было быЭто не проблема, но у меня в таблицах есть десятки тысяч или миллионы строк »и« мне нужен гораздо более сложный запрос за один выбор (я хочу получить сложный график сразу).Подумайте о 20+ таблицах с 3-6 уровнями вложенности, получая доступ к дополнительным 2-5 таблицам каждая.
Мой сервер SQL может с этим прекрасно справиться, мне тоже не нужна пропускная способность, он связан с экземпляромпо гигабитному соединению я не могу получить эти данные отложенным способом, я фактически «использую» все это сразу, так что это не просто лень.Прямо сейчас по соображениям производительности мне пришлось разделить запрос на множество небольших запросов и вручную соединить их по LINQ для размера объекта, что дает некоторый действительно неприятный код для тех, кто его поддерживает, но было единственным реальным решением, которое у меня было, так что в целом, включая всенебольшие запросы и окончательное объединение, у меня более 600 строк неразборчивого кода в одном методе, который абсолютно не поддерживается.
Есть ли на самом деле "какие-нибудь" работы с поставщиками LINQ, готовые сегодня, прежде чем я пойду и оценил их всеработать в таком умонастроении или мне лучше заниматься кодированием и коммерциализацией своего собственного?(Я очень удивлен, что на самом деле не все они работают таким образом, я не вижу ни одного случая, когда вам лучше было бы использовать случай foreach, и тех, которые я пробовал, чтобы избавиться от n+1 с loadwith, не избавляйтесь от него, поскольку они все еще выполняют n + 1 запросов, а просто группируют его за один вызов, 1 запрос туда и обратно и n + 1 запросов не удовлетворяют, когда 1 равно 10 000, а затем 10 000 000а затем 10 000 000 000)
- (обратите внимание, что я размышляю над тем, что именно вызывает это, но это не вопрос, независимо от того, что вызывает это "точно", я уверен, чтопоразить его в моем текущем контексте)
PS: обратите внимание, что я использую полный профиль .NET 4.0 на Windows Server 2008 или выше и на SQL Server 2008 или выше, поставщик, который неподдержка всего остального была бы хороша, у меня нулевые требования к миграции, переносимости, более низким версиям .net, более низкой поддержке сервера sql и т. д. При необходимости можно перейти на более поздние версии.У меня также нет никаких предпосылок для моделирования или расширенных функций, БД уже есть, я хочу только запрашивать таблицы, так что что-то без поддержки моделирования / представлений / DML / хранимых процедур / функций отлично, мое единственное требованиеявляется разумной генерацией SQL для сложных запросов и графов объектов
РЕДАКТИРОВАТЬ: для пояснения приведен фактический пример проблемы с БД, которую может получить каждый, adventureworks
Запрос сотрудников для каждого контакта
Contacts
.Select(cont=>new
{
cont.EmailAddress,
cont.EmailPromotion,
Employees = cont.Employees
.Select(emp=>new
{
emp.Gender,
emp.HireDate
}).ToList()
}).ToList()
Генерирует
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], (
SELECT COUNT(*)
FROM [HumanResources].[Employee] AS [t2]
WHERE [t2].[ContactID] = [t0].[ContactID]
) AS [value]
ОТ [Персона]. [Контакт] AS [t0] ВЛЕВО НАРУШЕНИЕ [HumanResources]. [Сотрудник] AS [t1] ВКЛ [t1]. [ContactID]= [t0]. [ContactID] ORDER BY [t0]. [ContactID], [t1]. [EmployeeID]
Теперь запрашивать только поставщиков для каждого контакта Contact .Select (cont => new {cont.EmailAddress, cont.EmailPromotion, Vendors = cont.VendorContacts.Select (vend => new {vend.ContactTypeID, vend.ModifiedDate}). ToList ()}). ToList ()
все еще в порядке:
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[ContactTypeID], [t1].[ModifiedDate], (
SELECT COUNT(*)
FROM [Purchasing].[VendorContact] AS [t2]
WHERE [t2].[ContactID] = [t0].[ContactID]
) AS [value]
ОТ [Персона]. [Контакт] КАК [t0]ВЛЕВО НАРУЖНОЕ СОЕДИНЕНИЕ [Покупка]. [VendorContact] КАК [t1] ВКЛ [t1]. [ContactID] = [t0]. [ContactID] ЗАКАЗАТЬ [t0]. [ContactID], [t1]. [VendorID]
Теперь запрашиваем оба одновременно (запускает X запрос строки)
Contacts
.Select(cont=>new
{
cont.EmailAddress,
cont.EmailPromotion,
Employees = cont.Employees
.Select(emp=>new
{
emp.Gender,
emp.HireDate
}).ToList(),
Vendors = cont.VendorContacts.Select(vend=>new
{
vend.ContactTypeID,
vend.ModifiedDate
}).ToList()
}).ToList()
Генерирует уродливое и медленное (не вставляя все это по понятным причинам, но вы получите точку):
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], (
SELECT COUNT(*)
FROM [HumanResources].[Employee] AS [t2]
WHERE [t2].[ContactID] = [t0].[ContactID]
) AS [value], [t0].[ContactID]
FROM [Person].[Contact] AS [t0]
LEFT OUTER JOIN [HumanResources].[Employee] AS [t1] ON [t1].[ContactID] = [t0].[ContactID]
ORDER BY [t0].[ContactID], [t1].[EmployeeID]
GO
-- Region Parameters
DECLARE @x1 Int = 1
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 2
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 3
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 4
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 5
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 6
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 7
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 8
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 9
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 10
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
Что я ожидаю / хотел бы видеть сгенерированным:
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], [t2].[ContactTypeID], [t2].[ModifiedDate] ,[t0].[ContactID]
FROM [Person].[Contact] AS [t0]
LEFT OUTER JOIN [HumanResources].[Employee] AS [t1] ON [t1].[ContactID] = [t0].[ContactID]
LEFT OUTER JOIN [Purchasing].[VendorContact] AS [t2] ON [t2].[ContactID] = [t0].[ContactID]
GO