LINQ选择SQL视图得到错误的答案

Cod*_*ely 8 c# sql linq view

我有一个SQL视图,产生一个8列的响应.它是一个相当复杂的,所以我不会在这里列出它,它不会增加我想要了解的问题.

当我使用此查询直接在SQL Manager中查询视图时

SELECT * FROM [GPPS].[dbo].[PartIndex]
WHERE CategoryNameId = 182 AND CycleId = 13 AND BasketId = 304 AND MarketId = 8
ORDER BY ProductNameId
Run Code Online (Sandbox Code Playgroud)

我得到了预期的结果(前两行很重要)

218   13    8   304 182 124 32575   162.84
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 125 32577   156.13
218   13    8   304 182 127 32578   605.84
218   13    8   304 182 130 32579   141.51
Run Code Online (Sandbox Code Playgroud)

当我对视图执行以下LINQ时

PartIndexes.Where(x => x.CategoryNameId == 182 
                       && x.CycleId == 13 
                       && x.BasketId == 304 
                       && x.MarketId == 8)
           .ToList()
           .OrderBy(x => x.ProductNameId);
Run Code Online (Sandbox Code Playgroud)

我真的得到了

218   13    8   304 182 124 32576   184.08
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 125 32577   156.13
218   13    8   304 182 127 32578   605.84
218   13    8   304 182 130 32579   141.51
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,前两个条目是相同的,并且ID(32575和32576)的区别已丢失.

在视图上运行LINQ查询时查看SQL事件探查器会生成以下SQL

SELECT 
[Extent1].[SetNameId] AS [SetNameId], 
[Extent1].[CycleId] AS [CycleId], 
[Extent1].[MarketId] AS [MarketId], 
[Extent1].[BasketId] AS [BasketId], 
[Extent1].[CategoryNameId] AS [CategoryNameId], 
[Extent1].[ProductNameId] AS [ProductNameId], 
[Extent1].[PartId] AS [PartId], 
[Extent1].[Total] AS [Total]
FROM (SELECT 
  [PartIndex].[SetNameId] AS [SetNameId], 
  [PartIndex].[CycleId] AS [CycleId], 
  [PartIndex].[MarketId] AS [MarketId], 
  [PartIndex].[BasketId] AS [BasketId], 
  [PartIndex].[CategoryNameId] AS [CategoryNameId], 
  [PartIndex].[ProductNameId] AS [ProductNameId], 
  [PartIndex].[PartId] AS [PartId], 
  [PartIndex].[Total] AS [Total]
  FROM [dbo].[PartIndex] AS [PartIndex]) AS [Extent1]
WHERE (182 = [Extent1].[CategoryNameId]) AND (13 = [Extent1].[CycleId]) AND (304 =  [Extent1].[BasketId]) AND (8 = [Extent1].[MarketId])
Run Code Online (Sandbox Code Playgroud)

当我然后直接在SQL管理器中执行它时,我得到了所需的结果:

218   13    8   304 182 124 32575   162.84
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 125 32577   156.13
218   13    8   304 182 127 32578   605.84
218   13    8   304 182 130 32579   141.51
Run Code Online (Sandbox Code Playgroud)

任何人都知道这里可能发生了什么以及为什么执行LINQ请求会返回一个不同的结果,但是在执行LINQ查询生成的SQL时它会返回所需的结果?

当正确呈现时LINQ不直接使用时,SQL正在做什么?

Mic*_*uen 7

您的问题与此类似:使用没有主键的视图与实体

指定键小号,使您的行都是唯一的.您可以通过属性在实体映射上指定这些键:

public class YearlySalesOnEachCountry
{        
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }

    public long SalesCount { get; set; }      
    public decimal TotalSales { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

或者你可以通过代码方法来做到这一点:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);    
    modelBuilder.Entity<YearlySalesOnEachCountry>()
           .HasKey(x => new { x.CountryId, x.OrYear });     
}
Run Code Online (Sandbox Code Playgroud)


cro*_*sek 5

如果实体框架为视图选择的键不是唯一的,则可能无法正确返回结果。对于某些视图,无法定义正确的键(包含所有非空列)并且对使用视图没有任何好处。

对于这些情况,请考虑使用 EF Edmx 界面手动定义密钥:

 1) Any existing non-null field or 

 2) A separately added column "key" such as:

     select 1 as EfKey -- Must use with AsNoTracking()
Run Code Online (Sandbox Code Playgroud)

这两种方法都需要对每个查询(链接)使用“AsNoTracking()” 。

使用 AsNoTracking() 信号 EF 绕过其基于密钥的记录缓存机制。如果没有 AsNoTracking(),结果可能会被损坏,其中包含重复的行。

使用 (2) 的一个优点是,如果忘记了 AsNoTracking(),那么结果应该很糟糕,很容易被注意到。

避免使用 row_number() 的任何变体,因为它通常会妨碍 SQL 引擎中谓词的有效使用。这可以通过查看带有谓词的 SQL 实际计划来验证。(抱歉,这是我最初发布的建议。)

   -- Avoid!
   select row_number() over (order by (select null)) as RowId,
          ...
Run Code Online (Sandbox Code Playgroud)

希望 EF 团队会考虑为视图提供一个选项,允许禁用关键要求并自动使用 AsNoTracking() 与每个查询。

  • +1 表示`希望 EF 团队能够考虑为视图提供一个选项,允许禁用关键要求并在每个查询中自动使用 AsNoTracking()。` (3认同)

Cod*_*ely 2

实际上@stanke 的问题给了我一个想法。

实际上,我稍微更改了视图以包含另一列,以便可以唯一地标识每条记录。

我实际上不需要结果表中的列值,但它确实帮助 LINQ 在查询时保持记录的唯一性。看来 SQL 本身就可以很好地完成此操作,但 LINQ 需要一些帮助来保持记录的不同。

现在它在 SQL 和 LINQ 中都能按预期工作