任何包含的表达式树
Expression Tree for Any with Contains
我们有一个 REST-API,它采用 ProductEANs.EAN 格式的 string
查询,所以 .分开了。 searchFields
是从 string
解析而来的,如下所示:{"ProductEANs.EAN":"113"}
。基类型总是已知的。现在我想动态创建一个 Expression
树来查询数据集。如果 属性 不是 List
,它可以工作,但我无法让它与 List
一起工作。我想要重新创建的是 expression
: productsQuery.Where(p => p.ProductEANs.Any(e => e.EAN.Contains("113")));
。这是我到目前为止的位置:
IQueryable<Product> productsQuery = _context.Products
.Include(p => p.ProductEANs);
foreach (KeyValuePair<string, string> searchField in searchFields)
{
if (!string.IsNullOrWhiteSpace(searchField.Value))
{
var parameterExpression = Expression.Parameter(typeof(Product), "product");
var constant = Expression.Constant(searchField.Value);
MethodCallExpression expression = null;
Expression property = parameterExpression;
foreach (var member in searchField.Key.Split('.'))
{
if (property.Type.IsGenericType &&
typeof(IEnumerable<>)
.MakeGenericType(property.Type.GetGenericArguments())
.IsAssignableFrom(property.Type))
{
var genType = property.Type.GenericTypeArguments.FirstOrDefault();
if (genType != null)
{
Expression subExp = Expression.Parameter(genType, "pEAN");
Expression subProp = Expression.PropertyOrField(subExp, member);
var expContains = Expression.Call(subProp, "Contains", null, constant);
var anyMethod = typeof(Enumerable).GetMethods().First(method => method.Name == "Any" && method.GetParameters().Length == 2).MakeGenericMethod(genType);
expression = Expression.Call(anyMethod, subProp, expContains);
}
}
else
{
property = Expression.PropertyOrField(property, member);
}
}
if (expression == null)
expression = Expression.Call(property, "Contains", null, constant);
var lambda = Expression.Lambda<Func<Product, bool>>(expression, parameterExpression);
productsQuery = productsQuery.Where(lambda);
}
}
productsQuery = productsQuery.OrderBy(p => p.Title);
var products = productsQuery.ToList().Select(p => (ProductDTO)p).ToList();
if (property.Type
的 if 部分中的所有内容...都是我在努力让它发挥作用。数据模型如下所示:
public class Product
{
public Guid Id {get;set;}
public string Tile {get;set;}
public List<ProductEAN> ProductEANs { get; set; }
}
public class ProductEAN
{
public string EAN { get; set; }
public decimal ProductCount { get; set; }
}
这应该可以解决问题:
public static class FilterExtensions
{
private readonly struct Node
{
public Node(ParameterExpression parameter, Expression body)
{
Parameter = parameter;
Body = body;
}
public ParameterExpression Parameter { get; }
public Expression Body { get; }
}
public static Expression<Func<T, bool>> BuildFilter<T>(string key, string value)
{
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
var p = Expression.Parameter(typeof(T), "p");
Expression body = p;
var stack = new Stack<Node>();
foreach (string member in key.Split('.'))
{
if (body.Type.IsGenericType)
{
var genericArgs = body.Type.GetGenericArguments();
if (genericArgs.Length == 1 && typeof(IEnumerable<>)
.MakeGenericType(genericArgs)
.IsAssignableFrom(body.Type))
{
stack.Push(new Node(p, body));
p = Expression.Parameter(genericArgs[0], "s" + stack.Count);
body = p;
}
}
body = Expression.PropertyOrField(body, member);
}
var constant = Expression.Constant(value, typeof(string));
body = Expression.Call(body, nameof(string.Contains), null, constant);
while (stack.Count != 0)
{
var childFilter = Expression.Lambda(body, p);
var parent = stack.Pop();
body = Expression.Call(typeof(Enumerable),
nameof(Enumerable.Any),
new[] { p.Type },
parent.Body,
childFilter);
p = parent.Parameter;
}
return Expression.Lambda<Func<T, bool>>(body, p);
}
}
用法:
IQueryable<Product> productsQuery = _context.Products
.Include(p => p.ProductEANs);
foreach (KeyValuePair<string, string> searchField in searchFields)
{
if (!string.IsNullOrWhiteSpace(searchField.Value))
{
var filter = FilterExtensions.BuildFilter<Product>(searchField.Key, searchField.Value);
productsQuery = productsQuery.Where(filter);
}
}
我们有一个 REST-API,它采用 ProductEANs.EAN 格式的 string
查询,所以 .分开了。 searchFields
是从 string
解析而来的,如下所示:{"ProductEANs.EAN":"113"}
。基类型总是已知的。现在我想动态创建一个 Expression
树来查询数据集。如果 属性 不是 List
,它可以工作,但我无法让它与 List
一起工作。我想要重新创建的是 expression
: productsQuery.Where(p => p.ProductEANs.Any(e => e.EAN.Contains("113")));
。这是我到目前为止的位置:
IQueryable<Product> productsQuery = _context.Products
.Include(p => p.ProductEANs);
foreach (KeyValuePair<string, string> searchField in searchFields)
{
if (!string.IsNullOrWhiteSpace(searchField.Value))
{
var parameterExpression = Expression.Parameter(typeof(Product), "product");
var constant = Expression.Constant(searchField.Value);
MethodCallExpression expression = null;
Expression property = parameterExpression;
foreach (var member in searchField.Key.Split('.'))
{
if (property.Type.IsGenericType &&
typeof(IEnumerable<>)
.MakeGenericType(property.Type.GetGenericArguments())
.IsAssignableFrom(property.Type))
{
var genType = property.Type.GenericTypeArguments.FirstOrDefault();
if (genType != null)
{
Expression subExp = Expression.Parameter(genType, "pEAN");
Expression subProp = Expression.PropertyOrField(subExp, member);
var expContains = Expression.Call(subProp, "Contains", null, constant);
var anyMethod = typeof(Enumerable).GetMethods().First(method => method.Name == "Any" && method.GetParameters().Length == 2).MakeGenericMethod(genType);
expression = Expression.Call(anyMethod, subProp, expContains);
}
}
else
{
property = Expression.PropertyOrField(property, member);
}
}
if (expression == null)
expression = Expression.Call(property, "Contains", null, constant);
var lambda = Expression.Lambda<Func<Product, bool>>(expression, parameterExpression);
productsQuery = productsQuery.Where(lambda);
}
}
productsQuery = productsQuery.OrderBy(p => p.Title);
var products = productsQuery.ToList().Select(p => (ProductDTO)p).ToList();
if (property.Type
的 if 部分中的所有内容...都是我在努力让它发挥作用。数据模型如下所示:
public class Product
{
public Guid Id {get;set;}
public string Tile {get;set;}
public List<ProductEAN> ProductEANs { get; set; }
}
public class ProductEAN
{
public string EAN { get; set; }
public decimal ProductCount { get; set; }
}
这应该可以解决问题:
public static class FilterExtensions
{
private readonly struct Node
{
public Node(ParameterExpression parameter, Expression body)
{
Parameter = parameter;
Body = body;
}
public ParameterExpression Parameter { get; }
public Expression Body { get; }
}
public static Expression<Func<T, bool>> BuildFilter<T>(string key, string value)
{
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
var p = Expression.Parameter(typeof(T), "p");
Expression body = p;
var stack = new Stack<Node>();
foreach (string member in key.Split('.'))
{
if (body.Type.IsGenericType)
{
var genericArgs = body.Type.GetGenericArguments();
if (genericArgs.Length == 1 && typeof(IEnumerable<>)
.MakeGenericType(genericArgs)
.IsAssignableFrom(body.Type))
{
stack.Push(new Node(p, body));
p = Expression.Parameter(genericArgs[0], "s" + stack.Count);
body = p;
}
}
body = Expression.PropertyOrField(body, member);
}
var constant = Expression.Constant(value, typeof(string));
body = Expression.Call(body, nameof(string.Contains), null, constant);
while (stack.Count != 0)
{
var childFilter = Expression.Lambda(body, p);
var parent = stack.Pop();
body = Expression.Call(typeof(Enumerable),
nameof(Enumerable.Any),
new[] { p.Type },
parent.Body,
childFilter);
p = parent.Parameter;
}
return Expression.Lambda<Func<T, bool>>(body, p);
}
}
用法:
IQueryable<Product> productsQuery = _context.Products
.Include(p => p.ProductEANs);
foreach (KeyValuePair<string, string> searchField in searchFields)
{
if (!string.IsNullOrWhiteSpace(searchField.Value))
{
var filter = FilterExtensions.BuildFilter<Product>(searchField.Key, searchField.Value);
productsQuery = productsQuery.Where(filter);
}
}