c# - 如何在 C# 中通过表达式树构建集合过滤器

标签 c# filtering expression-trees

我正在为 UserProfiles 构建一个过滤系统基于已知属性但未知(直到运行时)的过滤条件组合。

在我之前的问题中How do I create a generic Expression that has an expression as a parameter ,我找到了一种方法,可以通过导航属性从 User 实体访问任何值属性(即 (User)u=> u.NavigationProperty.AnotherNavigationProperty.SomeValue ) 我有一个方法可以返回谓词 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.Booksb=>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

最佳答案

你快完成了。现在您只需执行一个小技巧 - 将 CollectionPropertySelector lambda 表达式包装在 CollectionSelector lambda 表达式中。

Expression<Func<TParent,bool>> Wrap<TParent,TElement>(Expression<Func<TParent, IEnumerable<TElement>>> collection, Expression<Func<TElement, bool>> isOne, Expression<Func<IEnumerable<TElement>, Func<TElement, bool>, bool>> isAny)
{
    var parent = Expression.Parameter(typeof(TParent), "parent");

    return 
        (Expression<Func<TParent, bool>>)Expression.Lambda
        (
            Expression.Invoke
            (
                isAny,
                Expression.Invoke
                (
                    collection,
                    parent
                ),
                isOne
            ),
            parent
        );
}

您可能需要对此进行一些更改才能用于您的特定场景,但想法应该很清晰。我的测试基本上是这样的:

var user = new User { Books = new List<string> { "Book 1", "Book 2" }};

var query = Wrap<User, string>(u => u.Books, b => b.Contains("Bookx"), (collection, condition) => collection.Any(condition));

因此,您指定集合选择器、谓词和谓词运算符,然后就完成了。

为了清楚起见,我将其编写为通用方法,但它是动态的,本质上不是强类型的,因此如果需要,将其更改为非通用方法应该很容易。

关于c# - 如何在 C# 中通过表达式树构建集合过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20400474/

相关文章:

javascript - ASP.NET Page_Load 与 AJAX 页面刷新代码重用/冗余?

c# - 我应该使用 TCP 还是 UDP 作为 MMORPG 的网络协议(protocol)?

matlab - 中值滤波器与 matlab 中的伪中值滤波器

elasticsearch - elasticsearch过滤器不适用于聚合

c# - 过滤 IEnumerable

c# - 基于子实体的属性构建 OrderBy Lambda 表达式

c# - 在 C# 中处理 VB6 事件 - 为什么它有时只起作用?

c# - ASP.NET Core 1.0 中的单元测试路由(前 MVC 6)

c# - Linq2SQL 表达式没有翻译的解决方案

c# - 减少 LambdaExpression 签名