Linq to Entities-SQLの「IN」句


230

T-SQLでは、次のようなクエリを使用できます。

SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")

LINQ to Entitiesクエリでそれをどのように複製しますか?可能ですか?

回答:


349

あなたはそれについて考えている方法の観点からそれを頭に向ける必要があります。「in」を実行して、事前定義された一連の適用可能なユーザー権限で現在のアイテムのユーザー権限を見つける代わりに、事前定義された一連のユーザー権限に現在のアイテムの適用可能な値が含まれている場合は、それを要求します。これは、.NETの通常のリストでアイテムを見つける方法とまったく同じです。

LINQを使用してこれを行う方法は2つあり、1つはクエリ構文を使用し、もう1つはメソッド構文を使用します。基本的に、これらは同じであり、好みに応じて互換的に使用できます。

クエリ構文:

var selected = from u in users
               where new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights)
               select u

foreach(user u in selected)
{
    //Do your stuff on each selected user;
}

メソッド構文:

var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights));

foreach(user u in selected)
{
    //Do stuff on each selected user;
}

このインスタンスでの個人的な好みは、変数を割り当てる代わりに、次のように匿名呼び出しよりもforeachを実行できるため、メソッド構文になる可能性があります。

foreach(User u in users.Where(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

構文的にはこれはより複雑に見え、何が起こっているのかを実際に理解するにはラムダ式またはデリゲートの概念を理解する必要がありますが、ご覧のとおり、これによりコードがかなり凝縮されます。

それはすべてあなたのコーディングスタイルと好みに帰着します-私の3つの例はすべて同じことをわずかに異なって行います。

別の方法ではLINQも使用せず、「where」を「FindAll」で置き換える同じメソッド構文を使用して同じ結果を得ることができます。これは.NET 2.0でも機能します。

foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

多分私は答えとしてマークするのが速すぎたのですが、。 "Contains after the {" Admin "、" User "、" Limited "}を取得できません。
StevenMcD 2009年

1
私の名前「FailBoy」に忠実に私はそれを理解しました:PIをstring []に入れて使用し、それが機能しました。ありがとう!
StevenMcD 2009年

申し訳ありませんが、無名配列を新しくするのを忘れていました;)私はコード例を修正しました。あなたが自分でそれを理解したのはうれしいです。
BenAlabaster、2009年

28
Linq-to-SQLまたはLinq全般についての質問であれば、この答えは正しかったでしょう。ただし、「Linq-to-Entities」と具体的に記載されているため、この答えは正しくありません。array.Containsは(まだ)Linq-to-Entitiesでサポートされていません。
KristoferA

6
@KristoferA-それはEFの以前のバージョンに当てはまる可能性がありますが、EF4では私には問題ないようです。
Drew Noakes、

21

これで十分でしょう。2つのコレクションを比較し、1つのコレクションの値が他のコレクションの値と一致するかどうかを確認します

fea_Features.Where(s => selectedFeatures.Contains(s.feaId))

9

VS2008 / .net 3.5を使用している場合は、Alex Jamesのヒント#8を参照してください。 http://blogs.msdn.com/alexj/archive/2009/03/26/tip-8-writing-where-in-style -queries-using-linq-to-entities.aspx

それ以外の場合は、array.Contains(someEntity.Member)メソッドを使用してください。


リンクのみの回答の使用は避けてください。将来リンクが切れた場合に備えて、リンクの内容を回答に要約する必要があります。


8

このコンテキストでは、内部結合に行きます。containsを使用した場合、一致が1つしかないにもかかわらず、6回反復されます。

var desiredNames = new[] { "Pankaj", "Garg" }; 

var people = new[]  
{  
    new { FirstName="Pankaj", Surname="Garg" },  
    new { FirstName="Marc", Surname="Gravell" },  
    new { FirstName="Jeff", Surname="Atwood" }  
}; 

var records = (from p in people join filtered in desiredNames on p.FirstName equals filtered  select p.FirstName).ToList(); 

包含の欠点

2つのリストオブジェクトがあるとします。

List 1      List 2
  1           12
  2            7
  3            8
  4           98
  5            9
  6           10
  7            6

Containsを使用すると、リスト2の各リスト1アイテムが検索されます。つまり、反復が49回発生します!!!


5
これは、ステートメントがSQLに変換されるという事実を完全に無視します。こちらをご覧ください
Gert Arnold

5

これは、LINQ拡張メソッドを直接使用して、in句を確認するための可能な方法である可能性があります

var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();

2

また、SQL-INのようなものを操作しようとしました- エンティティデータモデルに対するクエリ。私のアプローチは、大きなOR式を作成する文字列ビルダーです。それはひどく醜いですが、今それが唯一の方法だと思います。

さて、それはこのようになります:

Queue<Guid> productIds = new Queue<Guid>(Products.Select(p => p.Key));
if(productIds.Count > 0)
{
    StringBuilder sb = new StringBuilder();
    sb.AppendFormat("{0}.ProductId = Guid\'{1}\'", entities.Products.Name, productIds.Dequeue());
    while(productIds.Count > 0)
    {
        sb.AppendFormat(" OR {0}.ProductId = Guid\'{1}\'",
          entities.Products.Name, productIds.Dequeue());
    }
}

このコンテキストでのGUIDの操作:上記のように、クエリ文字列フラグメントでは、GUID自体の前に常に「GUID」という単語があります。これを追加しないとObjectQuery<T>.Where、次の例外がスローされます。

引数の型 'Edm.Guid'および 'Edm.String'は、この操作と互換性がありません。ほぼ等しい式、6行目、14列目。

これはMSDNフォーラムで見つかりました。覚えておくと役に立ちます。

マティアス

...次のバージョンの.NETおよびEntity Frameworkを楽しみにしています。:)


2

BenAlabasterの答えの代替方法

まず、次のようにクエリを書き直すことができます。

var matches = from Users in people
        where Users.User_Rights == "Admin" ||
              Users.User_Rights == "Users" || 
              Users.User_Rights == "Limited"
        select Users;

確かに、これはより「冗長」で書くのは面倒ですが、まったく同じように機能します。

したがって、この種のLINQ式を簡単に作成できるユーティリティメソッドがあれば、ビジネスに携わることになります。

ユーティリティメソッドを使用すると、次のように記述できます。

var matches = ctx.People.Where(
        BuildOrExpression<People, string>(
           p => p.User_Rights, names
        )
);

これにより、以下と同じ効果を持つ式が作成されます。

var matches = from p in ctx.People
        where names.Contains(p.User_Rights)
        select p;

しかし、これは.NET 3.5 SP1に対して実際に機能します。

これを可能にする配管機能は次のとおりです。

public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
        Expression<Func<TElement, TValue>> valueSelector, 
        IEnumerable<TValue> values
    )
{     
    if (null == valueSelector) 
        throw new ArgumentNullException("valueSelector");

    if (null == values)
        throw new ArgumentNullException("values");  

    ParameterExpression p = valueSelector.Parameters.Single();

    if (!values.Any())   
        return e => false;

    var equals = values.Select(value =>
        (Expression)Expression.Equal(
             valueSelector.Body,
             Expression.Constant(
                 value,
                 typeof(TValue)
             )
        )
    );
   var body = equals.Aggregate<Expression>(
            (accumulate, equal) => Expression.Or(accumulate, equal)
    ); 

   return Expression.Lambda<Func<TElement, bool>>(body, p);
}

私はこのメソッドを説明するつもりはありませんが、本質的には、valueSelector(p => p.User_Rights)を使用してすべての値の述語式を構築し、それらの述語をORして完全な式を作成します述語

出典:http : //blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx


0

実際の例:

var trackList = Model.TrackingHistory.GroupBy(x => x.ShipmentStatusId).Select(x => x.Last()).Reverse();
List<int> done_step1 = new List<int>() {2,3,4,5,6,7,8,9,10,11,14,18,21,22,23,24,25,26 };
bool isExists = trackList.Where(x => done_step1.Contains(x.ShipmentStatusId.Value)).FirstOrDefault() != null;

-14

マジ?あなたは人々が使ったことがない

where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)

9
-1 1000行を超えるテーブルで20以上の値を使用してこれを試してください。受け入れられたソリューションの利点がすぐにわかります。また、任意の数の条件をwhereステートメントに追加することは簡単ではありません(ユーザーがオプション1と2を含めるように選択し、3は含めない場合など)。
2013

まあ、私はマッドサイエンティストのものを必要としませんでした。ANDと2つのORS変数SamplePoints =(_db.tblPWS_WSF_SPID_ISN_Lookup.OrderBy(x => x.WSFStateCodeのcから)が必要だったため、この答えは投票に行きました。 PWS == id &&((c.WSFStateCode.Substring(0、2)== "SR")||(c.WSFStateCode.Substring(0、2)== "CH"))select c).ToList() ;
JustJohn

@Trisped-行数(1000)は何も変更しません-または何か不足していますか?
ティムタム

@Tymskiはい、行数は重要です。より多くの行、より多くの計算。可能な値の数と同じ:Checks = NumValues * NumRows。これはM * Nタイプの計算であるため、どちらかが小さければ、必要な各チェックを実行する時間も短くなります。制約を追加して、cjm30305が彼のソリューションが貧弱である理由を示すテスト環境のセットアップ方法を認識できるようにしました。
16

@Trisped where new[] { 1, 2, 3 }.Contains(x)比較が少ないということwhere (x == 1 || x == 2 || x == 3)ですか?
ティムタム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.