9

我有一个 SQL 视图,它产生 8 列的响应。它相当复杂,所以我不会在这里列出它,它不会对我试图理解的问题增加太多。

当我直接使用此查询查询 SQL 管理器中的视图时

SELECT * FROM [GPPS].[dbo].[PartIndex]
WHERE CategoryNameId = 182 AND CycleId = 13 AND BasketId = 304 AND MarketId = 8
ORDER BY ProductNameId

我得到了(前两行很重要)的预期结果,并且 ProductNameId 列在结果中排​​名第 7

                            vvvvv
                            =====   
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

当我对视图执行以下 LINQ

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

我实际上得到的是:

                            vvvvv
                            ===== 
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

如您所见,前两个条目是相同的,并且 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])

然后当我直接在 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

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

SQL 在直接使用时在做什么,而在正确呈现时 LINQ 没有做什么?

4

7 回答 7

8

您的问题与此类似:Using a view with no primary key with Entity

指定使您的行唯一的key 。您可以通过属性在实体映射上指定这些键:

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; }
}

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

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);    
    modelBuilder.Entity<YearlySalesOnEachCountry>()
           .HasKey(x => new { x.CountryId, x.OrYear });     
}
于 2013-01-08T15:34:17.377 回答
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()

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

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

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

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

   -- Avoid!
   select row_number() over (order by (select null)) as RowId,
          ...

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

于 2015-04-02T23:37:51.663 回答
2

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

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

我实际上并不需要结果表中的列值,但它确实有助于 LINQ 在查询时保持记录的唯一性。看起来 SQL 本身就可以很好地做到这一点,但 LINQ 需要一点帮助来保持记录的不同。

它现在可以在 SQL 和 LINQ 中按预期工作

于 2013-01-08T15:19:34.693 回答
2

我添加了

ISNULL(CONVERT(VARCHAR(50), NEWID()), '') AS Pkid 

作为我认为解决这个问题的第一篇专栏。

于 2017-08-08T18:32:17.097 回答
1

在前两行的第 6 列,您有相同的值 - 124,如果这是此视图中的某个外键,则可能导致过滤一行。我在使用数据表加载函数时遇到了类似的情况,因为它在将检索到的数据加载到数据表时应用了约束。尝试从 linq 到 sql 模式中删除密钥。

于 2013-01-08T14:52:45.567 回答
0

我没有看到您的 LINQ 有任何问题。

您如何填充 PartIndexes(ORM、SqlCommand 等)?

也许你在 DAL 或 ORM 映射中的逻辑搞砸了,这搞砸了存储在 PartIndexes 中的内容。

于 2013-01-08T14:56:59.793 回答
0

假设您使用的是EF (entity framework)。如果是这样,请在 IQueryable 上使用.AsNoTracking(),否则您可能会得到缓存的结果。

于 2015-02-06T15:58:21.997 回答