Как составить выражение до фактического результата? - PullRequest
5 голосов
/ 09 августа 2011

Я строю API на основе вызова веб-службы с использованием выражений, чтобы позволить разработчику указать запрос и заставить ExpressionVisitor преобразовать выражение в строку запроса.Запрос представляет собой XML с конкретным элементом, содержащим строку запроса.

Например, я могу сделать что-то подобное, что будет извлекать все текущие счета с именем банка 1 или 2 банка:

"bankname =" Bank 1 "или bankname =" Bank 2 ""

Веб-сервис может обрабатывать значительно более сложные запросы, но сейчас я просто буду придерживаться этого.

Так что яиметь класс 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 = 'Bank 1' или bankname= "Банк 2" "

Это не так сложно.Когда все начинает ломаться, я начинаю вводить локальные переменные:


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

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

Я знаю, как обращаться с простой локальной переменной (т. Е. String bankName = "Bank 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;
    }
}

Пожалуйста, дайте мне знать, если вы хотите больше подробностей ....

Спасибо

Ответы [ 2 ]

0 голосов
/ 26 августа 2011

Посмотрите на статью об этой самой проблеме от Мэтта Уоррена .Он предоставляет источник для класса, который делает именно это, с объяснением, как он это делает.Он также включен в его IQToolkit .

0 голосов
/ 26 августа 2011

Если вы хотите использовать стороннюю библиотеку с открытым исходным кодом для этой цели, вы можете проверить Сериализация дерева выражений . Я считаю, что он делает то, что вы ищете.

...