简体   繁体   English

构建动态表达式树以过滤集合属性

[英]Building a dynamic expression tree to filter on a collection property

I am trying to build a lambda expression that will be combined with others into a rather large expression tree for filtering. 我正在尝试构建一个lambda表达式,它将与其他表达式组合成一个相当大的表达式树进行过滤。 This works fine until I need to filter by a sub collection property. 这工作正常,直到我需要通过子集合属性进行过滤。

How do you build a Lambda expression that will filter using Any() on a property of a collection which is a property of the root object? 如何构建一个Lambda表达式,它将在集合的属性上使用Any()进行过滤,该集合属性是根对象的属性?

Example: 例:

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test"))

This is how I would build the expression statically but I need to build it dynamically. 这是我如何静态构建表达式,但我需要动态构建它。 Sorry for the confusion. 对困惑感到抱歉。

Edit: Here is a snippet of how I handle the less complicated expressions: 编辑:这是我如何处理不太复杂的表达式的片段:

IQueryable<Office> officeQuery = CurrentDataSource.Offices.AsQueryable<Office>();
ParameterExpression pe = Expression.Parameter(typeof(Office), "Office");
ParameterExpression tpe = Expression.Parameter(typeof(Trades), "Trades");

Expression SimpleWhere = null;
Expression ComplexWhere = null;
foreach (ServerSideFilterObject fo in ssfo)
{
    SimpleWhere = null;
    foreach (String value in fo.FilterValues)
    {
        if (!CollectionProperties.Contains(fo.PropertyName))
        {
            //Handle singleton lambda logic here.
            Expression left = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName));
            Expression right = Expression.Constant(value);
            if (SimpleWhere == null)
            {
                SimpleWhere = Expression.Equal(left, right);
            }
            else
            {
                Expression e1 = Expression.Equal(left, right);
                SimpleWhere = Expression.Or(SimpleWhere, e1);
            }
        }
        else
        {
            //handle inner Collection lambda logic here.
            Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name"));
            Expression right = Expression.Constant(value);
            Expression InnerLambda = Expression.Equal(left, right);

            //Problem area.
            Expression OfficeAndProperty = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName));
            Expression OuterLambda = Expression.Call(OfficeAndProperty, typeof(Trades).GetMethod("Any", new Type[] { typeof(Expression) } ),InnerLambda);

            if (SimpleWhere == null)
                SimpleWhere = OuterLambda;
            else
                SimpleWhere = Expression.Or(SimpleWhere, OuterLambda);
        }
    }
    if (ComplexWhere == null)
        ComplexWhere = SimpleWhere;
    else
        ComplexWhere = Expression.And(ComplexWhere, SimpleWhere);
}
MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { officeQuery.ElementType }, officeQuery.Expression, Expression.Lambda<Func<Office, bool>>(ComplexWhere, new ParameterExpression[] { pe }));
results = officeQuery.Provider.CreateQuery<Office>(whereCallExpression);

Found the solution. 找到了解决方案。 I wasn't looking for the any method in the right place before. 我之前没有在正确的地方寻找任何方法。

Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name"));
Expression right = Expression.Constant(value);
Expression InnerLambda = Expression.Equal(left, right);
Expression<Func<Trades, bool>> innerFunction = Expression.Lambda<Func<Trades, bool>>(InnerLambda, tpe);

method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(Trades));
OuterLambda = Expression.Call(method, Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)),innerFunction);

Please don't do this, what you really want it to use a library called dynamic linq. 请不要这样做,你真的希望它使用一个名为dynamic linq的库。 http://nuget.org/packages/DynamicLINQ http://nuget.org/packages/DynamicLINQ

You can just store your queries as strings, and it supports very complex querying. 您可以将查询存储为字符串,并且它支持非常复杂的查询。 Expression trees are a nightmare. 表达树是一场噩梦。

What you listed as your example will work based on your comment. 您列出的示例将根据您的评论工作。 Here's an example of what I work with: 这是我合作的一个例子:

Templates.Where(t => t.TemplateFields.Any(f => f.Required == 'Y'))

We have templates that have specific collection of fields, and those fields could be required. 我们有具有特定字段集合的模板,可能需​​要这些字段。 So I can get the templates where any field is required by that statement above. 所以我可以获得上面那个语句需要任何字段的模板。

Hopefully this helps...or at least confirms what you're trying to do. 希望这有助于...或至少证实你正在尝试做什么。 Let me know if you have more questions about this and I'll elaborate. 如果您对此有更多疑问,请告诉我,我会详细说明。

Good luck! 祝好运!

The provided code 提供的代码

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test"))

should work, as long as o.base_Trades implements IEnumerable<Trade> . 应该工作,只要o.base_Trades实现IEnumerable<Trade> If o.base_Trades does only implement IEnumerable , you need to use either Cast<Trade>() if you can be sure that all elements in o.base_Trades are of your Trade type or OfType<Trade>() if there might be elements of other (incompatible) types. 如果o.base_Trades只实现IEnumerable ,你需要使用Cast<Trade>()如果你可以确定o.base_Trades中的所有元素都是你的Trade类型或o.base_Trades OfType<Trade>()如果有可能的元素其他(不兼容)类型。

That would then look like this: 那将是这样的:

CurrentDataSource.Offices
    .Where(o => o.base_Trades.Cast<Trade>.Any(t => t.Name == "test"))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM