2011-02-10 8 views
14

レコードタイプのレコードを検索するユーザーがいます。彼らはテキストボックスに検索タームを入力し、いくつかのフィールドを検索タームと照合してレコードを検索します。LinqからSQLへのwhere句の再利用方法

私のクエリは次のようになります。私はすべてが同じフィルタを使用するので、私はそれを再利用できるようにフィルタリングを抽出したいクエリの数を持っている

var results = from record in DataContext.Records 
       where 
        record.Field1.ToLower().Contains(term) || 
        record.Field2.ToLower().Contains(term) || 
        record.Field3.ToLower().Contains(term) 
       select record; 

。ような何か:

var filter = new Func<Record, string, bool>(
       (record, term) => 
        record.Field1.ToLower().Contains(term) || 
        record.Field2.ToLower().Contains(term) || 
        record.Field3.ToLower().Contains(term) 
      ); 

var results = from record in DataContext.Records 
       where filter(record, term) 
       select record; 

しかし、それは動作しない理由は次のとおりです。

メソッド 'System.ObjectのDynamicInvoke(System.Objectの[])は、' SQLへのサポートの翻訳を持っていません。

どのようにwhere条件をクエリ間で再利用できますか?

答えて

12

CompiledQueryを使用してください。

var filter = CompiledQuery.Compile(
    (DatabaseDataContext dc, Record record, string term) => 
     record.Field1.ToLower().Contains(term) || 
     record.Field2.ToLower().Contains(term) || 
     record.Field3.ToLower().Contains(term) 
); 

var results = from record in DataContext.Records 
       where filter(DataContext, record, term) 
       select record; 

詳細については、How to: Store and Reuse Queriesを参照してください。

+0

これは書かれているようには機能しません--CQ.CompileはFuncを生成するので、LINQ to SQLクエリのWhere節では使用できません。 DataContext.Recordsのrから、var query = CompiledQuery.Compile((string term)=>)のようなものは必要ありません。r.Field1.ToLower()。Contains(term)select r); '' var results =クエリ( "someTerm"); '?私は間違っている可能性がありますので、CompiledQueryを使用していない! – itowlson

+0

@itowlson:うまくいきます。これは、LINQ to SQLで使用できる再利用可能な関数を作成する方法です。クエリプロバイダは、コンパイルされた関数を認識し、必要に応じてクエリを生成します。単に完全なクエリを生成するのではなく、クエリの一部を生成することもできます(条件付きなど)。 –

+1

これは期待どおりに動作します。唯一の奇妙なことは、私はLinq Toオブジェクトクエリでそのフィルタを再利用することです、そして、私はまだLinq To Sql DataContextをパラメータとして渡す必要があります。 DataContextとは無関係にクエリを定義する方法はありますか? –

1

Expression<Func<Record, bool>>にする必要があると思います。それ以外の場合は、実際のC#メソッド呼び出しをその説明ではなくSQLに変換しようとしています。これは、バージョンが動作する保証ではありません。私はどの文字列関数がSQLに翻訳可能であるのかよく分かりません。 C#コンパイラのコンパイルを行います -

Expression<Func<Record, bool>> filter = 
    record => record.Field1.ToLower().Contains(term); // rest omitted 

ラムダ式は同じままですが、あなたがタイプExpression<Func<Record, bool>>の変数にそれを返す必要があります:あなたは代わりに、関数の式を作成する必要が

12

デリゲートの代わりに式として使用し、LINQ to SQLに渡すことができます。場合:編集、追加し

var results = DataContext.Records.Where(filter); 

:あなたはどこに拡張メソッドを使用する必要があります:

ただし、C#の-syntax where句で表現変数を使用することはできませんあなたが別の条件でフィルタを作成することができるようにしたい、あなただけの用語から表現を生成するための方法が必要になります。

private static Expression<Func<Record, bool>> Filter(string term) 
{ 
    return r => r.Field1.ToLower().Contains(term); 
} 

var results = DataContext.Records.Where(Filter(term)); 

をあなたが持っているように、現時点ではラムダとしてfilterを維持することを好む場合は、することができますそうするが、ジェネリックスビットは、ネストされた取得:

Func<string, Expression<Func<Record, bool>>> filter = 
    term => (r => r.Field1.ToLower().Contains(term)); 

var results = DataContext.Records.Where(filter(term)); 

にかかわらず、重要なことは、中に何が起こるのか句がExpression<Func<Record, bool>>なければならない場合ということです - しかし、表現をすることができます上に示したように、適切な表現を構築することによりtermに依存します飛ぶ。あなたがWhere句でフィルタの長さを綴っていれば、LINQ to SQLとまったく同じことになります。

+0

where句のC#構文はコンパイラの砂糖です。同じコードを生成します。 –

+0

はい、フィルタがFuncではなくExpression型の場合、 'filter(record)'構文をサポートしません。 「フィルタは変数ですが、メソッドのように使用されます」というエラーが表示されます。そして、 'where filter'と書くだけで、暗黙的に型式(...)をboolに変換することはできません。 – itowlson

+0

ありがとう - それは意味をなさない。しかし、私が与えてくれた(間違った)例では、「用語」はラムダ式の閉包であり、それをパラメータにするために必要なフィルタを再利用するため、私はそれを取り除くことができませんでした。これで、私のフィルタは、 'Expression >'がある 'Expression >'になりました。これはまだ実行可能ですか? –

2

他の人が指摘したExpression<Func<Record, bool>>の問題に加えて、PredicateBuilderを調べることをお勧めします。これはラムダ式を動的に組み合わせるのに非常に有効です。