2012-08-07 74 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