【发布时间】:2013-12-05 12:47:37
【问题描述】:
我正在为UserProfiles 构建一个基于已知属性但未知(直到运行时)过滤条件组合的过滤系统。
在我之前的问题How do I create a generic Expression that has an expression as a parameter 中,我想出了一种方法,可以为通过导航属性(即(User)u=> u.NavigationProperty.AnotherNavigationProperty.SomeValue)从用户实体可访问的任何值属性设置 FilterDefinition
我有一个方法可以为给定的属性、操作( > Expression<Func<User,bool>>。
现在是时候根据集合属性过滤它们了。 例如说用户有 CheckedOutBooks 集合(这完全是虚构的,但会做) 我需要为 User 对象上 CheckedOutBooks 集合的 Name 属性创建一个过滤器定义。
我有什么:
用户集合
用户类具有书籍集合
现在我想创建一个方法
Expression<Func<User,bool>> GetPredicate(Expression<User,TProperty>, Operations operation, TProperty value)
我可以打电话给GetPredicate(u=>u.Books.Select(b=>b.Name), Operations.Contains, "C# in a nutshell")
并得到一个类似于
的表达式u=>u.Books.Any(b=>b.Name == "C# in a nutshell")
我在想也许将第一个参数一分为二来实现这一点会更容易。
也许u=>u.Books 和b=>b.Name 会做得更好?
编辑: 到目前为止我得到了什么:
class FilterDefinitionForCollectionPropertyValues<T>:FilterDefinition, IUserFilter
{
public Expression<Func<UserProfile, IEnumerable<T>>> CollectionSelector { get; set; }
public Expression<Func<T, string>> CollectionPropertySelector { get; set; }
public Expression<Func<Profile.UserProfile, bool>> GetFilterPredicateFor(FilterOperations operation, string value)
{
var propertyParameter = CollectionPropertySelector.Parameters[0];
var collectionParameter = CollectionSelector.Parameters[0];
// building predicate to supply to Enumerable.Any() method
var left = CollectionPropertySelector.Body;
var right = Expression.Constant(value);
var innerLambda = Expression.Equal(left, right);
Expression<Func<T, bool>> innerFunction = Expression.Lambda<Func<T, bool>>(innerLambda, propertyParameter);
var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(T));
var outerLambda = Expression.Call(method, Expression.Property(collectionParameter, typeof(UserProfile).GetProperty("StaticSegments")), innerFunction);
throw new NotImplementedException();
}
}
现在这个工作非常好,完全符合需要,现在我唯一需要弄清楚的是如何替换typeof(UserProfile).GetProperty("StaticSegments")) 以某种方式使用CollectionPropertySelector,在当前示例中将是(UserProfile)u=>u.StaticSegments
【问题讨论】:
-
您能否解释一下这种方法的原因?我可以想到很多方法来做到这一点,但我不知道哪一种对你有帮助,因为你没有说你想如何实际使用它。
-
为什么还需要
GetPredicate()?u => u.Books.Select(b=>b.Name).Contains("C# in a nutshell")或类似的东西不可以吗? -
@Luaan 我已经更新了问题以包含一些背景故事。从我现在的立场来看,没有办法使用动态 linq。并且定义所有可能的过滤组合的可能组合似乎很乏味而且不是 DRY (-:
-
@svick 当然它会做临时的,但会有一个用户界面,用户可以选择如何塑造过滤器。用户所关心的只是一个变量、条件和一个要匹配的值。所以应该有一个变量列表,可能的比较操作和一些关于值可能是什么的提示。
-
这是一个想法——你真的需要一个表达式树吗?
IQueryable还不够吗?你仍然可以使用它来查询数据库(或其他任何东西),它有大量方便的方法来做你需要的事情,并且构建查询变得更加容易。
标签: c# filtering expression-trees