2016-07-03 5 views
1

まず、EntityFrameworkデータベースを使用するREST WebAPIがあります。 EDMXファイル、エンティティ、リポジトリクラス、APIコントローラなどからコードが生成されます。Linq PredicateBuilderを使用して文字列を連結してテキスト検索を行います

ユーザーはクエリ文字列を使用して条件を追加できるようになりました。LinqKit PredicateBuilder/Linq db 。

e.g. /api/Users?FirstName_contains=Rog 

これは、User.FirstNameメンバーの「Rog」を持つすべてのユーザーを返します。これは、PredicateBuilderを使用して、適切なという式を動的に構築し、Whereという句としてDbSetに対して使用します。例えば

:今

var fieldName = "FirstName"; 
var value = "Rog"; 

var stringContainsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 

var parameter = Expression.Parameter(typeof(User), "m"); 
var fieldAccess = Expression.PropertyOrField(parameter, fieldName); 
var fieldType = typeof(User).GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public).PropertyType; 

var expression = Expression.Lambda<Func<User, bool>>(Expression.Call(fieldAccess, stringContainsMethod, Expression.Constant(value, fieldType)) 
    , parameter) 

var andPredicate = PredicateBuilder.True<User>(); 
andPredicate = andPredicate.And(expression); 

var query = Db.Users 
    .AsQueryable() 
    .AsExpandable() 
    .Where(andPredicate); 

問題。私は、クライアントがメンバーの構成に基づいて結果を一致させることができるようにしたい。

e.g. /api/Users?api_search[FirstName,LastName]=Rog 

すなわち「Rogメール」の試合のためにfirst name + last nameを検索するので、私は「ロジャーのSm」を検索し、最初の名前=ロジャーと姓= Smithの結果を得ることができます。

私はそれは次のようになり流暢使っDbSetを照会した場合:私は、文字列のメンバーを動的FirstName + " " + LastNameの連結を処理するpredicate/linq式を作成しているに苦しんでいますどのような

users.Where(u => (u.FirstName + " " + u.LastName).Contains("Rog")); 

+0

t-sqlにCONCAT関数があるにもかかわらず、私はEF(特にL2SQL)機能がこの変換をサポートしているとは思わない。すべてのエントリを最初に.ToList()でプルし、次にメモリ内の選択を行う必要があります。 – DevilSuichiro

+0

本当にね?私の旅行からは、上記の流暢な例が、 'WHERE FirstName + '+ LastName LIKE'%Rog% ''のT-SQLに変換されることは間違いありません。 –

+0

どうして 'u => u.FirstName.Contains(query)|| u.LastName.Contains(query) '? – haim770

答えて

1

PredicateBuilderが本当にここに必要とされていません。

文字列の連結式は、EFによってサポートされているstring.Concatメソッド呼び出しを使用して生成することができます。

static Expression<Func<T, string>> GenerateConcat<T>(IEnumerable<string> propertyNames) 
{ 
    var parameter = Expression.Parameter(typeof(T), "e"); 
    // string.Concat(params string[] values) 
    var separator = Expression.Constant(" "); 
    var concatArgs = Expression.NewArrayInit(typeof(string), propertyNames 
     .SelectMany(name => new Expression[] { separator, Expression.PropertyOrField(parameter, name) }) 
     .Skip(1)); 
    var concatCall = Expression.Call(typeof(string).GetMethod("Concat", new[] { typeof(string[]) }), concatArgs); 
    return Expression.Lambda<Func<T, string>>(concatCall, parameter); 
} 

文字列が述語は、単純なstring.Containsメソッド呼び出しによって生成することができます含まれています

static Expression<Func<T, bool>> GenerateContains<T>(Expression<Func<T, string>> member, string value) 
{ 
    var containsCall = Expression.Call(member.Body, "Contains", Type.EmptyTypes, Expression.Constant(value)); 
    return Expression.Lambda<Func<T, bool>>(containsCall, member.Parameters); 
} 

それらを組み合わせますあなたの例とともに:

var predicate = GenerateContains(GenerateConcat<User>(new[] { "FirstName", "LastName" }), "Rog"); 
+0

ありがとうIvan - 素晴らしい。私は受け入れられた答えを変更しました。はい、あなたは正しいのですが、質問は実際には適切なラムダ式を作成することでした。 PredicateBuilderは、コレクションに対する実行のために私のいろいろな表現の連鎖を処理するだけです。 –

0

は(私はデータベースに対してそれをテストしていない)、次の試してみてください。

public class User 
{ 
public string FirstName { get; set; } 
public string LastName { get; set;} 
} 
void Main() 
{ 
    List<User> users = new List<User> { 
          new User { FirstName = "john", LastName = "smith" }, 
          new User { FirstName = "siler", LastName = "johnston" } }; 
    string searchName = "ja smi"; 
    String[] terms = searchName.Split(' '); 
    var items = users.Where(x => terms.Any(y => x.FirstName.Contains(y)) 
           || terms.Any(y => x.LastName.Contains(y))); 
} 
+0

ありがとうございます。条件を分割して、Any/Orベースの検索を行うことは、連結を達成できない場合、間違いなく崩壊です。私はそれについて考えているので、十分な結果が得られるかどうかを試してみるかもしれません。 –

+0

私はこれを前向きに受け入れましたが、技術的には既に機能は冗長であっても可能でした。 '/ api/Users?or_firstName_contains = ja&or_firstName_contains = smi&or_lastName_contains = ja&or_lastName_contains = smi'です。私はそれが可能でなければならないことを知っています。なぜなら、Linqはこれを通常の流暢な構文やクエリ構文を使って処理しているからです。 –

関連する問題