问题描述
Expression<Func<Dealer, bool>> GetFilter() { Expression<Func<Dealer, bool>> f = (d) => 1 == 1; var s = ""; if (QS["Province"] != null) { s = QS["Province"]; f = d => d.Address.Province.Equals(s); } if (QS["City"] != null) { s = QS["City"]; f = d => d.Address.City.Equals(s); } if (QS["NameStartWith"] != null) { s = QS["NameStartWith"]; f = d => d.DealerName.Substring(0, 1).ToUpper().Equals(s); } return f; }
根据查询字符串,我想生成表达式,以便使用通用存储库从数据库中获取所有经销商.
这里是代码
Expression<Func<Dealer, bool>> filter = GetFilter(); GenericRepository<Dealer> DealerRepository = new GenericRepository<Dealer>(); List<Dealer> Dealers = DealerRepository.Get(filter).ToList();
推荐答案
我假设,你使用 这个存储库模式一个>?
问题是,您只有一个表达式变量 (f),并且您正在覆盖它的内容.因此查询字符串
<块引用>Province=ONCA&City=多伦多
会执行类似的东西
s = QS["Province"]; f = d => d.Address.Province.Equals(s); s = QS["City"]; f = d => d.Address.City.Equals(s);
但 f 只包含最后一个赋值.要以 EF 友好的方式组合 Lamda-Expressions,您需要一些来自 这篇 MSDN 帖子:
public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> _map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) { _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); } } public static class Utility { public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) { // build parameter 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 parameters from the first var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); } 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); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.Or); } }
现在您可以编写如下代码:
Expression<Func<string, bool>> filter = x => true; filter = filter.And(x => x.Contains("a")); filter = filter.And(x => x.Contains("b")); filter = filter.Or(x => x.Contains("c")); var compiled = filter.Compile(); Console.WriteLine(compiled.Invoke("aaa")); // False Console.WriteLine(compiled.Invoke("abba")); // True Console.WriteLine(compiled.Invoke("aac")); // True
或者在你的情况下:
Expression<Func<Dealer, bool>> GetFilter() { Expression<Func<Dealer, bool>> filter = x => true; if (QS["Province"] != null) { var s = QS["Province"]; filter = filter.And(d => d.Address.Province.Equals(s)); } if (QS["City"] != null) { var s = QS["City"]; filter = filter.And(d => d.Address.City.Equals(s)); } if (QS["NameStartWith"] != null) { var s = QS["NameStartWith"]; filter = filter.And(d => d.DealerName.Substring(0, 1).ToUpper().Equals(s)); } return filter; }
问题描述
Expression<Func<Dealer, bool>> GetFilter() { Expression<Func<Dealer, bool>> f = (d) => 1 == 1; var s = ""; if (QS["Province"] != null) { s = QS["Province"]; f = d => d.Address.Province.Equals(s); } if (QS["City"] != null) { s = QS["City"]; f = d => d.Address.City.Equals(s); } if (QS["NameStartWith"] != null) { s = QS["NameStartWith"]; f = d => d.DealerName.Substring(0, 1).ToUpper().Equals(s); } return f; }
Depending on querystrings I want to generate Expression for getting all the dealers from database using generic repository.
here is the code
Expression<Func<Dealer, bool>> filter = GetFilter(); GenericRepository<Dealer> DealerRepository = new GenericRepository<Dealer>(); List<Dealer> Dealers = DealerRepository.Get(filter).ToList();
推荐答案
I assume, you utilize this repository pattern?
The problem is, that you have only one Expression variable (f) and you are overwriting its contents. Thus the query string
Province=ONCA&City=Toronto
Will execute something like
s = QS["Province"]; f = d => d.Address.Province.Equals(s); s = QS["City"]; f = d => d.Address.City.Equals(s);
But f will only contain the last assigned value. To combine Lamda-Expressions in an EF-friendly way, you need some Utility Classes from this MSDN post:
public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> _map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) { _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); } } public static class Utility { public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) { // build parameter 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 parameters from the first var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); } 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); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.Or); } }
Now you can write code like this:
Expression<Func<string, bool>> filter = x => true; filter = filter.And(x => x.Contains("a")); filter = filter.And(x => x.Contains("b")); filter = filter.Or(x => x.Contains("c")); var compiled = filter.Compile(); Console.WriteLine(compiled.Invoke("aaa")); // False Console.WriteLine(compiled.Invoke("abba")); // True Console.WriteLine(compiled.Invoke("aac")); // True
Or in your case:
Expression<Func<Dealer, bool>> GetFilter() { Expression<Func<Dealer, bool>> filter = x => true; if (QS["Province"] != null) { var s = QS["Province"]; filter = filter.And(d => d.Address.Province.Equals(s)); } if (QS["City"] != null) { var s = QS["City"]; filter = filter.And(d => d.Address.City.Equals(s)); } if (QS["NameStartWith"] != null) { var s = QS["NameStartWith"]; filter = filter.And(d => d.DealerName.Substring(0, 1).ToUpper().Equals(s)); } return filter; }