Linq:条件付きでwhere句に条件を追加する


102

このようなクエリがあります

(from u in DataContext.Users
       where u.Division == strUserDiv 
       && u.Age > 18
       && u.Height > strHeightinFeet  
       select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

これらの条件がこのクエリを実行するメソッドに提供されたかどうかに基づいて、年齢、身長などのさまざまな条件を追加したいと思います。すべての条件にはユーザー部門が含まれます。年齢が指定されている場合、それをクエリに追加します。同様に、高さが指定されている場合は、それも追加したいと思います。

SQLクエリを使用してこれを行う場合は、文字列ビルダーを使用して、それらをメインのstrSQLクエリに追加します。しかし、ここLinqでは、同じクエリを3回記述し、各IFブロックに追加の条件があるIF条件の使用のみを考えることができます。これを行うより良い方法はありますか?

回答:


182

呼び出さずにToList()DTOタイプへの最終的なマッピングを行う場合は、必要に応じてWhere句を追加し、最後に結果を作成できます。

var query = from u in DataContext.Users
   where u.Division == strUserDiv 
   && u.Age > 18
   && u.Height > strHeightinFeet
   select u;

if (useAge)
   query = query.Where(u => u.Age > age);

if (useHeight)
   query = query.Where(u => u.Height > strHeightinFeet);

// Build the results at the end
var results = query.Select(u => new DTO_UserMaster
   {
     Prop1 = u.Name,
   }).ToList();

この場合でも、データベースへの呼び出しは1回だけであり、クエリを1つのパスで書き込むのと同じくらい効果的です。


1
「var query = ..」ステートメントにすべてのwhere条件を含める必要がありますか?
user20358

4
後続のWhere条件はORまたはANDとして集約されますか?
Vi100 2015年

4
@ vi100それらは追加のフィルターになるので、さらに
Reed Copsey

シンプルで良かった!上記が非常に読みやすいときに、20行以上のLinqクエリに
うんざりし

このエラーが発生する理由LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
Ali Umair

18

通常、メソッドチェーンを使用しますが、同じ問題があります。そしてここに私が使用する拡張機能があります

public static IQueryable<T> ConditionalWhere<T>(
        this IQueryable<T> source, 
        Func<bool> condition,
        Expression<Func<T, bool>> predicate)
    {
        if (condition())
        {
            return source.Where(predicate);
        }

        return source;
    }

チェーンの破損を防ぐのに役立ちます。また、同じConditionalOrderByConditionalOrderByDescending役に立ちます。


参考になりますが、実際の使用例を追加していただけますか。
Suncat2000 2016年

1
それは次のようになります:var Fruits = await db.Fruits .ConditionalWhere(()=> color!= null、f => f.Color == color).ConditionalWhere(()=> ripe!= null、f => f .Ripe == ripe).ToListAsync();
Yuriy Granovskiy 2016年

4
よく働く!ありがとう!また、条件のオーバーロードを関数ではなく単純なブール値として作成し、デリゲートが不必要な複雑さを追加する場所をより直感的にしました。私はこの拡張メソッドをかなり頻繁に使用しており、あなたのソリューションにとても感謝しています。
Suncat2000 2017

17

1つのオプション。

bool? age = null

(from u in DataContext.Users
           where u.Division == strUserDiv 
           && (age == null || (age != null && u.Age > age.Value))
           && u.Height > strHeightinFeet  
           select new DTO_UserMaster
           {
             Prop1 = u.Name,
           }).ToList();

または、linqのメソッド構文に切り替えて、if条件を使用して式をwhere句に付加することもできます。


3

単に私は私のwhere句でそれを使っています

    public IList<ent_para> getList(ent_para para){
     db.table1.Where(w=>(para.abc!=""?w.para==para.abc:true==true) && (para.xyz!=""?w.xyz==para.xyz:true==true)).ToList();
}

3

特定の条件に基づいてwhere条件を追加します...

from u in DataContext.Users
where u.Division == strUserDiv 
&& u.Age != null ? u.Age > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
 select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

2

同様のことを行うための私のコードは次のとおりです。これは私のWCF SOAP WebサービスAPIのメソッドです。

    public FruitListResponse GetFruits(string color, bool? ripe)
    {
        try
        {
            FruitContext db = new FruitContext();
            var query = db.Fruits.Select(f => f);
            if (color != null)
            {
                query = query.Where(f => f.Color == color);
            }
            if (ripe != null)
            {
                query = query.Where(f => f.Ripe == ripe);
            }
            return new FruitListResponse
            {
                Result = query.Select(f => new Fruit { Id = f.FruitId, Name = f.Name }).ToList()
            };
        }
        catch (Exception e)
        {
            return new FruitListResponse { ErrorMessage = e.Message };
        }
    }

基本クエリはSelect(f => f)基本的にWhereすべてを意味し、句はオプションでそれに付加されます。ファイナルSelectはオプションです。データベース行オブジェクトを結果の「フルーツ」オブジェクトに変換するために使用します。


0

次のパラメータを想定すると、

Int? Age = 18;

条件演算子を使用するだけで&&||別のバージョンを作成できます。

(from u in DataContext.Users
where u.Division == strUserDiv 
    && (Age == null || u.Age > Age)
    && (Param1 == null || u.param1 == Param1)
    && u.Height > strHeightinFeet
select new DTO_UserMaster
{
    Prop1 = u.Name,
}).ToList();

Param1と同様に、検索条件のパラメーターをいくつでも追加できます。


0

私は何か他のものを探してこれに出くわしましたが、ラムダ版を投入すると思いました。

最初に、次のようなクラスを作成して、パラメーターをデータレイヤーに渡します。

   public class SearchParameters() {
       public int? Age {get; set;}
       public string Division {get;set;}
       etc
    }

次に、私のデータレイヤーでは、次のようなものです:

public IQueryable<User> SearchUsers(SearchParameters params) 
{
    var query = Context.Users;
    if (params.Age.HasValue)
    {
         query = query.Where(u => u.Age == params.Age.Value);
    }
    if (!string.IsNullOrEmpty(params.Division)
    {
        query = query.Where(u => u.Division == params.Division);
    }
    etc
    return query;
}

クエリを具体化する場所は自由です。アプリとデータの間に、db固有の表現をdb-agnosticに変換するレイヤーがある場合があります(複数のデータソースをクエリする場合があります)。そのレイヤーは、たとえば、これらのソースから複数のタイプのクエリ可能オブジェクトを取得し、それらを共通のPOCO表現にマップする場合があります。


おっと、ジョン・ヘンケルの答えはわかりませんでした。同じ考え。
Scott Peterson、

0

上記の受け入れられた回答にここで追加するために、結合で動的検索を実行している場合は、最初のlinqクエリで両方のテーブル(t1、t2)を含む新しいオブジェクトを返すことを検討してください。探す。

var query = from t1 in _context.Table1
            join t2 in _context.Table2 on t1.Table1Id equals t2.Table1IdId
            select new { t1, t2 };

        if (!string.IsNullOrEmpty(searchProperty1))
        {
            query = query.Where(collection => collection.t1.TableColumn == searchProperty1);
        }
        if (!string.IsNullOrEmpty(searchProperty2))
        {
            query = query.Where(collection => collection.t2.TableColumn == searchProperty2);
        }
        ....etc.

2つのテーブルを結合し、いずれかのテーブルの特定の列をクエリすることに関して、私がここで探していた答えを得ました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.