2012-08-07 73 views
1

我看到Linq与Entitites的行为不符合我对Linq如何工作的理解。请考虑下面的代码片段:使用Linq与OrderByDescending和SaveChanges的实体

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对象将在使用ImpactOverall和LikelihoodOverall值在第一foreach循环设置第二个foreach循环进行排序。但是,看起来Linq正在基于原始ImpactOverall和LikelihoodOverall值(如数据库中的值而不是内存中的值)对第二个foreach循环进行排序。我可以简单地通过在第二个foreach循环之前添加第二个对entity.SaveChanges()的调用来修复代码。

任何人都可以告诉我,如果这种行为是预期的,如果是的话,为什么?

谢谢!

回答

2

您需要注意,您在此处使用的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查询,即SingleOrDefault, t找到已经附加到上下文的实体。)

作为一个备注:需要将表达式树转换为SQL也是为什么您不能在LINQ to Entites查询中使用任意.NET方法的原因,因为在大多数情况下没有可能的SQL翻译,或者LINQ to Entities提供者不知道如何翻译它。喜欢的东西...

rsmq => MySpecialRoundMethod(...) 

...其中MySpecialRoundMethod是你用C#编写自定义的方法将LINQ工作对象(上IEnumerable<T>),但与LINQ到实体(上IQueryable<T>)。它恰好是对Math.Round(...)实现了一个到SQL的转换,因此您可以将其与实体框架一起使用。

+0

真棒回应。非常感谢你。我注意到有些方法不能用于这些表达式,并且不知道为什么。我现在明白了更多的事情。这是否意味着如果我将一个对象插入到上下文中,然后尝试使用select linq查询找到它,我不会找到它,因为它没有被插入到数据库中? – AEberhard 2012-08-07 21:53:23

+1

@AEberhard:是的,确切地说。你所看到的例外可能是臭名昭着的“不能转化为商店表达”的例外,它是最常见的问题之一,意味着什么。我相信人们对此感到困惑的是“商店表达”这个术语,99%的案例都意味着“SQL”。我认为他们正在使用更抽象的术语,因为EF不需要翻译成SQL。如果有一家公司拥有一个特殊的数据库系统和一个名为“Babble”的专有查询语言,他们可以为Babble编写一个LINQ to Entities提供程序。那么“商店表达”将会是“Bab”“。 – Slauma 2012-08-07 22:07:48