2

Is there anyone here with experience writing custom Linq providers?

What I'm trying to do is tell whether a MemberExpression that is a property on a business object should be included in the SQL, or treated as a constant, because its from a local variable that just happens to be a business object.

So for example, if you have this:

Customer c = LoadCustomerFromDatabase();

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

At the moment, my query translator will try to execute SELECT * FROM orders o where o.CustomerID = c.CustomerID, which of course doesn't work.

What I would like to do is examine the MemberExpression on the c.CustomerID and try to work out if its a local variable, or just something that is being used as part of the Linq expression.

I have managed to do it as a 2nd pass over the query, looking for fields that SQL Server won't be able to bind, and injecting their values instead, but if possible I'd like to get it all happening at the same time. I tried looking at the expression Type property, and IsAutoClass, but that was just a guess because it contained the word Auto. And it didn't work :)

4

3 回答 3

1

在 Where 表达式中,您正在查看Expression<Func<T,bool>>- 这意味着最外层的 lambda 应该有一个ParameterExpression带有 type的单个T

如果比较与该行有关,它将具有(作为祖先) this ParameterExpression; 如果它是局部变量,它将具有(作为祖先)a ConstantExpression- 但是,此常量表达式的类型将由编译器生成,以处理表达式中使用的所有捕获变量。

像这样:

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

Well, I don't know if you can cut it down to one pass, but you can get information on the member, and if it coincides with another variable you have declared as being part of the query (in this case "o"), you use it to generate your query.

Otherwise, you would assume it is a constant, and then plug that value in.

Unfortunately, because you can have from statements (in addition to the let statement) in multiple places in the query, it doesn't seem like you can do it in just one pass, since you need to know all of the query variables up front.

于 2009-01-19T02:47:44.023 回答
1

好的,经过一些快速的统计分析(即手动比较各个属性),DeclaringType、ReflectedType 和 Namespace 是 Lambda 参数不在范围内时触发的。

因此,除非有人提出更好的答案,否则我可能只需要继续下去。

于 2009-01-19T04:03:21.703 回答