2011-08-09 8 views
5

Estoy creando una API en torno a una llamada de servicio web utilizando Expresiones para permitir que un desarrollador especifique una consulta y que un ExpressionVisitor convierta la Expresión en la cadena de consulta. La solicitud es XML con un elemento particular que contiene una cadena de consulta.¿Cómo compilar una expresión hasta el resultado real?

Por ejemplo, puedo hacer algo como esto que recuperará todas las cuentas corrientes con un nombre de banco de banco 1 o el Banco 2:

"bankname = 'Banco 1' o bankname = 'Banco 2'"

El servicio web puede manejar consultas significativamente más complejas, pero me quedaré con esto por ahora.

así que tengo una CuentaCorriente clase:


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

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

Y un ExpressionVisitor cuya función principal es la de convertir una expresión como la siguiente:

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

en la consulta: "bankname = 'Banco 1' o bankname = 'Banco 2' "

Esto no es tan difícil. Donde las cosas empiezan a romper son cuando me presento variables locales:


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

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

sé cómo tratar con una simple variable local (es decir, cadena bankname = "Banco 2".) Pero en relación con un otro tipo (var account = new CheckingAccount {BankName = "Bank 1"}) es mucho más complejo.

Al final del día, estos son los grandes problemas que tengo que resolver en este momento. Sé que hay escenarios mucho más complejos, pero no estoy tan preocupado por esos en este momento.

Aquí es mi expresión de visitantes (tenga en cuenta la restricción genérica sobre createFilter método):


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; 
    } 
} 

Por favor, hágamelo saber si desea más detalles ....

Gracias

+0

¿sus filtros serán siempre Entity.Property para el lado izquierdo de la operación binaria? – MerickOWA

+0

Serán propiedades o campos. – devlife

+0

Veo que aceptó mi respuesta a su otra pregunta. ¿Mi respuesta fue lo suficientemente clara como para integrar las clases de QueryFilter? – MerickOWA

Respuesta

-1

Si está interesado en utilizar una biblioteca de código abierto de terceros para hacer esto por usted, puede consultar Expression Tree Serialization. Creo que hace lo que estás buscando.

+0

Creo que no. – svick

+0

Al ver cómo el serializador convertirá cualquier expresión LINQ en un XElement, que luego se puede convertir en una cadena XML a través del cable, esto parece ser exactamente lo que el operador está buscando hacer. podría agradecer lo que falta, estaría muy agradecido. – Mranz

+0

La forma en que leo la pregunta, quiere convertir la expresión en una forma muy específica (el ejemplo es '" bankname = 'Bank 1' o bankname = ' Banco 2 '"'), no XML. – svick

Cuestiones relacionadas