2015-01-09 42 views
12

我有一個包含這些行的大型實體框架查詢。Sum()在實體框架查詢中返回null

var programs = from p in Repository.Query<Program>() 
       where p.OfficeId == CurrentOffice.Id 
       let totalCharges = p.ProgramBillings.Where(b => b.Amount > 0 && b.DeletedDate == null).Select(b => b.Amount).Sum() 
       let totalCredits = p.ProgramBillings.Where(b => b.Amount < 0 && b.DeletedDate == null).Select(b => -b.Amount).Sum() 
       let billingBalance = (totalCharges - totalCredits) 

當我兌現了數據,我得到以下錯誤:

The cast to value type 'Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.

如果我改變如下我的查詢(兩種類型轉換添加),錯誤消失。

var programs = from p in Repository.Query<Program>() 
       where p.OfficeId == CurrentOffice.Id 
       let totalCharges = (decimal?)p.ProgramBillings.Where(b => b.Amount > 0 && b.DeletedDate == null).Select(b => b.Amount).Sum() 
       let totalCredits = (decimal?)p.ProgramBillings.Where(b => b.Amount < 0 && b.DeletedDate == null).Select(b => -b.Amount).Sum() 
       let billingBalance = (totalCharges - totalCredits) 

我不明白這一點。 ProgramBilling.Amount是一個不可空的Decimal。如果我將鼠標懸停在Sum()調用上,Intellisense說它返回Decimal類型。而另外的測試證實,在我的第二個版本中,對於ProgramBillings沒有數據的那些行,totalChargestotalCredits都設置爲空。

問題:

  1. 我明白Sum()一個空的集合返回0。在什麼情況下這是不正確的?

  2. 如果有時候這不是真的,那麼爲什麼當我將鼠標懸停在Sum()上時,Intellisense顯示它返回類型Decimal而不是Decimal?看起來Intellisense和我有同樣的理解。

編輯:

這似乎是一個簡單的辦法是做這樣的事情Sum() ?? 0m。但是,這是非法的,給我的錯誤:

Operator '??' cannot be applied to operands of type 'decimal' and 'decimal'

+3

你看過這篇文章:http://stackoverflow.com/questions/17593371/how-to-force-linq-sum-to-return-0-while-source-collection-is-empty – 2015-01-09 18:33:52

+0

@JonathanWood:「但我仍然對爲什麼這是必要的感到困惑,」再次閱讀鏈接問題的標題。這是必要的,因爲收集是空的。如果空集合的總和不爲零,你會期望什麼?顯然'0'不會削減它,因爲'0'是一個完全有效的值。 – 2015-01-09 18:43:37

+0

@DavidTansey:我確實看到了這一點,但它並沒有提供我在問題結束時提出的任何問題的線索。這是我將根據需要採取的方法。只是想明白爲什麼。 – 2015-01-09 18:45:40

回答

8

I understood Sum() returned 0 for an empty collection. Under what circumstances is this not true?

當你不使用LINQ to對象,比如這裏的情況。這裏有一個將此查詢翻譯爲SQL的查詢提供程序。 SQL操作的SUM操作符具有不同的語義。

And if sometimes that is not true, then why when I hover over Sum(), Intellisense shows it returns type Decimal and not Decimal? It appears Intellisense had the same understanding that I had.

C#LINQ SUM運算符不返回可爲空的值;它需要具有非空值,但S​​QL SUM運算符具有不同的語義,因此在對空集進行求和時將返回null,而不是0。事實上,在C#需要非空值的上下文中提供值null是所有事情都破壞的全部原因。如果這裏的C#LINQ SUM運算符返回了一個可爲空的值,那麼只需返回null即可毫無問題地返回。

這是C#操作符與SQL運算符之間的差異,它用於表示導致此錯誤的SQL運算符。

+0

感謝您的解釋。我認爲它必須處理C#和SQL語義之間的區別,但是這似乎是編譯器假定C#語義的一個缺點。如果沒有,我可以簡單地使用合併運算符來強制返回零值爲零。 – 2015-01-09 18:53:07

+0

@JonathanWood LINQ無法支持任何可以在任何地方查詢的每一件事情的語義。如果它嘗試了,那麼會讓它幾乎無法使用。它需要做出關於支持什麼類型的操作,不支持什麼以及應該支持哪些語義的某些決定。然後它成爲查詢提供者的職責,將可以在LINQ中描述的操作映射到正在查詢的任何操作。這種情況的現實是,一切都不會映射到1:1。 – Servy 2015-01-09 18:55:21

+0

我明白了,但在那種情況下,它似乎要求更嚴格的類型化構造。無論如何,我無法找到對此的解釋。這個問題引起了相當多的關注。再次感謝。 – 2015-01-09 19:00:00

0

我已經得到了同樣的問題在我的EF查詢時,其中一個集合是空的,一個快速修復,這是投可空十進制:

var total = db.PaiementSet.Sum(o => (Decimal?)o.amount) ?? 0M; 

希望它幫助。

+0

沒有爲我工作。 – Sentinel 2017-06-15 08:34:26

0

在.Sum之前添加一個DefaultIfEmpty(0。0M)