具有多个联接的Linq查询未给出正确的结果

时间:2018-08-02 16:00:09

标签: c# entity-framework linq entity-framework-6

我有一个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

3 个答案:

答案 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的WhereDistinct()