我看到Linq对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循环中设置的ImpactOverall和LikelihoodOverall值在第二个foreach循环中进行排序。然而,似乎Linq基于原始的ImpactOverall和LikelihoodOverall值在第二个foreach循环中进行排序(例如,数据库中的值不是内存中的值)。我可以通过在第二个foreach循环之前立即添加对entities.SaveChanges()的第二次调用来轻松修复代码。
有人能告诉我这种行为是否是预料到的,为什么会这样?
谢谢!
答案 0 :(得分:2)
您需要记住,您在此处使用的OrderbyDescending
是IQueryable<T>
的扩展方法,而不是IEnumerable<T>
。此扩展方法和您用作此方法的参数的LINQ表达式 - rsmq => Math.Round(...)
- 不会在内存中的数据结构/集合上执行,但它仅表示表达式树。该表达式树实际发生的情况取决于数据提供程序(在IQueryable<T>
类型的可查询对象内引用)。对于Entity Framework / 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实体提供商不知道如何翻译它。有点像...
rsmq => MySpecialRoundMethod(...)
...其中MySpecialRoundMethod
是一个用C#编写的自定义方法,可以使用LINQ to Objects(在IEnumerable<T>
上)而不是LINQ to Entities(在IQueryable<T>
上) )。碰巧是Math.Round(...)
实现了对SQL的翻译,以便您可以将它与Entity Framework一起使用。