2011-06-27 6 views
2

linqを使用してC#で汎用の "検索エンジン"を作成しようとしています。私は次のように機能している単純な検索エンジンを持っています。C#でEF 4.1クエリにカラム/プロパティを動的にフィルタリングする

var query = "joh smi"; 
var searchTerms = query.Split(new char[] { ' ' }); 
var numberOfTerms = searchTerms.Length; 

var matches = from p in this.context.People 
       from t in searchTerms 
       where p.FirstName.Contains(t) || 
       p.LastName.Contains(t) 
       group p by p into g 
       where g.Count() == numberOfTerms 
       select g.Key; 

は、私はそれがより一般的になりたいので、私はこのようにそれを呼び出すことができます。

var matches = Search<Person>(dataset, query, p => p.FirstName, p => p.LastName); 

私は、次の限り得ているが、それは、「LINQ式ノードタイプで失敗します「Invoke」はLINQ to Entitiesではサポートされていません。 System.NotSupportedException。

static IEnumerable<T> Find<T>(IQueryable<T> items, string query, 
           params Func<T, string>[] properties) 
{ 
    var terms = query.Split(' '); 
    var numberOfParts = terms.Length; 

    foreach (var prop in properties) 
    { 
     var transformed = items.SelectMany(item => terms, 
       (item, term) => new { term, item }); 

               // crashes due to this method call 
     var filtered = transformed.Where(p => prop(p.item).Contains(p.term)); 
     items = filtered.Select(p => p.item); 
    } 

    return from i in items 
      group i by i into g 
      where g.Count() == numberOfParts 
      select g.Key; 
} 

私はそれだけでExpression<Func<T, bool>>i => i.FirstNameをコンパイルする方法がなければならない、なんとかだが、私のLINQの専門知識が終わるところだ確信しています。誰にもアイデアはありますか?

答えて

1

あなたのような何か、あなたのOrクエリを構築するために述語Builderを使用する必要があります。

var predicate = PredicateBuilder.False<T>(); 
foreach (var prop in properties) 
{ 
    Func<T, string> currentProp = prop; 
    predicate = predicate.Or (p => currentProp(p.item).Contains(p.term)); 
} 
var result = items.Where(predicate); 
+0

)。コンテナ(p.term) – djeebus

0

は、仕様のパターンを使用して調べてください。 this blogをチェックしてください。具体的には、彼が開発した仕様パターンを見てください。これは@Variantと同様の考えで、動的な仕様を作成してコンテキストやリポジトリに渡すことができます。

0

'Expanded'にする必要があるクエリの内容が分かります。私はhereの式を展開するライブラリを使用しました。私はそれがLinqにエンティティにSQLにそれを翻訳することができると思います。 Expandは何度も何度も呼び出されます。私はそれらのすべてが必要だと思います。とにかく、それは動作します。フォローするコード:

using System.Linq.Expressions; 

public static class SearchEngine<T> 
{ 
    class IandT<T> 
    { 
     public string Term { get; set; } 
     public T Item { get; set; } 
    } 

    public static IEnumerable<T> Find(
        IQueryable<T> items, 
        string query, 
        params Expression<Func<T, string>>[] properties) 
    { 
     var terms = query.Split(new char[] { ' ' }, 
           StringSplitOptions.RemoveEmptyEntries); 
     var numberOfParts = terms.Length; 

     Expression<Func<IandT<T>, bool>> falseCond = a => false; 
     Func<Expression<Func<IandT<T>, bool>>, 
      Expression<Func<IandT<T>, bool>>, 
      Expression<Func<IandT<T>, bool>>> combineOr = 
       (f, g) => (b) => f.Expand(b) || g.Expand(b); 

     var criteria = falseCond; 

     foreach (var prop in properties) 
     { 
      var currentprop = prop; 
      Expression<Func<IandT<T>, bool>> current = c => 
        currentprop.Expand(c.Item).IndexOf(c.Term) != -1; 
      criteria = combineOr(criteria.Expand(), current.Expand()); 
     } 

     return from p in items.ToExpandable() 
       from t in terms 
       where criteria.Expand(new IandT<T> { Item = p, Term = t }) 
       group p by p into g 
       where g.Count() == numberOfParts 
       select g.Key; 
    } 
} 

次のコードを経由して呼び出すことができます。これはまだLINQのツーエンティティを表現 P => currentProp(p.itemを拡大することはできません

var matches = Search<Person>(dataset, query, p => p.FirstName, p => p.LastName); 
関連する問題