Построение выражений запросов с универсальными типами - PullRequest
0 голосов
/ 08 мая 2018

Мне нужна помощь в завершении этого запроса. Мне нужно использовать выражения, чтобы я мог обрабатывать запрос динамически. Другими словами, пользователь создает параметры запроса, которые вставляются в это. Мне нужна помощь конкретно с родовыми типами, указанными в строках Type yourType = typeof(YourGeneric);, & ParameterExpression pe = Expression.Parameter(yourType, "x"); & Expression left = Expression.Property(pe, yourType.GetProperty(wheres[i].ColumnName)) .. Полное раскрытие, я делаю это из другой рекомендации и не полностью понимаю дженерики. Пожалуйста, обратитесь к Entity Framework Dynamic Where Clause из списка для получения более подробной информации.

Expression query;
for (int i = 0; i < wheres.Count; i++)
{
    Type yourType = typeof(YourGeneric);
    ParameterExpression pe = Expression.Parameter(yourType, "x");
    Expression left = Expression.Property(pe, yourType.GetProperty(wheres[i].ColumnName));
    Expression right = Expression.Constant(wheres[i].Value, typeof(int));

    Expression result = getOperatorExp(wheres[i].Operator, left, right);
    if (i == 0)
    {
        query = result;
    }
    else
    {
        Expression grammer = getGrammerExp(wheres[i].AndOr, query, result);
        query = grammer;
    }


}

MasterQuery.Where(query);


public Expression getOperatorExp(string Operator, Expression left, Expression right)
{
    Expression exp;
    switch (Operator.ToUpper())
    {
        case "Equals":
            exp = Expression.Equal(left, right);
            break;

        case "NOT EQUALS":
            exp = Expression.NotEqual(left, right);
            break;

        case "LESS THAN":
            exp = Expression.LessThan(left, right);
            break;

        case "LESS THAN OR EQUALS":
            exp = Expression.LessThanOrEqual(left, right);
            break;

        case "GREATER THAN":
            exp = Expression.GreaterThan(left, right);
            break;

        case "GREATER THAN OR EQUALS":
            exp = Expression.GreaterThanOrEqual(left, right);
            break;

        case "ON":
            exp = Expression.Equal(left, right);
            break;

        case "BEFORE":
            exp = Expression.LessThan(left, right);
            break;

        case "ON OR BEFORE":
            exp = Expression.LessThanOrEqual(left, right);
            break;

        case "AFTER":
            exp = Expression.GreaterThan(left, right);
            break;

        case "ON OR AFTER":
            exp = Expression.GreaterThanOrEqual(left, right);
            break;

        default:
            exp = Expression.Equal(left, right);
            break;
    }

    return exp;
}

        public Expression getGrammerExp(string AndOr, Expression left, Expression right)
        {
            Expression exp;
            switch (AndOr.ToUpper())
            {
                case "AND":
                    exp = Expression.And(left, right);
                    break;

                case "OR":
                    exp = Expression.Or(left, right);
                    break;

                case "":
                    exp = Expression.LessThan(left, right);
                    break;

                default:
                    exp = Expression.Equal(left, right);
                    break;
            }

            return exp;
        }

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Вот так будет выглядеть моя реализация.

Тестовый код:

var masterQuery = new[]
{
    new { Name = "Asterix", Weight = 50 },
    new { Name = "Obelix", Weight = 120 },
    new { Name = "Idefix", Weight = 1 }
}.AsQueryable();

var wheres = new[]
{
    new Filtering.WhereParams { ColumnName = "Name", Operator = "Equals", Value = "Asterix" },
    new Filtering.WhereParams { AndOr = "OR", ColumnName = "Name", Operator = "Equals", Value = "Obelix" },
    new Filtering.WhereParams { AndOr = "AND", ColumnName = "Weight", Operator = "LESS THAN", Value = 100 }
};

var filtered = masterQuery.Where(wheres).ToList();
// asterix

А вот и реализация:

public static class Filtering
{
    public class WhereParams
    {
        public string ColumnName { get; set; }
        public object Value { get; set; }
        public string Operator { get; set; }
        public string AndOr { get; set; }
    }

    /// <summary>
    /// Make a predicate from the specified <paramref name="wheres"/>.
    /// </summary>
    public static Expression<Func<T, bool>> ToPredciate<T>(this IEnumerable<WhereParams> wheres)
    {
        using (var e = wheres.GetEnumerator())
        {
            if (!e.MoveNext()) // not filtered
                return x => true;

            var pe = Expression.Parameter(typeof(T), "x");
            var body = GetComparePredicateBody(pe, e.Current); // first condition

            // join body with more conditions
            while (e.MoveNext())
            {
                var right = GetComparePredicateBody(pe, e.Current);
                switch (e.Current.AndOr)
                {
                    case "AND":
                        body = Expression.AndAlso(body, right);
                        break;
                    case "OR":
                        body = Expression.OrElse(body, right);
                        break;
                    default:
                        // LessThan and Equal don't make much sense on booleans, do they?
                        throw new Exception("Bad boolean operator.");
                }
            }

            return Expression.Lambda<Func<T, bool>>(body, pe);
        }
    }

    /// <summary>
    /// Returns a boolean expression x.ColumnName op Value.
    /// </summary>
    private static Expression GetComparePredicateBody(Expression x, WhereParams where)
    {
        var left = Expression.Property(x, where.ColumnName);
        var right = Expression.Constant(where.Value);
        switch (where.Operator)
        {
            case "Equals": return Expression.Equal(left, right);
            case "LESS THAN": return Expression.LessThan(left, right);
            // ...
            default: throw new ArgumentException("Bad comparison operator.");
        }
    }

    public static IQueryable<T> Where<T>(this IQueryable<T> source, IEnumerable<WhereParams> wheres) => source.Where(wheres.ToPredciate<T>());
    public static IEnumerable<T> Where<T>(this IEnumerable<T> source, IEnumerable<WhereParams> wheres) => source.Where(wheres.ToPredciate<T>().Compile());
}
0 голосов
/ 08 мая 2018

YourGeneric должен быть типом объекта, который вы хотите запросить. Например, если в вашем DbContext у вас есть коллекция городов (DbSet Cities), вы должны передать этот тип в качестве универсального типа.

Вы не хотите писать собственный код для каждого типа объекта в вашем контексте. Код, который вы пишете, должен работать как для запроса таблицы Cities, так и для запроса таблицы Fruits. Отсюда - использование дженериков.

Как пример:

ParameterExpression pe = Expression.Parameter(typeof(City), "x");

Создает выражение для лямбда-параметра типа City, который можно использовать для запроса коллекции этого типа. Чтобы сделать код многоразовым, вы можете сделать его универсальным (как указано в вашем вопросе) и что-то вроде этого:

public Expression CreateExpression<TEntity, TConst>(WhereClause singleWhere)
{ 
    Type entityType = typeof(TEntity);
    ParameterExpression pe = Expression.Parameter(entityType, "x");
    Expression left = Expression.Property(pe, 
        entityType.GetProperty(singleWhere.ColumnName));
    Expression right = Expression.Constant(singleWhere.Value, typeof(TConst));

    return getOperatorExp(singleWhere.Operator, left, right);
}

Затем вы можете передавать соответствующие типы в своих вызовах, например так:

Expression result = CreateExpression<City, int>();

Это позволяет вам передавать разные типы сущностей и разные типы констант для запроса при каждом вызове.

Кроме того, это, вероятно, должен быть верхний регистр:

case "Equals":

Было бы хорошо, если бы ваш пример кода был компилируемым до некоторой степени - мне было бы легче дать вам рабочий пример. Деревья выражений в .NET - гораздо более сложная тема, чем обобщенные.

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