【问题标题】:Using Linq to Entities with OrderByDescending and SaveChanges将 Linq 用于带有 OrderByDescending 和 SaveChanges 的实体
【发布时间】:2012-08-07 20:46:29
【问题描述】:

我发现 Linq to Entitites 的行为与我对 Linq 工作原理的理解不一致。请考虑以下 sn-p:

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() 的第二个调用来轻松修复代码。

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

谢谢!

【问题讨论】:

    标签: entity-framework c#-4.0 linq-to-entities


    【解决方案1】:

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

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

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

    附带说明:将表达式树转换为 SQL 的需要也是不能在 LINQ to Entites 查询中使用任意 .NET 方法的原因,因为在大多数情况下,无法转换为 SQL 或 LINQ to实体提供者不知道如何翻译它。有点像...

    rsmq => MySpecialRoundMethod(...)
    

    ...MySpecialRoundMethod 是您在 C# 中编写的自定义方法,它将与 LINQ to Objects(在 IEnumerable<T> 上)一起使用,但不适用于 LINQ to Entities(在 IQueryable<T> 上)。恰好Math.Round(...) 实现了到 SQL 的转换,因此您可以将它与 Entity Framework 一起使用。

    【讨论】:

    • 很棒的反应。非常感谢你。我注意到有些方法不能在这些表达式中使用,不知道为什么。我现在明白了更多。这是否意味着如果我将一个对象插入到上下文中,然后尝试使用 select linq 查询来查找它,我将找不到它,因为它还没有被插入到数据库中?
    • @AEberhard:是的,没错。您看到的异常可能是臭名昭著的“无法转化为商店表达”异常,这是最常问的问题之一,这意味着什么。我相信人们对此感到困惑的是术语“存储表达式”,在所有情况下,它在 99% 的情况下仅表示“SQL”。我认为他们使用的是更抽象的术语,因为 EF 不需要翻译成 SQL。如果有一家公司拥有一个特殊的数据库系统和一种名为“Babble”的专有查询语言,他们可以为 Babble 编写一个 LINQ to Entities 提供程序。那么“商店表达式”将是“Babble”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-25
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-21
    • 1970-01-01
    相关资源
    最近更新 更多