如何将表达式编译为实际结果?

发布于 2024-11-29 05:50:31 字数 5209 浏览 0 评论 0原文

我正在使用表达式围绕 Web 服务调用构建一个 API,以允许开发人员指定查询并让 ExpressionVisitor 将表达式转换为查询字符串。该请求是 XML,具有包含查询字符串的特定元素。

例如,我可以执行类似这样的操作,它将检索银行名称为 Bank 1 或 Bank 2 的所有支票帐户:

“bankname = 'Bank 1' 或bankname = 'Bank 2'”

Web 服务可以处理更加复杂的查询但我现在就坚持这个。

所以我有一个类 CheckingAccount:


[IntacctObject("checkingaccount")]
public class CheckingAccount : Entity
{
    [IntacctElement("bankaccountid")]
    public string Id { get; set; }

    [IntacctElement("bankname")]
    public string BankName { get; set; }
}

和一个 ExpressionVisitor,其主要功能是将这样的表达式转换

Expression> expression = ca => ca.BankName == "Bank 1" || ca.BankName == "Bank 2"

为查询:“bankname = 'Bank 1' 或bankname = 'Bank 2'”

这并不难。当我引入局部变量时,事情开始崩溃:


var account = new CheckingAccount { BankName = "Bank 1" };
string bankName = "Bank 2";

Expression> expression = ca => ca.BankName == account.BankName || ca.BankName == bankName;

我知道如何处理一个简单的局部变量(即字符串bankName =“Bank 2”),但处理另一种类型(var account = new CheckingAccount { BankName = “Bank 1”}) 要复杂得多。

归根结底,这些都是我现在需要弄清楚如何处理的大问题。我知道还有更复杂的场景,但我目前不太关心这些。

这是我的表达式访问者(请注意方法 CreateFilter 的通用约束):


internal class IntacctWebService30ExpressionVisitor : ExpressionVisitor
{
    private readonly List _Filters = new List();
    private IntacctWebServicev30SimpleQueryFilter _CurrentSimpleFilter;
    private IntacctWebServicev30ComplexQueryFilter _CurrentComplexFilter;
    private MemberExpression _CurrentMemberExpression;

    public string CreateFilter(Expression> expression) where TEntity : Entity
    {

        Visit(expression);

        string filter = string.Join(string.Empty, _Filters.Select(f => f.ToString()).ToArray());
        return filter;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        switch (node.NodeType)
        {
            case ExpressionType.AndAlso:
            case ExpressionType.OrElse:
                _CurrentComplexFilter = new IntacctWebServicev30ComplexQueryFilter { ExpressionType = node.NodeType };
                break;
            case ExpressionType.Equal:
            case ExpressionType.GreaterThan:
            case ExpressionType.GreaterThanOrEqual:
            case ExpressionType.LessThan:
            case ExpressionType.LessThanOrEqual:
            case ExpressionType.NotEqual:
                _CurrentSimpleFilter = new IntacctWebServicev30SimpleQueryFilter { ExpressionType = node.NodeType };
                break;
        }

        return base.VisitBinary(node);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        var attr = node.Member.GetAttribute();
        if (attr != null)
            _CurrentSimpleFilter.FieldName = attr.FieldName;
        else
            _CurrentMemberExpression = node;

        return base.VisitMember(node);
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        object value = Expression.Lambda>(node).Compile().Invoke();

        string fieldValue = extraxtFieldValue(value, node);

        _CurrentSimpleFilter.FieldValue = fieldValue;

        if (_CurrentComplexFilter != null)
        {
            if (_CurrentComplexFilter.Left == null)
            {
                _CurrentComplexFilter.Left = _CurrentSimpleFilter;
            }
            else if (_CurrentComplexFilter.Right == null)
            {
                _CurrentComplexFilter.Right = _CurrentSimpleFilter;
                _Filters.Add(_CurrentComplexFilter);
            }
            else
                throw new InvalidOperationException();
        }
        else
        {
            _Filters.Add(_CurrentSimpleFilter);
        }

        return base.VisitConstant(node);
    }

    private string extraxtFieldValue(object value)
    {
        string fieldValue;
        if (value is DateTime)
            fieldValue = ((DateTime)value).ToString("yyyy-MM-dd");
        else if (value is string)
            fieldValue = value.ToString();
        else if (value.GetType().IsEnum)
        {
            throw new NotImplementedException();
        }
        else
        {
            // Not sure if this is the best way to do this or not but can't figure out how
            // else to get a variable value.

            // If we are here then we are dealing with a property, field, or variable.
            // This means we must extract the value from the object.
            // In order to do this we will rely on _CurrentMemberExpression
            if (_CurrentMemberExpression.Member.MemberType == MemberTypes.Property)
            {
                fieldValue = value.GetType().GetProperty(_CurrentMemberExpression.Member.Name).GetValue(value, null).ToString();
            }
            else if (_CurrentMemberExpression.Member.MemberType == MemberTypes.Field)
            {
                fieldValue = value.GetType().GetFields().First().GetValue(value).ToString();
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

        return fieldValue;
    }
}

如果您需要更多详细信息,请告诉我......

谢谢

I am building an API around a web service call using Expressions to allow a developer to specify a query and have an ExpressionVisitor convert the Expression into the query string. The request is XML with a particular element containing a query string.

For example, I can do something like this which will retrieve all checking accounts with a bank name of Bank 1 or Bank 2:

"bankname = 'Bank 1' or bankname = 'Bank 2'"

The web service can handle significantly more complex queries but I'll just stick with this for now.

So I have a class CheckingAccount:


[IntacctObject("checkingaccount")]
public class CheckingAccount : Entity
{
    [IntacctElement("bankaccountid")]
    public string Id { get; set; }

    [IntacctElement("bankname")]
    public string BankName { get; set; }
}

And an ExpressionVisitor whose primary function is to convert an expression like this:

Expression> expression = ca => ca.BankName == "Bank 1" || ca.BankName == "Bank 2"

into the query: "bankname = 'Bank 1' or bankname = 'Bank 2'"

This isn't so tough. Where things start to break down are when I introduce local variables:


var account = new CheckingAccount { BankName = "Bank 1" };
string bankName = "Bank 2";

Expression> expression = ca => ca.BankName == account.BankName || ca.BankName == bankName;

I know how to deal with a simple local variable (ie. string bankName = "Bank 2") but dealing with a the other type (var account = new CheckingAccount { BankName = "Bank 1" }) is much more complex.

At the end of the day these are the big issues that I need to figure out how to deal with right now. I know there are much more complex scenarios but I'm not so concerned with those at the moment.

Here is my expression visitor (please note the generic constraint on method CreateFilter):


internal class IntacctWebService30ExpressionVisitor : ExpressionVisitor
{
    private readonly List _Filters = new List();
    private IntacctWebServicev30SimpleQueryFilter _CurrentSimpleFilter;
    private IntacctWebServicev30ComplexQueryFilter _CurrentComplexFilter;
    private MemberExpression _CurrentMemberExpression;

    public string CreateFilter(Expression> expression) where TEntity : Entity
    {

        Visit(expression);

        string filter = string.Join(string.Empty, _Filters.Select(f => f.ToString()).ToArray());
        return filter;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        switch (node.NodeType)
        {
            case ExpressionType.AndAlso:
            case ExpressionType.OrElse:
                _CurrentComplexFilter = new IntacctWebServicev30ComplexQueryFilter { ExpressionType = node.NodeType };
                break;
            case ExpressionType.Equal:
            case ExpressionType.GreaterThan:
            case ExpressionType.GreaterThanOrEqual:
            case ExpressionType.LessThan:
            case ExpressionType.LessThanOrEqual:
            case ExpressionType.NotEqual:
                _CurrentSimpleFilter = new IntacctWebServicev30SimpleQueryFilter { ExpressionType = node.NodeType };
                break;
        }

        return base.VisitBinary(node);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        var attr = node.Member.GetAttribute();
        if (attr != null)
            _CurrentSimpleFilter.FieldName = attr.FieldName;
        else
            _CurrentMemberExpression = node;

        return base.VisitMember(node);
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        object value = Expression.Lambda>(node).Compile().Invoke();

        string fieldValue = extraxtFieldValue(value, node);

        _CurrentSimpleFilter.FieldValue = fieldValue;

        if (_CurrentComplexFilter != null)
        {
            if (_CurrentComplexFilter.Left == null)
            {
                _CurrentComplexFilter.Left = _CurrentSimpleFilter;
            }
            else if (_CurrentComplexFilter.Right == null)
            {
                _CurrentComplexFilter.Right = _CurrentSimpleFilter;
                _Filters.Add(_CurrentComplexFilter);
            }
            else
                throw new InvalidOperationException();
        }
        else
        {
            _Filters.Add(_CurrentSimpleFilter);
        }

        return base.VisitConstant(node);
    }

    private string extraxtFieldValue(object value)
    {
        string fieldValue;
        if (value is DateTime)
            fieldValue = ((DateTime)value).ToString("yyyy-MM-dd");
        else if (value is string)
            fieldValue = value.ToString();
        else if (value.GetType().IsEnum)
        {
            throw new NotImplementedException();
        }
        else
        {
            // Not sure if this is the best way to do this or not but can't figure out how
            // else to get a variable value.

            // If we are here then we are dealing with a property, field, or variable.
            // This means we must extract the value from the object.
            // In order to do this we will rely on _CurrentMemberExpression
            if (_CurrentMemberExpression.Member.MemberType == MemberTypes.Property)
            {
                fieldValue = value.GetType().GetProperty(_CurrentMemberExpression.Member.Name).GetValue(value, null).ToString();
            }
            else if (_CurrentMemberExpression.Member.MemberType == MemberTypes.Field)
            {
                fieldValue = value.GetType().GetFields().First().GetValue(value).ToString();
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

        return fieldValue;
    }
}

Please let me know if you'd like any more detail....

Thanks

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

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

发布评论

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

评论(2

£噩梦荏苒 2024-12-06 05:50:31

看一下 马特·沃伦 (Matt Warren) 发表的关于这个问题的文章。他提供了一个完全执行此操作的类的源代码,并解释了它是如何做到的。它也包含在他的 IQToolkit 中。

Have a look at an article about this very issue from Matt Warren. He provides source for a class that does exactly this, with an explanation how does it do it. It's also included in his IQToolkit.

酒浓于脸红 2024-12-06 05:50:31

如果您有兴趣使用开源第三方库来为您执行此操作,您可以查看表达式树序列化。我相信它能满足您的需求。

If you are interested in using a open source third party library to do this for you, you can check out Expression Tree Serialization. I believe it does what you are looking for.

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