web-dev-qa-db-ja.com

LINQ:値がnullでない場合にのみwhere句を追加

私は典型的な方法が次のようであることを知っています:

IQueryable query = from staff in dataContext.Staffs;
if(name1 != null)
{
     query = from staff in query where (staff.name == name1);
}

しかし、他の開発者から引き継いだプログラムから、次のようなコードを見ました。

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);

これが通常のSQLステートメントである場合、2番目のステートメントは悪い習慣であると断言できます。 name1がnullの場合、クエリに無意味なwhere句を追加するためです。

しかし、私はLINQが初めてなので、LINQが異なるかどうかわかりませんか?

22
Henry

あなたはそれを次のように書くことができます

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 != null && staff.name == name1);

この方法では、最初の条件がfalseと評価された場合、条件の2番目の部分は評価されません

更新:
書く場合

IQueryable query = from staff in dataContext.Staffs;
    query = from staff in query where (name1 == null || staff.name == name1);

またはname1がnullの場合、条件の2番目の部分は評価されません。

plzこれを参照してください link 詳細

18

多くの場合、この種のことは、クエリ構文ではなく、流syntaxな構文を使用して作成する方がスムーズです。

例えば.

_IQueryable query = dataContext.Staffs;
if(name1 != null)
{
     query = query.Where(x => x.name == name1);
}
_

したがって、_name1_がnullの場合は、Where()呼び出しを行わないだけです。複数の異なるフィルターがあり、そのすべてが必要な場合とそうでない場合があり、おそらくさまざまな異なる並べ替え順序がある場合、これははるかに管理しやすくなります。

alexの編集: OK、値がnullでない場合にのみwhere句を追加するという質問に答えていました。質問の他の部分に応えて、私はEntity Framework 4でこれを試して、LINQが生成したSQLを確認しました。これを行うには、queryObjectQueryにキャストし、.ToTraceString()を呼び出します。その結果、WHERE句は次のようになりました。

_WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1
_

したがって、はい、それは古典的な悪いSQLです。name列にインデックスがある場合、それが使用されることを期待しないでください。

編集#2: Entity FrameworkではなくLINQ to SQLを使用してこれを再試行し、結果はかなり異なりました。今回は、_name1_がnullのクエリを試行すると、希望どおりWHERE句がまったくありません。 _name1_を「a」にして試してみると、単純な_WHERE [t0].[name] = @p0_と_@p0_が「a」として送信されました。 Entity Frameworkはnotを最適化するようです。それは少し心配です。

11
Carson63000

これを行う最良の方法は、条件文とwhere式を受け取る拡張メソッドを作成することです。条件が真の場合、where式を使用します。それ以外の場合は使用しません。これによりコードが劇的にクリーンアップされ、ifステートメントが不要になります。

public static class LinqExtensions
{
    public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
    {
        if (condition)
        {
            return query.Where(whereClause);
        }
        return query;
    }
}

これで、次のようにコードを記述できます。

IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
4
Soto

LINQは他のいくつかの原因(この原因ではない)で異なります。LINQは、可能な限り小さなコードと明確なタラで「より高速に」データを取得する方法です。LINQには多くの利点があります。

  1. データをオブジェクトに簡単に変換できます。 「Impedence Mismatch」という用語が頻繁に使用されていることを聞いたことがあると思います。つまり、LINQは、オブジェクト指向コードと、階層、フラットファイル、メッセージなどのデータパラダイムとの間の変換に必要な作業量を削減し、リレーショナルなど。ネイティブ形式でデータについて推論する必要があるため、「Impedence Mismatch」は排除されませんが、ここからそこへのブリッジ(IMO)ははるかに短くなります。

  2. すべてのデータに共通の構文。クエリ構文を学習したら、任意のLINQプロバイダーで使用できます。これは長年にわたってデータアクセステクノロジーで成長してきたバベルの塔よりもはるかに優れた開発パラダイムだと思います。もちろん、各LINQプロバイダーには必要な固有のニュアンスがありますが、基本的なアプローチとクエリ構文は同じです。

  3. 強く型付けされたコード。 C#(またはVB.NET)クエリ構文は言語の一部であり、プロバイダーが理解できるものに変換されるC#型でコーディングします。これは、コンパイラーが開発ライフサイクルのどこよりも早くエラーを検出する生産性を獲得することを意味します。確かに、ストアドプロシージャ構文の多くのエラーは保存時にエラーを生成しますが、LINQはSQL Serverより一般的です。クエリは文字列またはその他の緩やかに型付けされたメカニズムで形成されるため、ランタイムエラーを生成する他のすべての種類のデータソースを考慮する必要があります。

  4. プロバイダー統合。データソースをまとめるのはとても簡単です。たとえば、非常に高度なシナリオでは、LINQ to Objects、LINQ to SQL、およびLINQ to XMLを一緒に使用できます。とてもエレガントだと思います。

  5. 仕事の削減。 LINQの前は、DALの作成に多くの時間を費やしていましたが、現在はDataContextがDALです。私もOPFを使用しましたが、ボックス内の複数のプロバイダーおよび他の多くのサードパーティプロバイダーに同梱されているLINQがあり、以前のポイントからメリットを得ることができます。 LINQ to SQL DataContextを1分でセットアップできます(コンピューターとIDEが追いつくことができる限り)。

  6. 一般的な場合のパフォーマンスは問題になりません。 SQL Serverは、ストアドプロシージャと同様に、最近ではクエリを非常によく最適化します。もちろん、パフォーマンス上の理由からストアドプロシージャが必要な場合もあります。たとえば、トランザクション内で追加のロジックを使用してテーブル間で複数の相互作用があった場合、ストアドプロシージャを使用する方が賢明です。分散トランザクションにDTCを関与させることに加えて、コードで同じタスクを実行しようとする通信オーバーヘッドにより、ストアドプロシージャの選択がより魅力的になりました。ただし、単一のステートメントで実行されるクエリの場合、ストアドプロシージャからわずかなパフォーマンスの向上があったとしても、以前のポイント(IMO)の利点はより重要であるため、LINQが好ましい選択です。

  7. 組み込みのセキュリティ。 LINQの前にストアドプロシージャを好んだ理由の1つは、パラメータの使用を強制し、SQLインジェクション攻撃を減らすことでした。 LINQ to SQLはすでに入力をパラメーター化していますが、これも同様に安全です。

  8. LINQは宣言型です。 LINQ to XMLまたはLINQ to SQLの操作には多くの注意が払われますが、LINQ to Objectsは非常に強力です。 LINQ to Objectsの典型的な例は、string []からアイテムを読み取ることです。ただし、これはほんの一例です。毎日作業するすべてのIEnumerableコレクション(IEnumerableを照会することもできます)について考える場合、機会は豊富です。つまり、選択したアイテムのASP.NET ListBoxコントロールを検索する、2つのコレクションで(Unionなどの)セット操作を実行する、またはリストを反復処理して各アイテムのForEachでラムダを実行する。本質的に宣言的なLINQで考え始めると、現在使用している命令型の手法よりも多くのタスクがよりシンプルで直感的になることがわかります。

私はおそらく続けることができますが、私はそこでやめたほうがいいでしょう。これにより、LINQを使用して生産性を高め、より広い観点から有用なテクノロジと見なす方法について、より前向きな見解が得られることを願っています。

1
Jacob

そこで、ここにリストされている.Where(..., x => ...)拡張メソッドを回答として試しましたが、Linq To EntitiesはそれをTSQLに変換する方法を知らないため、Entity Frameworkに対しては機能しません。

だからここに私のFuncを取得する私のソリューションがあります:

Expression<Func<SomeEfPoco, bool>> columnBeingFilteredPredicate = x => true; // Default expression to just say yes
if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue))
{
    columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue;
}

_context.SomeEfPocos.Where(x => ..... &&
            ..... &&
            ..... &&)
.Where(columnBeingFilteredPredicate);

私の場合、someColumnBeingFilteredValueは、カプセル化メソッドの文字列パラメーターであり、既定値はNULLです。

1
Stephen York

標準のSQLでこのパターンを見たことがありますが、NULLの可能性のあるパラメーターがいくつかあると便利です。例えば:

SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName )
                       AND ( @LastName IS NULL OR LastName = @LastName )

LINQでこれを見ると、彼らは古いSQLクエリを盲目的に翻訳しただけである可能性があります。

1
Danko Durbić

Expressionの使用が好きです。

    Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true;

    if (DateBirth.HasValue)
                {
                    Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth);
                    expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate);
                }

IQueryable query = dataContext.Persons;
 query = query.Where(expresionFinal);
1
J4ime

EF Coreの場合、次のように分割しました。

IQueryable<Partners> recs = contextApi.Partners;
if (status != -1)
{
   recs = recs.Where(i => i.Status == status);
}
recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName);
foreach (var rec in recs)
{
}

varに頼るのではなく、入力を明示的にする必要がありました。

0
Chad Kuehn

いいえ、私はあなたに強く同意しません。ここでは、単純なロジックを与えました

if(name1 != null)
// do your stuff

しかし、null値を持つname1で何か違うことをするとどうなりますか。さて、この状況を考えてみましょう。この例では、ソースコレクションでnull値の可能性を処理する方法を示します。 IEnumerable<T>などのオブジェクトコレクションには、値がnullの要素を含めることができます。ソースコレクションがnullまたは値がnullの要素を含み、クエリがnull値を処理しない場合、クエリを実行するとNullReferenceExceptionがスローされます。

おそらくこれが問題になる可能性があります...

0
PawanS