Создание дерева динамических выражений для фильтрации по свойству коллекции
Я пытаюсь создать лямбда-выражение, которое будет объединено с другими в довольно большое дерево выражений для фильтрации. Это отлично работает, пока мне не потребуется фильтровать свойство sub collection.
Как вы создаете выражение Lambda, которое будет фильтровать с помощью 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);
Ответы
Ответ 1
Нашел решение. Я раньше не искал какой-либо метод в правильном месте.
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);
Ответ 2
Пожалуйста, не делайте этого, что вы действительно хотите использовать библиотеку под названием dynamic linq. http://nuget.org/packages/DynamicLINQ
Вы можете просто хранить свои запросы в виде строк и поддерживать очень сложные запросы. Деревья выражений - это кошмар.
Ответ 3
То, что вы указали в качестве примера, будет работать на основе вашего комментария. Вот пример того, с чем я работаю:
Templates.Where(t => t.TemplateFields.Any(f => f.Required == 'Y'))
У нас есть шаблоны, которые имеют конкретный набор полей, и эти поля могут потребоваться. Поэтому я могу получить шаблоны, где это поле требуется для этого поля.
Надеюсь, это поможет... или, по крайней мере, подтверждает то, что вы пытаетесь сделать. Дайте мне знать, если у вас есть дополнительные вопросы по этому поводу, и я уточню.
Удачи!
Ответ 4
Предоставленный код
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"))