向我推荐一个合适的LINQ提供程序(SQL服务器,复杂查询)

时间:2012-02-08 10:39:52

标签: linq sql-server-2008 linq-to-sql c#-4.0 linq-to-entities

我一直在使用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百万(如果第一组中有100万个结果)这样的查询: 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行就不会出现问题,但我的表中有数十万到数百万行“和”我需要一个更加复杂的查询。单选(我希望一次得到复杂的图形)。考虑具有3-6级嵌套的20 +表,每个表访问额外的2-5个表。

我的SQL服务器可以完美地应对它,我也不关心带宽,它是在一个由千兆位连接链接的实例上,我无法以延迟方式获取该数据,我实际上“使用”全部它立刻就这样不只是懒惰。现在出于性能原因我不得不在许多小查询中拆分查询并在LINQ上手动连接到对象大小,这给了一些非常讨厌的代码,无论谁维护它但是我唯一的实际解决方案,所以整体包括所有小问题和最终加入,我在一个完全无法维护的单一方法中有600多行不可分割的代码。

在我去评估所有以这种思维模式工作的方式之前,实际上是否有“任何”LINQ提供商生产准备就绪,还是我最好编码和商业化我自己? (我很惊讶他们实际上并不都是这样做的,我看不到一个单独的例子,你对foreach案例和我试过要求摆脱n的情况会更好带有loadwith的+1,不要摆脱它,因为他们仍然进行n + 1个查询但只是在一次通话中批量处理,1次往返& n + 1次查询不满意当1为10 000然后10 000 000然后是10 000 000 000)

  • (请注意,我正在猜测究竟是什么触发了这个,但这不是问题,无论是什么触发这个“确切”,我肯定会在我目前的背景下触及它)

PS:请注意,我在Windows Server 2008或更高版本上运行.NET 4.0完整配置文件,在SQL Server 2008或更高版本上运行,不支持其他任何内容的提供程序都没问题,我对迁移没有任何要求,可移植性,较低的.net版本,较低的sql server支持等。如果需要,可以选择迁移到更新版本。我也没有任何建模或高级功能的先决条件,数据库已经存在,我只想查询表,所以没有建模/视图/ 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]

FROM [Person]。[联络] AS [t0] LEFT OUTER JOIN [HumanResources]。[Employee] AS [t1] ON [t1]。[ContactID] = [t0]。[ContactID] ORDER BY [t0]。[ContactID],[t1]。[EmployeeID]

现在只查询每个联系人的供应商     往来     。选择(续=>新建     {         cont.EmailAddress,         cont.EmailPromotion,         供应商= 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]

FROM [Person]。[联络] AS [t0] LEFT OUTER JOIN [采购]。[VendorContact] AS [t1] ON [t1]。[ContactID] = [t0]。[ContactID] ORDER BY [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 个答案:

答案 0 :(得分:1)

解决方法是创建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 } 

等等..我现在无法测试它,但我很确定你会创建你想要的选择(只有一个)。

答案 1 :(得分:0)

我认为你能得到的最接近的是NHibernate的Fetch(没有linq)。

对于深度嵌套的数据(例如ThenFetchMany),如果你快速达到NHibernate的限制,我也不会感到惊讶。 O / RM工具的复杂查询总是非常困难。 NHibernate在生成SQL时已经给我留下了深刻的印象(linq-to-nhibernate还没有!)。但即使是在不太复杂的情况下,有时候防止1 + N问题也是一件好事。

也许使用NHibernate的HQL你可以达到你想要的效果。

使用linq,我认为你能做的最好的事情是尽可能少地查询所需的对象图。

答案 2 :(得分:0)

您也可以这样做:

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();

答案 3 :(得分:0)

我找到了一个似乎可以处理我的核心问题的提供程序(理智的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 lightspeed

我应该知道的其他任何人?