Построение дерева динамических выражений для фильтрации по свойству коллекции - PullRequest
15 голосов
/ 24 января 2012

Я пытаюсь построить лямбда-выражение, которое будет объединено с другими в довольно большое дерево выражений для фильтрации.Это работает нормально, пока мне не нужно фильтровать по свойству подколлекции.

Как построить лямбда-выражение, которое будет фильтровать, используя Any (), для свойства коллекции, которая является свойством корневого объекта?

Пример:

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

Вот как я должен строить выражение статически, но мне нужно строить его динамически.Извините за путаницу.

Редактировать: Вот фрагмент того, как я обрабатываю менее сложные выражения:

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);

Ответы [ 4 ]

11 голосов
/ 24 января 2012

Нашел решение.Я не искал какой-либо метод в нужном месте раньше.

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);
1 голос
/ 24 января 2012

Пожалуйста, не делайте этого, для чего вы действительно хотите использовать библиотеку с именем dynamic linq.http://nuget.org/packages/DynamicLINQ

Вы можете просто хранить свои запросы в виде строк, и это поддерживает очень сложные запросы.Выражение деревьев - это кошмар.

0 голосов
/ 24 января 2012

Предоставленный код

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

должен работать, пока o.base_Trades реализует IEnumerable<Trade>.Если o.base_Trades реализует только IEnumerable, вам нужно использовать либо Cast<Trade>(), если вы можете быть уверены, что все элементы в o.base_Trades относятся к вашему типу Trade, либо OfType<Trade>(), если могут быть элементы других (несовместимые) типы.

Это будет выглядеть так:

CurrentDataSource.Offices
    .Where(o => o.base_Trades.Cast<Trade>.Any(t => t.Name == "test"))
0 голосов
/ 24 января 2012

То, что вы указали в качестве примера, будет работать на основе вашего комментария. Вот пример того, с чем я работаю:

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

У нас есть шаблоны с определенной коллекцией полей, и эти поля могут быть обязательными. Таким образом, я могу получить шаблоны, где любое поле требуется для этого утверждения выше.

Надеюсь, это поможет ... или хотя бы подтвердит то, что вы пытаетесь сделать. Дайте мне знать, если у вас есть еще вопросы по этому поводу, и я уточню.

Удачи!

...