2

次のコード行を検討してください。

private void DoThis() {
    int i = 5;
    var repo = new ReportsRepository<RptCriteriaHint>();

    // This does NOT work
    var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();      

    // This DOES work
    var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();    
}

したがって、実際の数値をラムダ関数にハードワイヤすると、正常に機能します。キャプチャされた変数を式に使用すると、次のエラーが返されます。

オブジェクト タイプ ReportBuilder.Reporter+<>c__DisplayClass0 から既知のマネージ プロバイダ ネイティブ タイプへのマッピングは存在しません。

なんで?どうすれば修正できますか?

4

1 に答える 1

9

技術的には、これを修正する正しい方法は、ラムダから式ツリーを受け入れてi参照を評価するフレームワークです。つまり、特定のフレームワークに対する LINQ フレームワークの制限です。現在実行しようとしているのはi、データベースから既知の型 (プロバイダー) へのメンバー アクセスとして解釈することです。ラムダ変数キャプチャの仕組みにより、iローカル変数は実際には、プロバイダーが認識しない、変な名前の隠しクラスのフィールドになります。

つまり、フレームワークの問題です。

本当にやり遂げなければならない場合は、次のように式を手動で作成できます。

ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
    Expression.Lambda<Func<RptCriteriaHint,bool>>(
        Expression.Equal(
            Expression.MakeMemberAccess(
                x,
                typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
            Expression.Constant(i)),
        x)).ToList();

...しかし、それは単なるマゾヒズムです。

このエントリに対するあなたのコメントは、私にさらに説明するように促します.

ラムダは、正しい署名を持つデリゲート、または正しい署名のデリゲートの 2 つのタイプのいずれかに変換できExpression<TDelegate>ます。外部データベースへの LINQ (あらゆる種類のメモリ内クエリとは対照的に) は、2 番目の種類の変換を使用して機能します。

コンパイラは、大まかに言えば、次の方法でラムダ式を式ツリーに変換します。

  1. 構文ツリーはコンパイラによって解析されます。これはすべてのコードで発生します。
  2. 構文ツリーは、変数のキャプチャを考慮して書き直されています。変数のキャプチャは、通常のデリゲートまたはラムダと同様です。したがって、表示クラスが作成され、キャプチャされたローカルがそれらに移動されます (これは、C# 2.0 匿名デリゲートでの変数キャプチャと同じ動作です)。
  3. 新しい構文ツリーはクラスへの一連の呼び出しに変換されるExpressionため、実行時に、解析されたテキストを忠実に表すオブジェクト ツリーが作成されます。

外部データ ソースへの LINQ は、この式ツリーを取得してそのセマンティック コンテンツを解釈し、ツリー内のシンボリック式を、そのコンテキストに固有のもの (DB 内の列など) を参照するものとして解釈するか、変換する即時値として解釈することになっています。通常、System.Reflection は、この変換をガイドするフレームワーク固有の属性を探すために使用されます。

ただし、サブソニックは、ドメイン固有の対応を見つけることができないシンボリック参照を適切に処理していないようです。シンボリック参照を評価するのではなく、単なるパントです。したがって、これはサブソニックの問題です。

于 2008-12-04T02:04:24.637 に答える