我遇到的问题是我希望在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
类,然后是BooleanFilter
,StringFilter
和ComparisonFilterBase
,它们来自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
参数是一个函数,而不是对象访问部分。
我可以做些什么来解决这个问题?
答案 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);
}