ソースコレクションが空のときにLINQ Sum()を強制的に0に戻す方法


183

基本的に次のクエリを実行すると、一致するリードがない場合、次のクエリは例外をスローします。その場合、例外がスローされるのではなく、合計が0になるようにします。これはクエリ自体で可能でしょうquery.Any()か?クエリを保存してチェックするのではなく?

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                && l.Date.Month == date.Month
                && l.Date.Year == date.Year
                && l.Property.Type == ProtectedPropertyType.Password
                && l.Property.PropertyId == PropertyId).Sum(l => l.Amount);

2
Where返却されないnull、それはすべてのレコードを見つけられませんでした場合、それはゼロ項目のリストを返します。例外は何ですか?
Mike Perrenoud 2013

3
例外は何ですか?
Toto

3
例外が発生します:実体化された値がnullであるため、値タイプ 'Int32'へのキャストが失敗しました。結果の型のジェネリックパラメーターまたはクエリでは、null許容型を使用する必要があります。
John Mayer

1
@Stijn、あなたがやったことはまだうまくいかなかったでしょう。問題は、SQL生成方法です。Amountは実際nullにはありません。ゼロの結果を処理する方法を取り巻く問題です。提供された答えを見てください。
Mike Perrenoud 2013

39
ドルの金額にdoubleを使用するべきではありません!ほんの少しの金額です。正確な量が意図されている場合は、決してdoubleを使用しないでください。データベースの列はである必要がdecimalあり、コードではを使用する必要がありますdecimal。あなたは知っていたの忘れてfloatdouble一日の誰かが統計やスター輝度や電子の確率過程または電荷の結果のために、それらを使用することを示していますまで、あなたのプログラミングのキャリアの中で!それまでは、あなたはそれを間違っています
ErikE、2015年

回答:


391

クエリを次のように変更してみてください。

db.Leads.Where(l => l.Date.Day == date.Day
            && l.Date.Month == date.Month
            && l.Date.Year == date.Year
            && l.Property.Type == ProtectedPropertyType.Password
            && l.Property.PropertyId == PropertyId)
         .Select(l => l.Amount)
         .DefaultIfEmpty(0)
         .Sum();

このように、クエリはAmountフィールドのみを選択します。コレクションが空の場合、値が1の要素が1つ返され0、合計が適用されます。


それは確かにトリックSumを行いますが、データベース側ではなくサーバー側で最初に量の値のリストを選択しませんか?imo 2kayのソリューションはより最適で、少なくとも意味的に正しいです。
Maksim Vi。

3
@MaksimVI EFは、IQueryable<T>チェーンが停止したときに(通常ToListAsEnumerable、などを呼び出したとき、この場合はSum)、最初の実体化時にクエリを生成します。 Sumは、EF Queryable Providerによって既知で処理されるメソッドであり、関連するSQLステートメントを生成します。
Simon Belanger、2015

@SimonBelanger正直に言うと、合計はDB側で行われますが、金額を最初に選択するサブクエリで行われます。基本的に、クエリはSELECT SUM(a.Amount) FROM (SELECT Amount FROM Leads WHERE ...) AS a単にの代わりですSELECT SUM(Amount) FROM Leads。また、サブクエリには追加のnullチェックと単一の行テーブルとの奇妙な外部結合があります。
Maksim Vi。

パフォーマンスに大きな違いはなく、おそらく最適化されていますが、他のソリューションはきれいに見えます。
Maksim Vi。

5
その点に注意してくださいDefaultIfEmptyあなたがスローする必要がありますので、LINQプロバイダの数によってサポートされていないToList()か、以前のそれはに適用されるような場合には、それを使用してのようなもののオブジェクトのへのLINQのシナリオ。
クリストファーキング

188

私は別のハックを使いたい:

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                                      && l.Date.Month == date.Month
                                      && l.Date.Year == date.Year
                                      && l.Property.Type == ProtectedPropertyType.Password
                                      && l.Property.PropertyId == PropertyId)
                          .Sum(l => (double?) l.Amount) ?? 0;

18
Linq to SQLを使用すると、受け入れられた回答よりもはるかに短いSQLコードが生成されます
wertzui

3
これが正解です。他のすべては失敗します。最初にnullableにキャストし、次に最終結果をnullと比較します。
Mohsen Afshin 2015

3
これは、Linq To EFで受け入れられている回答よりもはるかに優れています。私にとって、生成されたSQLはDefaultIfEmptyの約3.8倍のパフォーマンスを発揮します。
Florian

2
これははるかに高速です。
Frakon

1
これはハックとは呼ばないでしょう。これはnullableが設計された正確な使用法です。つまり、データなしとデフォルト値の違いを示しています
MikeT


4

それは私にとって勝利です:

int Total = 0;
Total = (int)Db.Logins.Where(L => L.id == item.MyId).Sum(L => (int?)L.NumberOfLogins ?? 0);

私のLOGINテーブルでは、フィールドNUMBEROFLOGINSの一部の値がNULLで、他の値はINT番号を持っています。ここでは、1つの企業(各ID)のすべてのユーザーの合計NUMBEROFLOGINSを合計します。


1

試してください:

ダブル収益= db.Leads.Where(l => l.ShouldBeIncluded).Sum(l => (double?) l.Amount)?? 0 ;

クエリ " SELECT SUM([Amount]) "は、空のリストに対してNULLを返します。ただし、LINQを使用する場合は、「Sum(l => l.Amount)」がdoubleを返すことが想定されているため、「??」演算子を使用して空のコレクションに0を設定することはできません。

この状況を回避するには、LINQに「double?」を期待させる必要があります。" (double?)l.Amount "をキャストすることでそれを行うことができます。

SQLへのクエリには影響しませんが、空のコレクションに対してLINQを機能させます。


0
db.Leads.Where(l => l.Date.Day == date.Day
        && l.Date.Month == date.Month
        && l.Date.Year == date.Year
        && l.Property.Type == ProtectedPropertyType.Password
        && l.Property.PropertyId == PropertyId)
     .Select(l => l.Amount)
     .ToList()
     .Sum();

1
コードに関する回答に情報を追加してください
Jaqen H'ghar

1
ToList()を使用しないと何も返されないため、エラーが発生しました。しかし、ToList()は空のリストを作成し、ToList()。Sum()を実行してもエラーにはなりません。
モナ

2
ToList必要なのが合計だけである場合は、おそらくここでは使用しません。これにより、結果セット全体(Amountこの場合は各レコードのみ)がメモリに返され、そのセットが返さSum()れます。SQL Serverを介して計算を行う別のソリューションを使用する方がはるかに優れています。
Josh M.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.