我想创建一个用于过滤的动态 lambda 表达式。
我搜索了一点,但找不到对子集合有用的东西。那么我怎样才能创建这样的表达式呢?
Expression<Func<ORDERS, bool>> filter1 = c => c.ORDER_DETAILS.Any(x => x.PRODUCTS.HEADING.Contains("foo"));
PS:我问过类似的问题,但没有得到正确的答案。所以如果我没猜错的话,我决定从这个方向开始。
有关我的问题的更多信息:( How to filter child collection with linq dynamic )
I'm trying to filter results for user request. For instance you have orders and order details and products is child collection.
When user wants to filter by product I'm getting an error because of No property or field 'PRODUCTS' exists in type 'ICollection1'`
I'm writing my query like this.
var orders = _uow.Repository() .Query() .Where("PRODUCTS.HEADING.ToLower().Contains(\"foo\")") .Include("ORDER_DETAILS") .Include("ORDER_DETAILS.PRODUCTS") .ToList(); So it's not possible to filter child collection like this? Or any way to filter?
最佳答案
是的,可以。我使用的一种方法使用与您的返回类型相同的对象作为搜索过滤器。因此,如果您要搜索“Bill”的客户名称,则将 Order.Customer.Name
设置为 Bill。将该对象传递给方法,然后应用所有适用的搜索。
为此,首先定义可搜索字段的列表:
Field<Order>[] Fields;
通过声明新字段来填充这些内容:
var newField = new Field<Order>(o => o.Customer.Name, true, "Customer Name");
“true”参数意味着它将充当结果的排序字段。
Field
对象包含足够的信息以供稍后生成表达式。它看起来像这样:
public class Field<T>
{
public Field(Expression<Func<T, object>> field, bool sortField = false, string displayName = null)
{
//get & validate member
MemberExp = field.Body is UnaryExpression ? ((UnaryExpression)field.Body).Operand as MemberExpression
: (MemberExpression)field.Body;
Field = MemberExp?.Member;
if (Field == null) throw new ArgumentException("Field expression is not a member.");
//set field type
switch (Field.MemberType)
{
case MemberTypes.Property:
PropertyInfo p = (PropertyInfo)Field;
FieldType = p.PropertyType;
break;
case MemberTypes.Field:
FieldInfo f = (FieldInfo)Field;
FieldType = f.FieldType;
break;
default:
throw new Exception("Unsupported member type detected.");
}
//store input values
FieldExpression = field;
SortField = sortField;
DisplayName = displayName ?? Field.Name;
}
public bool SortField { get; set; }
public string DisplayName { get; private set; }
public MemberExpression MemberExp { get; private set; }
public Expression<Func<T, object>> FieldExpression { get; private set; }
public Func<T, object> GetValue => FieldExpression.Compile();
public Type FieldType { get; set; }
/// <summary>
/// Gets the full field name, i.e o => o.Customer.CustomerName returns "Customer.CustomerName"
/// </summary>
public string UnqualifiedFieldName
{
get
{
var stringExp = MemberExp.ToString();
var paramEnd = stringExp.IndexOf('.') + 1;
return stringExp.Substring(paramEnd);
}
}
}
定义所有可搜索字段后,您将调用一个方法来根据从用户收集的搜索过滤器 (T
) 获取搜索结果:
//get the results in ascending order, 10 items per page, first page
var results = GetSearchResults(searchFilters, "ASC", 10, 1);
该方法将要求您拥有可查询的数据集合。我假设您有一些方法,例如 context.GetCollection()
来检索您的数据。 GetSearchResults
方法如下所示:
//Returns a filtered dataset based on provided search filters
//searchFilters is an object T which contains the search filters entered.
private List<T> GetSearchResults(T searchFilters, string sortDir = "ASC", int pageSize, int currentPage)
{
IQueryable<T> searchResults = context.GetCollection(); //get your data context here
var filterExpressions = new List<Expression<Func<T, bool>>>();
//Add filters
foreach (var field in Fields)
{
//try to get the search value, ignoring null exceptions because it's much harder
//to check for null objects at multiple levels. Instead the exception tells us there's
//no search value
string searchValue = null;
try
{
searchValue = field.GetValue(searchFilters)?.ToString();
}
catch (NullReferenceException) { }
if (string.IsNullOrWhiteSpace(searchValue)) continue;
//shared expression setup
ParameterExpression param = field.FieldExpression.Parameters.First();
Expression left = field.FieldExpression.Body;
ConstantExpression right = Expression.Constant(searchValue);
Expression body = null;
//create expression for strings so we can use "contains" instead of "equals"
if (field.FieldType == typeof(string))
{
//build the expression body
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(left, method, right);
}
else
{ //handle expression for all other types
body = Expression.Equal(left, right);
}
//finish expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, param);
filterExpressions.Add(lambda);
}
//apply the expressions
searchResults = filterExpressions.Aggregate(searchResults, (current, expression) => current.Where(expression));
//get sort field
Field<T> sortField = Fields.FirstOrDefault(f => f.SortField);
searchResults = searchResults.OrderBy($"{sortField.UnqualifiedFieldName} {sortDir}");
// Get the search results
int count = searchResults.Count();
int maxPage = count / pageSize;
if (maxPage * pageSize < count) maxPage++;
if (currentPage > maxPage) currentPage = maxPage;
int skip = Math.Max(0, (filters.page - 1) * pageSize);
int display = Math.Max(0, Math.Min(count - skip, pageSize));
return searchResults.Skip(skip).Take(display).ToList();
}
此方法使用您的 Field[]
数组为您的条件构建表达式并将其应用到数据集。
希望有帮助!如果您有任何疑问,请告诉我。
关于c# - 如何创建动态 Entity Framework 过滤表达式,如 Expression<Func<T, bool>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38308354/