【问题标题】:Two different LINQ queries against the same Entity Framework DbSet, both return the same results针对同一个实体框架 DbSet 的两个不同 LINQ 查询,都返回相同的结果
【发布时间】:2016-06-23 18:44:26
【问题描述】:

假设我有:

class ContractContext : DbContext
{
    DbSet<ACTIVITY> ACTIVITYs { get; set; }
}

我有以下代码,其中 startSnapshot 和 endSnapshot 是值为 1 和 2 的整数:

var activitiesStart = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == startSnapshot);
var activitiesEnd = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == endSnapshot);

这两个查询将返回相同的结果。该结果将是执行的这两个查询中的第一个查询的结果。因此,如果我强制它们都使用 ToList() 执行,

var activitiesStart = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == startSnapshot).ToList();
var activitiesEnd = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == endSnapshot).ToList();

那么存储在activityEnd中的结果将是activityStart查询的结果。如果我先执行 activityEnd 查询,则相反。这是怎么回事?我知道它们是相同的上下文,我想如果我在执行之前创建它们,我想我可以看到它如何可能组合查询。然而,在第二种情况下,一个在另一个甚至创建之前就被执行了,为什么它会践踏第二个查询呢?

生成的 SQL(两者相同):

{SELECT 
    [Extent1].[ACTIVITY_ID] AS [ACTIVITY_ID], 
    [Extent1].[ACTIVITY_NAME] AS [ACTIVITY_NAME], 
    [Extent1].[WBS_ID] AS [WBS_ID], 
    [Extent1].[VARIANCE_SNAPSHOT_ID] AS [VARIANCE_SNAPSHOT_ID], 
    [Extent1].[DUE_DATE] AS [DUE_DATE], 
    [Extent1].[IS_COMPLETE] AS [IS_COMPLETE]
FROM [p6].[ACTIVITY] AS [Extent1]
WHERE [Extent1].[VARIANCE_SNAPSHOT_ID] = @p__linq__0}

示例

var activitiesStart = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == 1).ToList();
var activitiesEnd = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == 2).ToList();

foreach (var item in activitiesStart)
{
    Debug.Write(item.VARIANCE_SNAPSHOT_ID + " ");
}

Debug.WriteLine("");

foreach (var item in activitiesEnd)
{
    Debug.Write(item.VARIANCE_SNAPSHOT_ID + " ");
}

Debug.WriteLine("");

这会打印出两行,因为首先执行了activityStart。如果我交换它们,我会得到两排两排。 SQL Server Profiler 显示查询已正确发送到服务器。

【问题讨论】:

  • startSnapshot 和 endSnapshot 的值是多少?这些是如何分配的?
  • 我看不出它们会相互影响的任何原因。调试到第一条语句时,startSnapshot 和 endSnapShot 的值是多少?
  • 它们是函数的参数,在我的测试用例中是 1(开始)和 2(结束)。我会更新问题。
  • 我认为startSnapshotendSnapshot 的实际值并不相关,只要我们都理解值不同的含义即可
  • 生成的SQL相同(应该相同)并不代表@p__linq__0参数的值相同,结果也一样。

标签: c# entity-framework linq


【解决方案1】:

由于延迟执行的工作方式,查询之间可能会出现一些串扰。查询将在执行时针对参数的值运行(即,当它是 ToList() 或以其他方式使用时)。

            var idA = 614;
            var idB = 130;

            var context = DbModel.ContextManager.Prism.dbo(DBName_Static);
            var custA = context.Customer.Where(x => x.Id == idA);
            var custB = context.Customer.Where(x => x.Id == idB);

            //different results (correct)
            var custA_forcedBefore = custA.ToList();
            var custB_forcedBefore = custB.ToList();

            //change the value of the input params to be the same
            idA = idB;

            //same results (incorrect). Because execution was deferred til after the parameter values were changed
            var custA_forcedAfter = custA.ToList();
            var custB_forcedAfter = custB.ToList();

编辑:我应该注意,当我遇到这个问题时,它并不是都在同一个函数中。在我错误地假设查询已经执行之后,输入参数的值已经在堆栈中向上更改。

【讨论】:

  • 这对我来说很有意义——我猜 EF 绑定了对参数的引用,而不是参数的值。但是,如果您查看我帖子中的第三个代码块,您会发现我正在使用不同的变量执行查询。我从来没有将一个设置为等于另一个。另外,请参阅示例部分,其中我对值进行了硬编码,我遇到了同样的问题。
  • @TomSchultz 鉴于您已经完成了即时 ToList() 并确认了 sql profiler 中的参数值,我倾向于同意这不是延迟执行参数值问题。这太奇怪了……
【解决方案2】:

我无法弄清楚这一点。我的解决方案是在第一次执行时使用 AsNoTracking。

var activitiesStart = contractContext.ACTIVITYs.AsNoTracking.Where(a => a.VARIANCE_SNAPSHOT_ID == startSnapshot).ToList();
var activitiesEnd = contractContext.ACTIVITYs.Where(a => a.VARIANCE_SNAPSHOT_ID == endSnapshot).ToList();

【讨论】:

  • 不过,这表明您的模型有问题。我从来没有见过这样的事情发生。我不会满足于变通办法。如果你不追根究底,迟早会给你带来更多麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-23
  • 1970-01-01
  • 1970-01-01
  • 2021-01-17
  • 1970-01-01
相关资源
最近更新 更多