我有一个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正在做什么?
您的问题与此类似:使用没有主键的视图与实体
指定键小号,使您的行都是唯一的.您可以通过属性在实体映射上指定这些键:
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)
如果实体框架为视图选择的键不是唯一的,则可能无法正确返回结果。对于某些视图,无法定义正确的键(包含所有非空列)并且对使用视图没有任何好处。
对于这些情况,请考虑使用 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() 与每个查询。
实际上@stanke 的问题给了我一个想法。
实际上,我稍微更改了视图以包含另一列,以便可以唯一地标识每条记录。
我实际上不需要结果表中的列值,但它确实帮助 LINQ 在查询时保持记录的唯一性。看来 SQL 本身就可以很好地完成此操作,但 LINQ 需要一些帮助来保持记录的不同。
现在它在 SQL 和 LINQ 中都能按预期工作
| 归档时间: |
|
| 查看次数: |
5949 次 |
| 最近记录: |