Посоветуйте подходящего поставщика LINQ для меня (сервер SQL, сложные запросы) - PullRequest
5 голосов
/ 08 февраля 2012

Я уже давно использую 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

Ответы [ 4 ]

1 голос
/ 08 февраля 2012

Обходной путь должен был бы создать view

по вашему определению

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

и используйте linq-2-sql для запроса этого представления.

Не уверен, что я полностью понимаю ваш запрос, но вы можете просто сделать

from x in MyContext.Sometable
Select new { x.a, x.b, x.t2.c, x.t2.d, x.t3.f } 

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

0 голосов
/ 11 февраля 2012

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

http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=20658&StartAtMessage=0&#116494

Любые другие поставщики, о которых я должен знать?Если бы мне удалось пропустить это до сих пор, могут быть и другие, и я был бы очень рад сравнить их.Теперь у меня есть

Полностью не соответствует моему требованию для запроса не по 1 запросу на строку: - linq to SQL - linq to Entities - devart linqconnect

Кажется, работает - llblgen

Не проверено / нужна обратная связь - Telerik OpenAccess - NHibernate - Mindscape световая скорость

есть еще какие-нибудь сообщения, которые я должен знать?

0 голосов
/ 10 февраля 2012

Вы также можете сделать что-то вроде этого:

var venderContacts= VendorContacts.ToLookup (u =>u.ContactID);
var contracts=Contacts
    .Select(cont=>new 
    {
        cont.EmailAddress,
        cont.EmailPromotion,
        Employees = cont.Employees
            .Select(emp=>new
            {
                emp.Gender,
                emp.HireDate
            }).ToList(),
        Vendors = venderContacts[cont.ContanctID]
    }).ToList();
0 голосов
/ 08 февраля 2012

Я думаю, что ближайший к вам доступ - это Fetch (без linq) в NHibernate.

С глубоко вложенными данными (например, ThenFetchMany), я не удивлюсь, если выБыстро попал в пределы NHibernate тоже.Сложные запросы всегда очень сложны для инструментов O / RM.NHibernate всегда производил на меня впечатление, когда дело дошло до сгенерированного SQL (linq-to-nhibernate еще нет!).Но даже при менее сложных сценариях иногда было трудной задачей предотвратить проблему 1 + N.

Может быть, с HQL от NHibernate вы можете достичь того, что вы хотите.

Я думаю, что с linq лучшее, что вы можете сделать, - это получить требуемый граф объектов в наименьшем количестве запросов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...