Как разложить выражение предиката в запрос? - PullRequest
5 голосов
/ 22 сентября 2011

У меня есть следующий класс Person, с пользовательским Where методом:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public string Where(Expression<Func<Person, bool>> predicate)
    {
        return String.Empty;
    }
}

Как я могу получить имена и значения параметров Expression перечислимым образом?

Person p = new Person();
p.Where(i => i.Name == "Shlomi" && i.Age == 26);

Для построения строкового запроса с параметрами, прикрепленными в соответствии с именем и значением выражения.

// Eventually I will convert the above into the following:
string Query = "select * from person where name = @Name AND Age = @Age";

SqlParameter[] param = new SqlParameter[] { 
    new SqlParameter("@Name","Shlomi"),
    new SqlParameter("@Age","26")
};

Ответы [ 3 ]

9 голосов
/ 23 сентября 2011

Я, конечно, думаю, вы должны последовать совету StriplingWarrior и использовать LINQ to Entities или LINQ to SQL, но ради переизобретения колеса (плохо) я построю предыдущий ответмоего .

// Start with a method that takes a predicate and retrieves the property names
static IEnumerable<string> GetColumnNames<T>(Expression<Func<T,bool>> predicate)
{
    // Use Expression.Body to gather the necessary details
    var members = GetMemberExpressions(predicate.Body);
    if (!members.Any())
    {
        throw new ArgumentException(
            "Not reducible to a Member Access", 
            "predicate");
    }

    return members.Select(m => m.Member.Name);
}

Теперь вам нужно пройтись по дереву выражений, посетив каждое выражение-кандидат, и определить, содержит ли оно MemberExpression.Приведенный ниже метод GetMemberExpressions будет обходить дерево выражений и извлекать каждый из MemberExpression s, найденных в:

static IEnumerable<MemberExpression> GetMemberExpressions(Expression body)
{
    // A Queue preserves left to right reading order of expressions in the tree
    var candidates = new Queue<Expression>(new[] { body });
    while (candidates.Count > 0)
    {
        var expr = candidates.Dequeue();
        if (expr is MemberExpression)
        {
            yield return ((MemberExpression)expr);
        }
        else if (expr is UnaryExpression)
        {
            candidates.Enqueue(((UnaryExpression)expr).Operand);
        }
        else if (expr is BinaryExpression)
        {
            var binary = expr as BinaryExpression;
            candidates.Enqueue(binary.Left);
            candidates.Enqueue(binary.Right);
        }
        else if (expr is MethodCallExpression)
        {
            var method = expr as MethodCallExpression;
            foreach (var argument in method.Arguments)
            {
                candidates.Enqueue(argument);
            }
        }
        else if (expr is LambdaExpression)
        {
            candidates.Enqueue(((LambdaExpression)expr).Body);
        }
    }
}
2 голосов
/ 23 сентября 2011

Проблема в том, что выражение на самом деле является деревом.

Например, у вас есть следующий предикат:

Expression<Func<Person, bool>> expr = x => x.Name == "Shlomi" && x.Age == 26;

Если вы проверите expr, вы увидите, что это телоимеет «AndAlso» NodeType, а также имеет два свойства для операндов - Left и Right, каждый из которых указывает на другой Expression объект с «Равным» NodeType, который в свою очередь снова имеет два свойства Left иRight, которые указывают на Expression типа MemberAccess и Constant соответственно.

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

1 голос
/ 23 сентября 2011

То, что вы хотите сделать, очень сложно, и для этого созданы целые фреймворки, поэтому вам не нужно писать логику самостоятельно.Взгляните на LINQ to Entities и LINQ to SQL, например

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...