Как добавить к выражению
На основе моего вопроса со вчерашнего дня:
если бы мне пришлось добавить к существующему выражению 'where', как бы добавить?
Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
clientWhere = c => c.ClientFName == searchForClientFName;
}
if (filterByClientLName)
{
clientWhere = c => c.ClientLName == searchForClientLName;
}
Пользователь может ввести имя, фамилию или имя. Если они вводят оба значения, я хочу добавить выражение. Попытка посмотреть, есть ли эквивалент добавления, где я мог бы сделать
clientWhere.Append or clientWhere += add new expression
или что-то подобное
Ответы
Ответ 1
Я считаю, что вы можете просто сделать следующее:
Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
var prefix = clientWhere.Compile();
clientWhere = c => prefix(c) && c.ClientFName == searchForClientFName;
}
if (filterByClientLName)
{
var prefix = clientWhere.Compile();
clientWhere = c => prefix(c) && c.ClientLName == searchForClientLName;
}
Если вам нужно сохранить все в Expression
-land (для использования с IQueryable
), вы также можете сделать следующее:
Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
Expression<Func<Client, bool>> newPred =
c => c.ClientFName == searchForClientFName;
clientWhere = Expression.Lambda<Func<Freight, bool>>(
Expression.AndAlso(clientWhere, newPred), clientWhere.Parameters);
}
if (filterByClientLName)
{
Expression<Func<Client, bool>> newPred =
c => c.ClientLName == searchForClientLName;
clientWhere = Expression.Lambda<Func<Freight, bool>>(
Expression.AndAlso(clientWhere, newPred), clientWhere.Parameters);
}
Это можно сделать менее подробным, указав этот метод расширения:
public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right)
{
return Expression.Lambda<TDelegate>(Expression.AndAlso(left, right), left.Parameters);
}
Затем вы можете использовать синтаксис следующим образом:
Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
clientWhere = clientWhere.AndAlso(c => c.ClientFName == searchForClientFName);
}
if (filterByClientLName)
{
clientWhere = clientWhere.AndAlso(c => c.ClientLName == searchForClientLName);
}
Ответ 2
Это сложный сценарий. Вы почти строите свой собственный механизм запросов поверх LINQ. Решение JaredPar (куда оно пошло?) Отлично, если вы хотите логическое И между всеми вашими критериями, но это может быть не всегда так.
Когда я недавно спорил с этим в одном из моих проектов, я создал два списка:
List<Predicate<T>> andCriteria;
List<Predicate<T>> orCriteria;
(В этом случае T является клиентом для вас)
Я бы заполнил списки предикатами, чтобы я был прав. Например,
decimal salRequirement = 50000.00;
andCriteria.Add(c => c.Salary > salRequirement);
orCriteria.Add(c => c.IsMarried);
Тогда я бы проверял все критерии в предложении "Списки в моем приложении". Например:
Expression<Func<Client, bool>> clientWhere =
c => andCriteria.All(pred => pred(c) ) && orCriteria.Any(pred => pred(c) );
Это также можно сделать с помощью цикла for-loop для удобочитаемости. Не забудьте использовать правильный порядок операций при применении предложений OR и AND.
Ответ 3
Взгляните на Predicate Builder, я считаю, что это может сработать для вас.
Ответ 4
Это не совсем ответ на ваш вопрос, но я искал то же, что и вы, а затем я нашел лучший ответ на мой вопрос.
Вместо создания динамического выражения вы можете получить IQueryable
а затем отфильтруйте то, что хотите:
var customers = CustomerRepository.AllEntities();
if (!forename.IsNullOrEmpty())
customers = customers.Where(p => p.Forename == forename);
if (!familyname.IsNullOrEmpty())
customers = customers.Where(p => p.FamilyNames.Any(n => n.Name==familyname));
if (dob.HasValue)
customers = customers.Where(p => p.DOB == dob);
Примечание. Я был обеспокоен выполнением более одного оператора ".Where", потому что я боялся, что это сгенерирует более одного запроса в DataBase или потому, что мне придется извлекать все записи и затем отфильтруйте их, но это не так,
Динамическое преобразование Linq генерирует только один запрос только при вызове метода .ToList().
Здесь вы можете увидеть оригинальный вопрос, из которого я взял пример.
Ответ 5
Или что-то добавить к Джошу (поместите его в сумку трюков):
public static IQueryable<TSource> ObjectFilter<TSource>(this TSource SearchObject, List<Predicate<TSource>> andCriteria, List<Predicate<TSource>> orCriteria) where TSource : IQueryable<TSource>
{
//Yeah :)
Expression<Func<TSource, bool>> ObjectWhere = O => andCriteria.All(pred => pred(O)) && orCriteria.Any(pred => pred(O));
return SearchObject.Where<TSource>(ObjectWhere);
}
Ответ 6
Я пытался реализовать такие вещи. Принял у меня день, чтобы узнать.
Мое решение основано на фильтрах в цикле на основе массива предикатов.
В качестве примечания, это полностью общее и основанное отражение, потому что единственная информация о классе и поле - это String.
Чтобы сделать это простым, я вызываю непосредственно класс Model, но в проекте вы должны пойти с помощью контроллера, который вызывает модель.
Итак, идем:
Модельная часть, где T является общим в классе
public class DALXmlRepository<T> where T : class
{
public T GetItem(Array predicate)
{
IQueryable<T> QueryList = null;
QueryList = ObjectList.AsQueryable<T>().Where((Expression<Func<T, bool>>)predicate.GetValue(0));
for (int i = 1; i < predicate.GetLength(0); i++)
{
QueryList = QueryList.Where((Expression<Func<T, bool>>)predicate.GetValue(i));
}
if (QueryList.FirstOrDefault() == null)
throw new InvalidOperationException(this.GetType().GetGenericArguments().First().Name + " not found.");
return QueryList.FirstOrDefault();
}
}
Теперь LambdaExpression Builder, он базовый (со строковым типом или чем-то еще), вы можете улучшить его с большей функциональностью:
private static Expression BuildLambdaExpression(Type GenericArgument, string FieldName, string FieldValue)
{
LambdaExpression lambda = null;
Expression Criteria = null;
Random r = new Random();
ParameterExpression predParam = Expression.Parameter(GenericArgument, r.Next().ToString());
if (GenericArgument.GetProperty(FieldName).PropertyType == typeof(string))
{
Expression left = Expression.PropertyOrField(predParam, FieldName);
Expression LefttoUpper = Expression.Call(left, "ToUpper", null, null);
//Type du champ recherché
Type propType = GenericArgument.GetProperty(FieldName).PropertyType;
Expression right = Expression.Constant(FieldValue, propType);
Expression RighttoUpper = Expression.Call(right, "ToUpper", null, null);
Criteria = Expression.Equal(LefttoUpper, RighttoUpper);
}
else
{
Expression left = Expression.PropertyOrField(predParam, FieldName);
Type propType = GenericArgument.GetProperty(FieldName).PropertyType;
Expression right = Expression.Constant(Convert.ChangeType(FieldValue, propType), propType);
Criteria = Expression.Equal(left, right);
}
lambda = Expression.Lambda(Criteria, predParam);
return lambda;
}
Теперь функция вызова:
public static Hashtable GetItemWithFilter(string Entity, XMLContext contextXML, Hashtable FieldsNameToGet, Hashtable FieldFilter)
{
//Get the type
Type type = Type.GetType("JP.Model.BO." + Entity + ", JPModel");
Type CtrlCommonType = typeof(CtrlCommon<>).MakeGenericType( type );
//Making an instance DALXmlRepository<xxx> XMLInstance = new DALXmlRepository<xxx>(contextXML);
ConstructorInfo ci = CtrlCommonType.GetConstructor(new Type[] { typeof(XMLContext), typeof(String) });
IControleur DalInstance = (IControleur)ci.Invoke(new object[] { contextXML, null });
//Building the string type Expression<func<T,bool>> to init the array
Type FuncType = typeof(Func<,>).MakeGenericType( type ,typeof(bool));
Type ExpressType = typeof(Expression<>).MakeGenericType(FuncType);
Array lambda = Array.CreateInstance(ExpressType,FieldFilter.Count);
MethodInfo method = DalInstance.GetType().GetMethod("GetItem", new Type[] { lambda.GetType() });
if (method == null)
throw new InvalidOperationException("GetItem(Array) doesn't exist for " + DalInstance.GetType().GetGenericArguments().First().Name);
int j = 0;
IDictionaryEnumerator criterias = FieldFilter.GetEnumerator();
criterias.Reset();
while (criterias.MoveNext())
{
if (!String.IsNullOrEmpty(criterias.Key.ToString()))
{
lambda.SetValue(BuildLambdaExpression(type, criterias.Key.ToString(), criterias.Value.ToString()),j);
}
else
{
throw new JPException(JPException.MessageKey.CONTROLER_PARAMFIELD_EMPTY, "GetItemWithFilter", criterias.Key.ToString());
}
j++;
}
Object item = method.Invoke(DalInstance, new object[] { lambda });
}
Аргумент:
String Entity: имя класса сущности.
XMLContext: это единица работы репозитория, аргумент, который я использую для инициализации класса Model
Hashtable FieldsNameToGet: индекс/значение списка поля, которое я хочу вернуть
Hashtable FieldFilter: ключ/значение с полем Name/Content, используемым для выражения Lambda
Удачи.
Ответ 7
Если вы столкнулись с подобной проблемой, вы можете найти все возможные решения в этой замечательной теме. Или просто используйте PredicateBuilder - замечательный помощник для этого популярного приложения.
var predicate = PredicateBuilder.True<Client>();
if (filterByClientFName)
{
predicate = predicate.And(c => c.ClientFName == searchForClientFName);
}
if (filterByClientLName)
{
predicate = predicate.And(c => c.ClientLName == searchForClientLName);
}
var result = context.Clients.Where(predicate).ToArray();
Это некая реализация разработчика.
public static class PredicateBuilder
{
// Creates a predicate that evaluates to true.
public static Expression<Func<T, bool>> True<T>() { return param => true; }
// Creates a predicate that evaluates to false.
public static Expression<Func<T, bool>> False<T>() { return param => false; }
// Creates a predicate expression from the specified lambda expression.
public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
// Combines the first predicate with the second using the logical "and".
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
// Combines the first predicate with the second using the logical "or".
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
// Negates the predicate.
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
var negated = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
}
// Combines the first expression with the second using the specified merge function.
static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// zip parameters (map from parameters of second to parameters of first)
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with the parameters in the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// create a merged lambda expression with parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
class ParameterRebinder : ExpressionVisitor
{
readonly Dictionary<ParameterExpression, ParameterExpression> map;
ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
}