Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to compile an Expression down to the actual result?
    text
    copied!<p>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.</p> <p>For example, I can do something like this which will retrieve all checking accounts with a bank name of Bank 1 or Bank 2: </p> <p>"bankname = 'Bank 1' or bankname = 'Bank 2'"</p> <p>The web service can handle significantly more complex queries but I'll just stick with this for now.</p> <p>So I have a class CheckingAccount:</p> <pre><code> [IntacctObject("checkingaccount")] public class CheckingAccount : Entity { [IntacctElement("bankaccountid")] public string Id { get; set; } [IntacctElement("bankname")] public string BankName { get; set; } } </code></pre> <p>And an ExpressionVisitor whose primary function is to convert an expression like this:</p> <pre> Expression> expression = ca => ca.BankName == "Bank 1" || ca.BankName == "Bank 2" </pre> <p>into the query: "bankname = 'Bank 1' or bankname = 'Bank 2'"</p> <p>This isn't so tough. Where things start to break down are when I introduce local variables:</p> <pre><code> var account = new CheckingAccount { BankName = "Bank 1" }; string bankName = "Bank 2"; Expression> expression = ca => ca.BankName == account.BankName || ca.BankName == bankName; </code></pre> <p>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.</p> <p>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.</p> <p>Here is my expression visitor (please note the generic constraint on method CreateFilter):</p> <pre><code> 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; } } </code></pre> <p>Please let me know if you'd like any more detail....</p> <p>Thanks</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload