确定 MemberExpressions 目标的范围

发布于 2024-07-11 17:50:09 字数 635 浏览 6 评论 0 原文

这里有人有编写自定义 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。 但它不起作用:)

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 :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

神魇的王 2024-07-18 17:50:09

好吧,我不知道您是否可以将其减少到一次,但是您可以获得有关该成员的信息,以及它是否与您声明为查询一部分的另一个变量一致(在本例中为“o”) ,您可以使用它来生成查询。

否则,您会假设它是一个常量,然后插入该值。

不幸的是,因为您可以在查询中的多个位置使用 from 语句(除了 let 语句之外),所以您似乎无法做到这一点只需一次即可完成,因为您需要预先了解所有查询变量。

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.

奢欲 2024-07-18 17:50:09

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

因此,除非有人想出更好的答案,否则这可能就是我所要做的在。

Okay, after some quick statistical analysis (ie, comparing individual properties manually), DeclaringType, ReflectedType and Namespace are when Lambda parameter is not in scope" fires.

So unless someone comes up with a better answer, that might be all I have to go on.

乱了心跳 2024-07-18 17:50:09

在Where表达式中,您正在查看Expression> - 这意味着最外层的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;
    }    
}

In a Where expression, you are looking at an Expression<Func<T,bool>> - which means that the outermost lambda should have a single ParameterExpression with type T.

If a comparison relates to the row, it will have (as an ancestor) this ParameterExpression; if it is local variable, it will have (as an ancestor) a ConstantExpression - however, the type of this constant expression will be compiler generated to cope with all of the captured variables used in the expression.

Like so:

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;
    }    
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文