我有一个Linq查询,该查询用于替换数据库功能。这是第一个具有多个连接的连接,我似乎无法弄清楚为什么它返回0个结果。
如果您发现任何可能导致不正确回报的差异,将不胜感激……我一直在努力解决这一问题,比我应有的时间长。
Linq查询
context.StorageAreaRacks
.Join(context.StorageAreas, sar => sar.StorageAreaId, sa => sa.Id, (sar, sa) => new { sar, sa })
.Join(context.StorageAreaTypes, xsar => xsar.sar.StorageAreaId, sat => sat.Id, (xsar, sat) => new { xsar, sat })
.Join(context.Racks, xxsar => xxsar.xsar.sar.RackId, r => r.Id, (xxsar, r) => new { xxsar, r })
.Where(x => x.xxsar.sat.IsManual == false)
.Where(x => x.r.IsEnabled == true)
.Where(x => x.r.IsVirtual == false)
.Select(x => new { x.xxsar.sat.Id, x.xxsar.sat.Name })
.Distinct()
.ToList();
这是由LINQ查询生成的查询
SELECT
[Distinct1].[C1] AS [C1],
[Distinct1].[Id] AS [Id],
[Distinct1].[Name] AS [Name]
FROM ( SELECT DISTINCT
[Extent2].[Id] AS [Id],
[Extent2].[Name] AS [Name],
1 AS [C1]
FROM [dbo].[StorageAreaRacks] AS [Extent1]
INNER JOIN [dbo].[StorageAreaTypes] AS [Extent2] ON [Extent1].[StorageAreaId] = [Extent2].[Id]
INNER JOIN [dbo].[Racks] AS [Extent3] ON [Extent1].[RackId] = [Extent3].[Id]
WHERE (0 = [Extent2].[IsManual]) AND (1 = [Extent3].[IsEnabled]) AND (0 = [Extent3].[IsVirtual])
) AS [Distinct1]
产生所需结果的Sql查询
SELECT DISTINCT sat.Name, sat.Id
FROM StorageAreaRacks sar
JOIN StorageAreas sa on sa.id = sar.StorageAreaId
JOIN StorageAreaTypes sat on sat.id = sa.StorageAreaTypeId
JOIN Racks r on r.id = sar.RackId
WHERE sat.IsManual = 0
AND r.IsEnabled = 1
AND r.IsVirtual = 0
答案 0 :(得分:4)
使用联接使用LINQ方法语法很难阅读且容易出错。
使用具有LINQ查询语法的联接更好,但是仍然容易出错(您可以通过错误的键来联接),并且不会提供有关联接基数的信息。
对于LINQ to Entities查询而言,最好的方法是使用导航属性(如Gert Arnold在评论中所建议的,不仅-参见Don’t use Linq’s Join. Navigate!),因为它们没有上述缺点。
整个查询应该是这样的:
var query = context.StorageAreaRacks
.Where(sar => !sar.StorageArea.StorageAreaType.IsManual
&& sar.Rack.IsEnabled && !sar.Rack.IsVirtual)
.Select(sar => new
{
sar.StorageArea.StorageAreaType.Id,
sar.StorageArea.StorageAreaType.Name,
})
.Distinct();
或
var query = (
from sar in context.StorageAreaRacks
let sat = sar.StorageArea.StorageAreaType
let r = sar.Rack
where !sat.IsManual && r.IsEnabled && !r.IsVirtual
select new { sat.Id, sat.Name })
.Distinct();
简单,易读,几乎没有出错的地方。导航属性是EF最漂亮的功能之一,请不要错过它们。
答案 1 :(得分:3)
您的LINQ不能正确转换SQL;请参见第11页的『SQL语句。 class Profile
{
/**
* @Id
* @OneToOne(targetEntity="User", inversedBy="profile")
* @JoinColumn(name="id")
* @var User
*/
private $user;
是{{1}上的Join
而不是StorageAreaTypes
上的,这就是EF丢弃StorageAreaRack.StorageAreaId
StorageAreas.StorageAreaTypeId
的原因-它没有对结果的影响。
我认为如果您将每个联接的成员提升为扁平化匿名对象并根据它们的成员(即联接表)对其进行命名,会更清楚。另外,由于没有理由分隔StorageAreas
子句,LINQ可以使用Join
以及使用Where
的SQL。另外,如果您具有布尔值,请不要将它们与&&
或AND
进行比较。同样也没有理由传递范围变量,以后将不再使用。
将它们放在一起:
true
但是,我认为当涉及多个联接以及翻译SQL时,LINQ理解语法可以更容易理解:
false
答案 2 :(得分:0)
您在LINQ语句中缺少机架ID!= null的Where
和Distinct()
。