C# 如何附加到表达式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1266742/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to Append to an expression
提问by
Based on my question from yesterday:
根据我昨天的问题:
if I had to append to my existing 'where' expression, how would i append?
如果我必须附加到我现有的“where”表达式,我将如何附加?
Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
clientWhere = c => c.ClientFName == searchForClientFName;
}
if (filterByClientLName)
{
clientWhere = c => c.ClientLName == searchForClientLName;
}
The user can input either first name or last name or both. If they enter both i want to append to the expression. Trying to see if there is an equivalent of an append where i could do
用户可以输入名字或姓氏或两者。如果他们同时输入,我想附加到表达式中。试图看看是否有一个相当于我可以做的附加
clientWhere.Append or clientWhere += add new expression
or something similar
或类似的东西
回答by JoshJordan
This is a complex scenario. You are almost building your own query engine on top of LINQ. JaredPar's solution (where did it go?) is great if you want a logical AND between all of your criteria, but that may not always be the case.
这是一个复杂的场景。您几乎是在 LINQ 之上构建自己的查询引擎。JaredPar 的解决方案(它去哪儿了?)如果您想在所有标准之间进行逻辑 AND 是非常棒的,但情况可能并非总是如此。
When I was wrangling with this in one of my project recently, I created two Lists:
最近在我的一个项目中与此争论时,我创建了两个列表:
List<Predicate<T>> andCriteria;
List<Predicate<T>> orCriteria;
(In this case, T is Client, for you)
(在这种情况下, T 是Client,对你来说)
I would populate the Lists with predicates that I want to be true. For instance,
我会用我想要为真的谓词填充列表。例如,
decimal salRequirement = 50000.00;
andCriteria.Add(c => c.Salary > salRequirement);
orCriteria.Add(c => c.IsMarried);
Then, I would check against all the criteria in the Lists in my Where clause. For instance:
然后,我将检查我的 Where 子句中列表中的所有标准。例如:
Expression<Func<Client, bool>> clientWhere =
c => andCriteria.All(pred => pred(c) ) && orCriteria.Any(pred => pred(c) );
This could also be done with a for-loop for readability's sake. Remember to use the correct order of operations when applying your OR and AND clauses.
为了可读性,这也可以通过 for 循环来完成。请记住在应用 OR 和 AND 子句时使用正确的操作顺序。
回答by Jason
I believe you can just do the following:
我相信你可以做到以下几点:
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;
}
If you need to keep everything in Expression
-land (to use with IQueryable
), you could also do the following:
如果您需要将所有内容保留在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);
}
This can be made less verbose by defining this extension method:
通过定义这个扩展方法,这可以变得不那么冗长:
public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right)
{
return Expression.Lambda<TDelegate>(Expression.AndAlso(left, right), left.Parameters);
}
You can then use syntax like this:
然后你可以使用这样的语法:
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);
}
回答by Vasu Balakrishnan
Take a look at Predicate Builder, I believe this might work for you.
看看Predicate Builder,我相信这可能对你有用。
回答by MarkWalls
Or something to add to Josh (Put it in my bag of tricks):
或添加到乔希的东西(把它放在我的技巧包里):
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);
}
回答by Daniel
It′s not exactly the answer for your question, but, I was looking for the same thing you are, and then I've found a better answer to my question.
这不完全是您问题的答案,但是,我一直在寻找与您相同的东西,然后我找到了更好的答案。
Instead of building a dynamic Expression, you could retrieve the IQueryable and then filter what you want like this:
您可以检索 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);
Note:I was concerned about executing more then one ".Where" statement because I was afraid this would generate more than one query in the DataBase, or because I would have to retrive all records and then filter them, but this is not true, Linq dynamic generate just one query only when you call .ToList() method.
注意:我担心执行多个“.Where”语句,因为我担心这会在数据库中生成多个查询,或者因为我必须检索所有记录然后过滤它们,但事实并非如此, 仅当您调用 .ToList() 方法时,Linq 动态仅生成一个查询。
Hereyou can see original question that I've took the example from.
在这里你可以看到我从这个例子中得到的原始问题。
回答by Alan
I tried to implement this kind of stuff. Took me a day to find out. My solution is based on filter in a loop based on a Array of predicate. As a note, it s totally Generic and based Reflection because the only information about class and field are String. To make it simple, i call directly the Model class but in a project you should go by a controler who is calling the Model.
我试图实现这种东西。花了我一天时间才知道。我的解决方案基于基于谓词数组的循环中的过滤器。需要注意的是,它完全是通用的和基于反射的,因为关于类和字段的唯一信息是字符串。为简单起见,我直接调用模型类,但在项目中,您应该由调用模型的控制器调用。
So here we go : The Model part where T is a Generic in the class
所以我们开始:模型部分,其中 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();
}
}
Now the LambdaExpression Builder, it's a base one(with String type or something else) , you can improve it with more functionnality :
现在 LambdaExpression Builder,它是一个基础的(带有 String 类型或其他类型),您可以使用更多功能对其进行改进:
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;
}
Now the Calling function :
现在调用函数:
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 });
}
The argument are : String Entity : Entity class name. XMLContext : it s the unit of work of the repository, argument i use to initialize the Model class Hashtable FieldsNameToGet : Index/value of the list of the field i want to get back Hashtable FieldFilter : the key/Value with FieldName/Content used to make the Lambda expression
参数是: String Entity :实体类名。XMLContext :它是存储库的工作单元,我用来初始化模型类的参数 Hashtable FieldsNameToGet :我想要取回的字段列表的索引/值 Hashtable FieldFilter :用于 FieldName/Content 的键/值制作 Lambda 表达式
Good Luck.
祝你好运。
回答by trueboroda
If you encounter a similar problem, you can find all possible solutions in this great topic. Or just use PredicateBuilder is awesome helper for this poporse.
如果您遇到类似的问题,您可以在这个伟大的主题中找到所有可能的解决方案。或者只是使用 PredicateBuilder 是这个 poporse 的好帮手。
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();
It is some builder implementation.
这是一些构建器实现。
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);
}
}
}
回答by Petr Savchenko
Much easier and elegant solution from 2020 :)
从 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);
}
Works for IQueryable.
适用于 IQueryable。