2012-03-30 10 views
1

私は奇妙だと思ったLINQ文を作成できました。他の誰かがそれを経験しているかどうかを見たいと思っていました。LINQから同じクエリ内のメソッドにクエリデータを渡す

私はこれにそれを単純化してきました:

var x = db.Test 
    .Where(a => a.Field1 == Utils.CreateHash(Preferences.getValue(a.Field2))) 
    .FirstOrDefault(); 

今どのようにこれは、データベース・コードに変換しますか? LINQは、行のために、すなわち、すべての単一の行のための二重のクエリを実行する必要はありません。

 
1) Query a.Field2 
2) Return value to run Utils.CreateHash(Preferences.getValue(a.Field2)) 
3) Take that value from step 2 and compare it against a.Field1 
4) Repeat 1-3 until I've gone through all the rows or returned a matching row 

これは非常に非効率的ではないでしょうか?または、LINQはこれをより良い方法で実行するのに十分なほどスマートですか?実際にこのコードを実行していないので、別の可能性がランタイムエラーであることに注意してください。なぜLINQは紛争を検出し、それをコンパイルできないほど賢明ではないでしょうか?

+0

あなたのクエリは、あなたが 'Utils 'を呼び出しているのと同じように動作するとは思えません。CreateHash'をDBで実行しようとしています – BrokenGlass

+0

@BrokenGlass私は実際にそれを実行していませんが、なぜそれがコンパイルされますか? Linqは機能がデータベース上にないことを知るのに十分スマートではありませんか? – onit

+0

いいえ、Linq IQuerableプロバイダは実行時に失敗し、ラムダ構文は有効で、プロバイダにはないLinq with Objectsと一緒に実行されます(実行時にクエリをDBクエリに変換します) – BrokenGlass

答えて

2

DB上で実行しようとしているラムダのUtils.CreateHashへの呼び出しがあるため、そのままのクエリは機能しません。そのコンテキストではDB側に同等のものがないためそのメソッドを実行できません。クエリは失敗します。

一般的に、メソッドやクラスなどのメモリ構造体にアクセスするためのプロバイダ(LinqからSQL、Linq to Entitiesなど)の能力は、ほとんどの場合、プリミティブ値またはコレクションにアクセスする際の経験則として非常に制限されていますプリミティブの機能が有効になります。この作品は、(私は同意する極端なケースを書きますが、最高:)またはカスタム(オープンソース)LINQプロバイダのソースコードを経ることであろう方法を知っている

+0

興味深いですが、a.Field2の代わりに既に定義された値を渡すとクエリが機能します。実際には、それはしばらくの間そのように働いています。つまり、DB側を実行する前に関数を評価できない場合、DB側で同等のものを見つけて、それが存在しなければ実行時エラーをスローします。 – onit

+0

@onit:はい、正確に - 前後に行くことはありません - 一方通行の通りとして見てください。あなたはあらかじめ計算された値を渡し、DB上のすべての作業を行い、 'AsEnumerable()'を使ってメモリ内の後処理を行うことができます。 DBのクエリの途中でコードに戻る列 – BrokenGlass

+0

あなたは正しいです。私はそれを実行しようとし、私はNotSupportedExceptionを得た。情報をありがとう。 LinqがDB側でどのようにストアドプロシージャを検索するかについての参考文献はありますか?または、なぜそれが有効でないことを認識できないのか。あなたがその大丈夫でなければ、私は少し具体的になっていることを実感します。 – onit

0

LINQプロバイダは、あなたのラムダ式は、各返される行のための2つの値を使用して実行し、両方のフィールドを選択し、単一のSELECTクエリを実行します。

+0

これは私の質問に答える。それはすべての行の2つのフィールドを返しますか?または、行単位でクエリを実行し、ラムダ式を評価する必要がありますか?それが行単位でそれを行うなら、それは私にとって非常に非効率的であるようです。次に、2つのフィールドから一致するものが見つかった場合、その行のすべてのフィールドについて再度dbにクエリを実行する必要はありませんか? – onit

+0

@onit:私が答えて言ったように、それは単一のSELECTを実行し、一致が見つかるまでDataReaderの行を繰り返します。もちろん、これはLINQプロバイダに依存します。 – SLaks

1

だけ速く追加する...
良い例(例えば、http://relinq.codeplex.com/には1つなどがあります)。
LINQプロバイダは基本的に(ここでは少し簡略化しています)、Db(サポートされているSQL、関数)にしか 'マップ'できません。
つまり、それ以外のフレームワークでは定数やその他に変換されないカスタムメソッドで動作する標準的なセットがあります.Db/SQLで解決する方法はありません側'。
など。あなたの 'カスタム' linqプロバイダ(ここではそうではありません)では、特定の内線番号を追加することができます。 .MyCalc() - これは適切に解決され、SQLに相当するものに変換されます。そして、それを使用することができます。
それ以外は正しいと思いますが、プロバイダーはそれをDb 'fetch'、クエリ操作から戻ったときに解決する式として残します。また、場合によってはそれについて文句を言います。
LinqはIQueryableに基づいています。サポートされている同等のSQLに対応している拡張メソッドをご覧ください。
これは役に立ちます
EDIT:物事が「仕事」かどうかは問題ではありません.Dbのコンテキストで実行することを意味するわけではありません。つまり、ほとんどの場合、パフォーマンスが賢明ではありません。 IQueryableは式で動作します(そしてインターフェイスを見れば) - linqは、通常は呼び出したり列挙したりするときに実行されます。その時点で、式の中には、SQLに組み込むことができるconst値に評価されるかもしれませんが、あなたの場合はそうではありません。
テストする最良の方法は、クエリによって生成されたSQLをテストバックすることです(おそらくこれは私がTranslate LINQ to sql statementと思う)。

関連する問題