2011-08-09 36 views
5

我正在使用表達式構建一個圍繞Web服務調用的API,以允許開發人員指定查詢並讓ExpressionVisitor將Expression轉換爲查詢字符串。請求是包含查詢字符串的特定元素的XML。如何將表達式編譯爲實際結果?

例如,我可以做這樣的事情,這將與銀行1或銀行2的銀行名稱檢索所有支票賬戶:

「BANKNAME =‘銀行1’或BANKNAME =‘銀行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 = '銀行1'或銀行名稱='銀行2'「

這並不難。事情開始分解的時候我介紹的局部變量:


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

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

我知道如何處理一個簡單的局部變量(即字符串BANKNAME =「銀行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; 
    } 
} 

請讓我知道如果你喜歡的任何詳細....

感謝

+0

是你的過濾器總是在Entity.Property左邊的二進制操作? – MerickOWA

+0

他們將是屬性或字段。 – devlife

+0

我看到你接受了我對你另一個問題的回答,我的答案已經足夠清楚你的QueryFilter類了嗎? – MerickOWA

回答

-1

如果您有興趣使用開放源代碼第三方庫來爲您做到這一點,您可以檢出Expression Tree Serialization。我相信它符合你的要求。

+0

我相信它沒有。 – svick

+0

看到序列化程序將如何將任何LINQ表達式轉換爲XElement,然後可以通過線將其轉換爲xml字符串,這似乎正是操作系統正在尋找的操作。 – Mranz

+0

我讀這個問題的方式,他想把表達式轉換成一個非常具體的形式(例如'bankname ='Bank 1'或bankname =' Bank 2'「'),而不是一些XML – svick