2

カスタム Linq プロバイダーを作成した経験のある人はいますか?

私がやろうとしているのは、ビジネス オブジェクトのプロパティである MemberExpression を SQL に含める必要があるか、それとも定数として扱う必要があるかを判断することです。これは、たまたまビジネス オブジェクトであるローカル変数からのものであるためです。

たとえば、これがある場合:

Customer c = LoadCustomerFromDatabase();

var orders = from o in db.Orders() where o.CustomerID == c.CustomerID select o;

現時点では、クエリ トランスレータは を実行しようとしますがSELECT * FROM orders o where o.CustomerID = c.CustomerID、もちろん動作しません。

私がやりたいのは、上のMemberExpressionを調べて、c.CustomerIDそれがローカル変数なのか、それともLinq式の一部として使用されているものなのかを調べることです.

SQL Serverがバインドできないフィールドを探し、代わりにそれらの値を注入するクエリの2回目のパスとしてそれを行うことができましたが、可能であればすべてを同時に実行したいと思います. 式のTypeプロパティ、 andを調べてみましたIsAutoClassが、 Auto という単語が含まれていたため、推測に過ぎませんでした。そして、それはうまくいきませんでした:)

4

3 に答える 3

1

Where式では、Expression<Func<T,bool>>-を見ています。これは、最も外側のラムダがParameterExpressionタイプがシングルである必要があることを意味しTます。

比較が行に関連している場合、(祖先として)これがParameterExpressionあります。ローカル変数の場合、(祖先として)aConstantExpressionがありますが、この定数式の型は、式で使用されるキャプチャされたすべての変数を処理するためにコンパイラによって生成されます。

そのようです:

using System;
using System.Linq.Expressions;
class Foo
{
    public string Name { get; set; }
    static void Main()
    {
        var exp = (LambdaExpression) GetExpression();
        WalkTree(0, exp.Body, exp.Parameters[0]);

    }
    static void WriteLine(int offset, string message)
    {
        Console.WriteLine(new string('>',offset) + message);
    }
    static void WalkTree(int offset, Expression current,
        ParameterExpression param)
    {
        WriteLine(offset, "Node: " + current.NodeType.ToString());
        switch (current.NodeType)
        {
            case ExpressionType.Constant:
                WriteLine(offset, "Constant (non-db)"
                    + current.Type.FullName);
                break;
            case ExpressionType.Parameter:
                if (!ReferenceEquals(param, current))
                {
                    throw new InvalidOperationException(
                        "Unexpected parameter: " + param.Name);
                }
                WriteLine(offset, "db row: " + param.Name);
                break;
            case ExpressionType.Equal:
                BinaryExpression be = (BinaryExpression)current;
                WriteLine(offset, "Left:");
                WalkTree(offset + 1, be.Left, param);
                WriteLine(offset, "Right:");
                WalkTree(offset + 1, be.Right, param);
                break;
            case ExpressionType.MemberAccess:
                MemberExpression me = (MemberExpression)current;
                WriteLine(offset, "Member: " + me.Member.Name);
                WalkTree(offset + 1, me.Expression, param);
                break;
            default:
                throw new NotSupportedException(
                    current.NodeType.ToString());
        }
    }

    static Expression<Func<Foo, bool>> GetExpression()
    {
        Foo foo = new Foo { Name = "abc" };

        return row => row.Name == foo.Name;
    }    
}
于 2009-01-19T10:06:34.433 に答える
1

さて、いくつかの簡単な統計分析 (つまり、個々のプロパティを手動で比較する) の後、DeclaringType、ReflectedType、および Namespace は、Lambda パラメータがスコープ内にない場合に発生します。

したがって、誰かがより良い答えを思いつかない限り、私が続けなければならないのはそれだけかもしれません。

于 2009-01-19T04:03:21.703 に答える
1

うーん、1回のパスに切り詰められるかどうかはわかりませんが、メンバーに関する情報を取得でき、それがクエリの一部として宣言した別の変数と一致する場合 (この場合は「o」) 、それを使用してクエリを生成します。

それ以外の場合は、それが定数であると想定し、その値をプラグインします。

残念ながら、(let ステートメントに加えて) from ステートメントをクエリ内の複数の場所で使用できるため、すべてのクエリ変数を前もって知る必要があるため、1 回のパスで実行できるようには見えません。 .

于 2009-01-19T02:47:44.023 に答える