如何编译一个表达式到实际的结果呢? [英] How to compile an Expression down to the actual result?
问题描述
我使用表达式允许开发人员指定的查询,并有ExpressionVisitor转换成表达式查询字符串构建一个API围绕Web服务调用。该请求是XML包含查询字符串的特定元素。
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.
例如,我可以做这样的事情,将与银行1的银行名称检索所有支票账户或银行2:
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 ='银行1'或BANKNAME ='银行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.
所以我有一个类的CheckingAccount:
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; }
}
和一个ExpressionVisitor,其主要功能是将一个表达式转换这样的:
And an ExpressionVisitor whose primary function is to convert an expression like this:
Expression> expression = ca => ca.BankName == "Bank 1" || ca.BankName == "Bank 2"
进入查询:BANKNAME ='银行1'或BANKNAME ='银行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;
我知道如何处理一个简单的局部变量(即字符串BANKNAME =银行2)但处理一个另一种类型(无功帐户=新的CheckingAccount {BANKNAME =银行1})复杂得多。
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.
下面是我的表达访问者(请注意,方法CreateFilter通用约束):
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....
感谢
推荐答案
看一看的有关从马特·沃伦就此问题文章。他为一个类,正是这一点,有一个解释,它是如何做到这一点提供了来源。它也包含在他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.
这篇关于如何编译一个表达式到实际的结果呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!