【问题标题】:How to create dynamic entity framework filter expression like Expression<Func<T, bool>>如何创建动态实体框架过滤器表达式,如 Expression<Func<T, bool>>
【发布时间】:2016-11-13 11:36:53
【问题描述】:

我想创建一个用于过滤的动态 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)

我正在尝试过滤用户请求的结果。例如你有 订单和订单详情和产品是子集合。

当用户想要按产品过滤时,我收到一个错误,因为 类型“ICollection1”中不存在属性或字段“PRODUCTS”`

我正在这样写我的查询。

var 订单 = _uow.Repository() 。询问() .Where("PRODUCTS.HEADING.ToLower().Contains(\"foo\")") .Include("ORDER_DETAILS") .Include("ORDER_DETAILS.PRODUCTS") .ToList();所以不可能像这样过滤子集合吗?或者有什么过滤方式?

【问题讨论】:

  • 你试过了吗?为什么它不起作用?
  • @Will 我想动态创建它。其实我问过类似的问题,但仍然没有正确的答案。 stackoverflow.com/questions/38118300/…
  • 动态是一个非常宽泛的词。它的范围从动态列名不可能。请更具体一些,也许是一些例子。
  • @Kadir 那更糟糕,看看这个然后stackoverflow.com/help/no-one-answers
  • @user3185569 我的意思是有人回答了,我们讨论了一下,但我仍然没有得到正确的答案。

标签: c# entity-framework linq lambda dynamic-linq


【解决方案1】:

是的,你可以。我使用的一种方法使用与您的返回类型相同的对象作为搜索过滤器。因此,如果要搜索“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[] 数组为您的条件构建表达式并将它们应用于数据集。

希望对您有所帮助!如果您有任何问题,请告诉我。

【讨论】:

    猜你喜欢
    • 2013-08-22
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 2010-10-02
    相关资源
    最近更新 更多