2011-08-09 5 views
5

Buduję interfejs API wokół wywołania usługi internetowej za pomocą wyrażeń, aby umożliwić programistom określenie zapytania i wyrażenie ExpressionVisitor przekształcić wyrażenie w łańcuch zapytania. Żądanie to XML z określonym elementem zawierającym ciąg zapytania.Jak skompilować wyrażenie do rzeczywistego wyniku?

Na przykład, mogę zrobić coś takiego, które będzie pobierać wszystkie rachunki sprawdzanie z nazwą banku banku 1 lub banku 2:

"BANKNAME = 'Bank 1' lub BANKNAME = 'Bank 2'"

Usługa internetowa może obsługiwać znacznie bardziej skomplikowane zapytania, ale na razie pozostanę z tym.

Więc mam klasy CheckingAccount:


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

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

i ExpressionVisitor, którego głównym zadaniem jest konwertowanie wyrażenie takiego:

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

do zapytania: „BANKNAME = 'banku 1' lub nazwa banku = "Bank 2" "

To nie jest takie trudne. Gdzie wszystko zaczyna się załamać się kiedy wprowadzać zmienne lokalne:


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

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

wiem, jak radzić sobie z prostym zmiennej lokalnej (czyli ciąg BANKNAME = „Bank 2”.), Ale do czynienia z innego rodzaju (var account = new CheckingAccount {BankName = "Bank 1"}) jest znacznie bardziej złożony.

Pod koniec dnia są to wielkie problemy, które muszę wymyślić, jak sobie z tym poradzić. Wiem, że są o wiele bardziej skomplikowane scenariusze, ale w tej chwili nie interesuję się nimi.

Oto mój gość wyrażenie (proszę zwrócić uwagę na ogólną presję na metodzie 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; 
    } 
} 

Proszę dać mi znać, jeśli chcesz już więcej szczegółów ....

Dzięki

+0

Czy Twoje filtry zawsze będą Entity.Property dla lewej strony op binarnej? – MerickOWA

+0

Będą to właściwości lub pola. – devlife

+0

Widzę, że przyjąłeś moją odpowiedź na inne pytanie, czy moja odpowiedź była wystarczająco jasna, abyś mógł zintegrować swoje klasy QueryFilter? – MerickOWA

Odpowiedz

-1

Osoby zainteresowane korzystaniem z biblioteki stron trzecich, aby to zrobić, mogą pobrać numer Expression Tree Serialization. Wierzę, że robi to, czego szukasz.

+0

Uważam, że tak nie jest: – svick

+0

Widząc, jak serializer zamieni dowolne wyrażenie LINQ na XElement, które następnie może zostać przekształcone w ciąg xml przez cały przewód, wydaje się, że dokładnie to, co chce zrobić operacja. możesz uprzejmie wskazać, czego brakuje, byłbym bardzo zobowiązany – Mranz

+0

Sposób, w jaki czytam pytanie, chce przekonwertować wyrażenie na bardzo konkretną formę (przykładem jest "nazwa banku =" Bank 1 "lub nazwa banku =" Bank 2 '"'), a nie jakiś XML. – svick

Powiązane problemy