如果我必须附加到我现有的'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
或类似的东西
答案 0 :(得分:52)
我相信你可以做到以下几点:
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);
}
答案 1 :(得分:8)
这是一个复杂的场景。您几乎在LINQ之上构建自己的查询引擎。 JaredPar的解决方案(它去了哪里?)如果你想要在所有标准之间进行逻辑AND,那就太棒了,但情况可能并非总是如此。
当我最近在我的一个项目中与此争吵时,我创建了两个列表:
List<Predicate<T>> andCriteria;
List<Predicate<T>> orCriteria;
(在这种情况下,T是客户,为你)
我会使用谓词来填充列表,我希望它是真的。例如,
decimal salRequirement = 50000.00;
andCriteria.Add(c => c.Salary > salRequirement);
orCriteria.Add(c => c.IsMarried);
然后,我会检查Where子句中列表中的所有条件。例如:
Expression<Func<Client, bool>> clientWhere =
c => andCriteria.All(pred => pred(c) ) && orCriteria.Any(pred => pred(c) );
为了便于阅读,也可以使用for循环来完成。在应用OR和AND子句时,请记住使用正确的操作顺序。
答案 2 :(得分:6)
看看Predicate Builder,我相信这可能适合你。
答案 3 :(得分:2)
从2020年开始的解决方案更加轻松,优雅:)
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.And);
}
适用于IQueryable。
答案 4 :(得分:1)
这不是你问题的答案,但是,我一直在寻找你的相同的事情,然后我找到了一个更好的答案。
您可以检索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中生成多个查询,或者因为我必须检索所有记录和然后过滤它们,但事实并非如此, 仅当您调用.ToList()方法时,Linq动态生成一个查询。
Here你可以看到我从这个例子中得到的原始问题。
答案 5 :(得分:1)
如果遇到类似的问题,您可以在出色的topic中找到所有可能的解决方案。 或者只是使用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);
}
}
}
答案 6 :(得分:0)
或者要添加到Josh的东西(把它放在我的技巧包里):
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);
}
答案 7 :(得分:0)
我试图实现这种东西。花了一天时间才发现。 我的解决方案基于基于谓词数组的循环中的过滤器。 作为一个注释,它完全是Generic和基于Reflection,因为关于类和字段的唯一信息是String。 为简单起见,我直接调用Model类,但是在项目中,你应该通过一个调用Model的控制器。
所以我们走了: Model部分,其中T是类中的Generic
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构建器,它是基础构建器(使用字符串类型或其他东西),您可以使用更多功能来改进它:
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 });
}
论点是: 字符串实体:实体类名称。 XMLContext:它是存储库的工作单元,我用它来初始化Model类 Hashtable FieldsNameToGet:我想要返回的字段列表的索引/值 Hashtable FieldFilter:用于生成Lambda表达式的FieldName / Content的键/值
祝你好运。