web-dev-qa-db-ja.com

LINQ to Entitiesクエリが何も返さない場合のnullまたは空のオブジェクト

次のようなLINQクエリがあるとします。

application = CreditDatabase
                        .Applications
                        .Select(Mapper.Map<Application>) 
                        .Where(c => c.uID == urID)
                        .DefaultIfEmpty().First();

LINQクエリが空の結果セットを返す場合はnullを返します。これは正しいです"。空のオブジェクトを返す方が良いですか?もしそうなら、どうすればそれを行うことができますか?このリンクは、null以外の参照タイプを返すことはできないことを示しています: https://msdn.Microsoft.com/en-us/library/bb360179(v = vs.110).aspx

3
w0051977

データベースクエリはresult setsを返します。 空のセットが妥当な答えです。それはあなたが検索したもののどれも持っていないことを意味します。

私はあなたのコードが結果の標準化にはるかに進んでいると主張します。

コード(この行の直後のどこか)は、検索したアプリケーションが存在するかどうかを確認するため、.FirstOrDefault()

できます—そして私はshouldと主張します—代わりに:空のセットを確認します(つまり、結果セットのサイズを使用します。おそらく.Any())アプリケーションが存在するかどうかを確認します。これは、空のセットをデフォルトに変換し、最初に、最後にnullをチェックして対象のアプリケーションが存在するかどうかを確認するよりも、かなりクリーンです。

ここまで行くのではなく、

application = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID)
                    .DefaultIfEmpty().First();
if ( application == null ) {
   // handle missing application, maybe add it...
} else {
    // use the found application
}

デフォルトの処理を省略します。代わりに:

search = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID);
if ( ! search.Any () ) {
    // handle missing application, maybe add it...
} else {
    application = search.Single ();
    // use the found application
}

補遺:上記のapplication = search.Single ()では、.Single()ではなく.First()を使用しました。ここでは1つの一致が理にかなっています。これは、一意の主キーの完全一致であると想定したものによるルックアップです(それ以外の場合は、@ JacquesBが指摘するように、クエリは順序付けられていないため、何を保持しているかがわかりません。 .First()で破棄するもの)

8
Erik Eidt

場合によります!

しかし、最初に明確化:.DefaultIfEmpty().First()は、同じように動作する.FirstOrDefault()として簡単に記述できます。最初の項目を返します。結果が空の場合は、nullを返します。 しかしおそらくここにバグがあります:First()は、複数のアイテムがあり、最初のアイテムを選択したいことを示します-ただし、順序付けが含まれていないため、randomアイテム。これはおそらくあなたが望むものではありません!複数の一致があると本当に予想される場合は、一致のリストを返すだけです。単一のアイテムが予想される場合は、代わりにSingle()を使用し、単一のアイテムが予想される場合、または何も予想されない場合は、SingleOrDefult()を使用して、一致がない場合にnullも返します。

興味深い質問です。単一のオブジェクトを検出するか、何も検出しないメソッドから何を返しますか?

達成したいことに応じて、さまざまなオプションがあります。

  • アイテムが見つからない場合はnullを返します。利点:これは非常に簡単です。短所:型システムがこのチェックを強制しないため、クライアントはnullをチェックする必要があります。これにより、NullReferenceExceptionsのデバッグが困難になる可能性があります。推奨されません。

  • オブジェクトが見つからない場合は、例外をスローします。利点:シンプルで安全。欠点:オブジェクトが存在しない場合にバグを示す場合にのみ使用する必要があります。

  • オブジェクトが存在するかどうかを確認できる別のメソッドを作成します(例:if (ApplicationExists(id))...)。このメソッドはブール値を返します。利点:クライアント側のコードをクリアします。短所:なんらかのキャッシュを使用しない限り、データベースに2回ヒットするため、コストがかかる可能性があります。また、より多くのコード。

  • TryXXXパターンを使用します。オブジェクトが存在するかどうかを示すブール値を返し、オブジェクト(存在する場合)をoutパラメータに配置します。利点:このパターンは、say Dictionary.TryGetValue()からすでに知られているため、読者が混乱することはありません。短所:C#6では改善されていますが、outパラメーターはやや醜いコードにつながります。

  • Option<T>タイプを返します。これには、クライアントがオブジェクトを明示的にチェックしてアンラップする必要があります。利点:nullを使用するよりもはるかに安全でエレガントです。短所:C#には組み込みのOption<T>型がないため、自分で見つけるか、自分で作成する必要があります。

あなたがしてはいけない唯一のこと:

  • クラスの「空の」インスタンスを返します。
2
JacquesB

何かが見つからないときにnullを返すのが好きです。私にはそれは理にかなっています。

この基準に一致する最初の項目を教えてください。何もありません。ここにnullがあります。コードは、使用しようとすると例外をスローします。

Nullまたは空のオブジェクトをテストしたくない場合の代替手段は、常にオブジェクトのリストを返すことです。

そうすれば、リストのfor eachを実行でき、何も見つからなければ当然何もしません。

デフォルトの動作をオーバーライドする方法はありませんが、null連結を使用できます

var x = Applications.FirstOrDefault() ?? new Application();

Ps。マッピングをwhere句の後に移動します!

0
Ewan