使用泛型类型构建查询表达式

时间:2018-05-08 14:31:00

标签: c# entity-framework linq

我需要帮助才能完成此查询。我需要使用表达式,以便我可以动态地处理查询。换句话说,用户构建了推入其中的查询参数。我需要专门处理行Type yourType = typeof(YourGeneric);中引用的泛型类型的帮助,&amp; ParameterExpression pe = Expression.Parameter(yourType, "x");&amp; Expression left = Expression.Property(pe, yourType.GetProperty(wheres[i].ColumnName)) ..完全披露,我是通过另一个推荐而做的,并不完全理解泛型。有关详细信息,请参阅Entity Framework Dynamic Where Clause from List<object>

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 :(得分:1)

YourGeneric应该是您要查询的实体类型。例如,如果在DbContext中有一组Cities(DbSet Cities),则应将该类型作为通用类型传递。

您不希望为上下文中的每个实体类型编写自定义代码。您编写的代码应该适用于查询Cities表和查询Fruits表。因此 - 使用泛型。

举个例子:

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

将为City类型的lambda参数创建表达式,该表达式可用于查询该类型的集合。为了使代码可以重复使用,你可以使它具有通用性(如你的问题中所述)以及这些内容:

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中的表达式树是一个比泛型更复杂的主题。

答案 1 :(得分:1)

这就是我的实施情况。

测试代码:

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