【问题标题】:Linq Query With Multiple Joins Not Giving Correct Results具有多个连接的 Linq 查询没有给出正确的结果
【发布时间】:2019-01-10 11:26:05
【问题描述】:

我有一个用于替换数据库函数的 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 Query

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

【问题讨论】:

  • 使用什么 ORM 来运行此查询(EF6、EF Core 等)?生成的 SQL 是什么?
  • @IvanStoev ORM 是 EF6,我不得不查找如何查看生成的 SQL 查询。这对我来说是新事物,因此经历可能会有所帮助。由于太长,我将生成的查询添加到问题中。
  • 嗯,在 EF6 中,您可以简单地将 .ToList() 替换为 .ToString(),然后您将获得 SQL 查询 :)
  • @IvanStoev EF 和 LINQ 对我来说很陌生,所以我还有很多东西要学:P。
  • 请使用导航属性重写此 LINQ 查询。连接很冗长(尤其是在方法语法中)并且很容易出错。例如,StorageAreaTypes 不应与 StorageAreaId 连接。使用导航属性时,连接字段不可能不匹配。

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


【解决方案1】:

将连接与 LINQ 方法语法一起使用很难阅读且容易出错。

将连接与 LINQ 查询语法结合使用会更好,但仍然容易出错(您可以像以前那样使用错误的键进行连接)并且不会提供有关连接基数的信息。

LINQ to Entities 查询的最佳方法是使用导航属性(正如 Gert Arnold 在 cmets 中所建议的,而不仅仅是 - 请参阅 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 最漂亮的功能之一,不要错过。

【讨论】:

    【解决方案2】:

    您的 LINQ 没有正确翻译 SQL; Joins StorageAreaTypesStorageAreaRack.StorageAreaId 而不是 StorageAreas.StorageAreaTypeId 上,这就是为什么 EF 放弃 StorageAreas Join - 它对结果没有影响。

    我认为如果您提升每个联接的成员以展平匿名对象并根据它们的成员(即联接表)命名它们会更清楚。此外,没有理由分开Where 子句,LINQ 可以使用&& 以及使用AND 的SQL。此外,如果您有布尔值,请不要将它们与truefalse 进行比较。此外,没有理由传递以后不使用的范围变量。

    把它们放在一起:

    var ans = context.StorageAreaRacks
                     .Join(context.StorageAreas, sar => sar.StorageAreaId, sa => sa.Id, (sar, sa) => new { sar, sa })
                     .Join(context.StorageAreaTypes, sarsa => sarsa.sa.StorageAreaTypeId, sat => sat.Id, (sarsa, sat) => new { sarsa.sar, sat })
                     .Join(context.Racks, sarsat => sarsat.sar.RackId, r => r.Id, (sarsat, r) => new { sarsat.sat, r })
                     .Where(satr => !satr.sat.IsManual && satr.r.IsEnabled && !satr.r.IsVirtual)
                     .Select(satr => new { satr.sat.Id, satr.sat.Name })
                     .Distinct()
                     .ToList();
    

    但是,我认为当涉及到多个连接以及翻译 SQL 时,LINQ 理解语法会更容易理解:

    var ans = (from sar in context.StorageAreaRacks
               join sa in context.StorageAreas on sar.StorageAreaId equals sa.Id
               join sat in context.StorageAreaTypes on sa.StorageAreaTypeId equals sat.Id
               join r in context.Racks on sar.RackId equals r.Id
               where !sat.IsManual && r.IsEnabled && !r.IsVirtual
               select new {
                   sat.Name,
                   sat.Id
               }).Distinct().ToList();
    

    【讨论】:

      【解决方案3】:

      您的 LINQ 语句中的机架 ID != null 缺少 WhereDistinct()

      【讨论】:

      • where rackId != null 显然没有意义,因为它不是可以为空的类型。我不认为缺少不同的结果会使它从 10 个结果变为 0。我很确定我尝试过,但无论如何我会再次尝试。
      • 嗯,是的,我在 Distinct() 上看到了你的观点。也许这是与 bool/int 类型相关的问题。如果不知道完整的表和对象结构,就很难知道。
      猜你喜欢
      • 2012-06-23
      • 2019-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多