1

我看到 Linq to Entitites 的行为与我对 Linq 工作原理的理解不一致。请考虑以下代码段:

MWGRCEntities entities = new MWGRCEntities();

foreach (EDMXModel.Classes.RiskScoreMetric rsm in entities.RiskScoreMetrics.Where(rsmq.StatusCode != (int)KnownCodes.RiskScoreMetricStatusInActive))
{
    //Magic happens here...
    rsm.ImpactOverall = (rsm.ImpactWorkingGroup + rsm.ImpactExecutive) / 2;
    rsm.LikelihoodOverall = (rsm.LikelihoodWorkingGroup + rsm.LikelihoodExecutive) / 2;
}

int rank = 0;
double prevScore = -1;
double score = -2;
foreach (EDMXModel.Classes.RiskScoreMetric rsm in entities.RiskScoreMetrics.Where(rsmq.StatusCode != (int)KnownCodes.RiskScoreMetricStatusInActive).OrderByDescending(rsmq => Math.Round((Math.Round(rsmq.ImpactOverall, 3) + Math.Round(rsmq.LikelihoodOverall, 3)), 3)))
{
    score = Math.Round((Math.Round(rsm.ImpactOverall, 3) + Math.Round(rsm.LikelihoodOverall, 3)), 3);

    if (score != prevScore)
        rank++;

    rsm.Ranking = rank;
    prevScore = score;
}

entities.SaveChanges();

我预计 RiskScoreMetric 对象将在第二个 foreach 循环中使用在第一个 foreach 循环中设置的 ImpactOverall 和 LikelihoodOverall 值进行排序。然而,Linq 似乎在第二个 foreach 循环中根据原始 ImpactOverall 和 LikelihoodOverall 值进行排序(如数据库中的值而不是内存中的值)。我可以通过在第二个 foreach 循环之前立即添加对 entity.SaveChanges() 的第二个调用来轻松修复代码。

谁能告诉我这种行为是否是预期的,如果是,为什么?

谢谢!

4

1 回答 1

2

您需要记住,OrderbyDescending您在这里使用的是 的扩展方法IQueryable<T>,而不是IEnumerable<T>. 此扩展方法和您用作此方法参数的 LINQ 表达式 - rsmq => Math.Round(...)- 不会在内存中的数据结构/集合上执行,但它仅表示表达式树。该表达式树实际发生的情况取决于数据提供者(在 type 的可查询对象内部引用IQueryable<T>)。在实体框架/LINQ to Entities 的情况下,此提供程序会将表达式树转换为 SQL 字符串(取决于该提供程序的详细信息的方言,例如 SQL Server 的 T-SQL、Oracle 的一些其他本机 SQL 方言或MySQL 等)。

翻译后的 SQL 被发送到数据库服务器,并将在数据库引擎中执行,该引擎对您对内存中已加载的实体所做的更改一无所知。

所有LINQ to Entities 查询始终根据表中的当前状态和数据值在数据库中执行。他们从不考虑您是否已经加载了实体,它们具有哪些值以及是否已更改。(DbSet<T>.Find或者ObjectSet<T>.GetObjectByKey是检查具有提供的键的实体是否已加载到内存中的唯一例外,但这些方法不是 LINQ to Entities 查询,尽管它们会发出 LINQ to Entities 查询,即SingleOrDefault,如果他们找不到实体已附加到上下文。)

附带说明:将表达式树转换为 SQL 的需要也是您不能在 LINQ to Entites 查询中使用任意 .NET 方法的原因,因为在大多数情况下,无法转换为 SQL 或 LINQ to Entities 提供程序可以不知道怎么翻译。就像是...

rsmq => MySpecialRoundMethod(...)

...MySpecialRoundMethod您用 C# 编写的自定义方法在哪里可以与 LINQ to Objects(在 an 上IEnumerable<T>)一起使用,但不适用于 LINQ to Entities(在 an 上IQueryable<T>)。恰好Math.Round(...)是实现了对 SQL 的翻译,以便您可以将其与 Entity Framework 一起使用。

于 2012-08-07T21:34:09.613 回答