我一直在使用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
答案 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𜜎
我应该知道的其他任何提供商?如果我设法错过这个,直到现在可能还有其他人,我很乐意比较它们。我现在拥有的是
完全没有要求每行没有1个查询: - linq to SQL - linq to Entities - devart linqconnect
似乎工作 - llblgen
未经测试/需要反馈 - Telerik OpenAccess - NHibernate - Mindscape lightspeed
我应该知道的其他任何人?