确定MemberExpressions目标的范围

时间:2009-01-19 02:37:34

标签: c# .net linq expression-trees custom-linq-providers

这里有没有人有编写自定义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,这当然不起作用。

我想做的是检查c.CustomerID上的MemberExpression,并尝试找出它是一个局部变量,还是只是用作Linq表达式的一部分。

我已经设法将其作为查询的第二遍,查找SQL Server无法绑定的字段,并注入其值,但如果可能的话,我想让它全部发生在同一时间。我试着查看表达式Type属性和IsAutoClass,但这只是猜测,因为它包含单词Auto。它不起作用:))

3 个答案:

答案 0 :(得分:1)

好吧,我不知道你是否可以把它减少到一遍,但是你可以获得有关该成员的信息,如果它与另一个你声明属于查询的变量一致(在这种情况下“ o“),你用它来生成你的查询。

否则,您会认为它是常量,然后将该值插入。

不幸的是,因为您可以在查询的多个位置使用语句(除了let语句之外),因此您似乎只能在一次传递中执行此操作,因为您需要知道所有查询变量在前面。

答案 1 :(得分:1)

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

因此,除非有人提出更好的答案,否则我可能会继续这样做。

答案 2 :(得分:1)

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

如果比较与行有关,则它将具有(作为祖先)ParameterExpression;如果它是局部变量,它将具有(作为祖先)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;
    }    
}