使用EntityFramework重构滤镜

时间:2014-08-16 22:04:51

标签: c# asp.net-mvc linq entity-framework linq-to-entities

我遇到的问题是我希望在Asp.net MVC 4上使用EntityFramework 5以一种很好的方式在应用程序中实现过滤。

过滤器是json字符串提供者,如下所示:

filter  
[   {"type":"string","value":"o","field":"name"},
    {"type":"list","value":[1,2,5],"field":"type"},
    {"type":"boolean","value":true,"field":"active"},   
    {"type":"numeric","comparison":"gt","value":0,"field":"order"},
    {"type":"date","comparison":"lt","value":"11/28/2014","field":"updated_at"}
]

(实际上,是ExtJs网格过滤器字符串)

我以这种方式实现了过滤器层次结构:FilterBase类,然后是BooleanFilterStringFilterComparisonFilterBase,它们来自FilterBase,然后{ {1}}和DateFilter

过滤器基座如下所示:

NumericFilter

我的想法是在每个班级中定义完成比较的方式。但为此,我需要知道要过滤的实体字段。这是public abstract class FilterBase { public string Field { get; set; } } /// <summary> /// Filter Base Class /// </summary> /// <typeparam name="T">The value type</typeparam> /// <typeparam name="T2">The entity property's type</typeparam> public abstract class FilterBase<T,T2>:FilterBase { public T Value { get; set; } /// <summary> /// Apply the filter /// </summary> /// <typeparam name="T1">The query main type, for instance, User if we are filtering users</typeparam> /// <param name="query">The query over to make the filter</param> /// <param name="field">A function that retrieves the main property's value</param> /// <returns>The new Query</returns> public abstract IQueryable<T1> Apply<T1>(IQueryable<T1> query, Func<T1, T2> field); } 方法的field参数的原因。例如:

Apply

例如,这可能是foreach (var filterBase in filters) { switch (filterBase.Field.ToLower()) { case "name": query = ((StringFilter)filterBase).Apply(query, u => u.UserFirstName); break; case "lastname": query = ((StringFilter) filterBase).Apply(query, u => u.UserLastName); break; case "login": query = ((StringFilter)filterBase).Apply(query, u => u.UserLogin); break; case "role": query = ((StringFilter)filterBase).Apply(query, u => u.UserRole.Name); break; case "email": query = ((StringFilter)filterBase).Apply(query, u => u.UserEmail); break; case "active": query = ((BooleanFilter)filterBase).Apply(query, u => u.Active); break; } } 类:

NumericFilter

这里的问题是Linq to Entities不适用于函数public class NumericFilter : ComparisonFilterBase<float, float> { public override IQueryable<T1> Apply<T1>(IQueryable<T1> query, Func<T1, float> field) { switch (Comparison) { case Comparison.GreaterThan: return query.Where(t => field(t) > Value); case Comparison.LowerThan: return query.Where(t => field(t) < Value); case Comparison.Equals: return query.Where(t => field(t) == Value); } return query; } } 的评估。我知道,对于instace,此查询有效:field,唯一的问题是query = query.Where(t => t.Age > Value); ExpressionTree参数是一个函数,而不是对象访问部分。

我可以做些什么来解决这个问题?

2 个答案:

答案 0 :(得分:1)

你快到了。只需使用Expression<Func<T, Type>>代替Func<T,Type>即可。 像这样:

   public static IQueryable<T1> Apply<T1, TField>(IQueryable<T1> query, Expression<Func<T1, TField>> field, TField value, Comparison compare)
    {
        ExpressionType expressionType;
        ConstantExpression searchValue = Expression.Constant(value);
        ParameterExpression parameter = field.Parameters.First();
        Expression body;
        if (!Enum.TryParse(Enum.GetName(typeof(Comparison),compare), true, out expressionType)) 
        {
            //probably string: StartsWith, EndsWith, Contains
            MethodInfo stringMethod = GetStringMethodInfo(compare);
            body = Expression.Call(field.Body, stringMethod, searchValue);
        }
        else
        {
            body = Expression.MakeBinary(expressionType, field, searchValue);
        }
        Expression<Func<T1, bool>> predicate = Expression.Lambda<Func<T1, bool>>(body, parameter);
        return query.Where(predicate);
    }

    private static MethodInfo GetStringMethodInfo(Comparison comparer)
    {
        string methodName = Enum.GetName(typeof(Comparison), comparer);
        return
            typeof(string).GetMethods()
                .FirstOrDefault(m => m.Name.Equals(methodName) && m.GetParameters().Count() == 1);
    }

    public enum Comparison
    {
        GreaterThan = ExpressionType.GreaterThan,

        GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual,

        LessThan = ExpressionType.LessThan,

        LessThanOrEqual = ExpressionType.LessThanOrEqual,

        Equals = ExpressionType.Equal,

        NotEqual = ExpressionType.NotEqual,

        StartsWith,

        EndsWith,

        Contains
    }

根据评论编辑:代码中的修正

答案 1 :(得分:0)

这个答案只是为了展示最终解决方案,也许对其他人有帮助。这是基于:user3411327的通过答案。这是最终的字符串过滤器Apply方法,Value字段来自基本过滤器类。

        public override IQueryable<T1> Apply<T1>(IQueryable<T1> query, Expression<Func<T1, string>> field)
        {   
            //create the constant expression for the value
            ConstantExpression searchValue = Expression.Constant(Value);
            //create the expression for the parameter

//            ParameterExpression parameter = Expression.Parameter(typeof(T1), "t");
            ParameterExpression parameter = field.Parameters.FirstOrDefault();

            //create the body -> it is calling the method contains from the field in the field expression, and the value in searchValue
            MethodInfo stringMethod = typeof(string).GetMethods().FirstOrDefault(m => m.Name.Equals("Contains") && m.GetParameters().Count() == 1);
            Expression body = Expression.Call(field.Body, stringMethod, searchValue);

            //create the final predicate
            Expression<Func<T1, bool>> predicate = Expression.Lambda<Func<T1, bool>>(body, parameter);
            return query.Where(predicate);
        }