Как создать выражение фильтра структуры динамической сущности, такое как Expression ‹Func‹ T, bool ››

Я хочу создать динамические лямбда-выражения для фильтрации.

Я немного искал, но не смог найти полезного для дочерних коллекций. Итак, как я могу создать подобное выражение?

Expression<Func<ORDERS, bool>> filter1 = c => c.ORDER_DETAILS.Any(x => x.PRODUCTS.HEADING.Contains("foo"));

PS: Я задал похожий вопрос, но не получил правильного ответа. Поэтому я решил пойти с этого пути, если я не ошибаюсь.

Дополнительная информация по моему вопросу: (Как фильтровать дочернюю коллекцию с помощью linq dynamic < / а>)

Я пытаюсь отфильтровать результаты по запросу пользователя. Например, у вас есть заказы и детали заказа, а товары - дочерняя коллекция.

Когда пользователь хочет выполнить фильтрацию по продукту, я получаю сообщение об ошибке из-за того, что в типе ICollection1 нет свойства или поля «ПРОДУКТЫ».

Я пишу свой запрос вот так.

var orders = _uow.Repository () .Query () .Where ("PRODUCTS.HEADING.ToLower (). Contains (\" foo \ ")") .Include ("ORDER_DETAILS") .Include ("ORDER_DETAILS.PRODUCTS") .К списку(); Значит, невозможно отфильтровать дочернюю коллекцию таким образом? Или как-нибудь фильтровать?


person Kadir    schedule 11.07.2016    source источник
comment
Вы пробовали это? Почему не сработало?   -  person    schedule 11.07.2016
comment
@ Я хочу создать это динамически. На самом деле я задал подобный вопрос, но все еще не получил правильного ответа. stackoverflow.com/ questions / 38118300 /   -  person Kadir    schedule 11.07.2016
comment
динамический - очень широкое слово. Диапазон значений - от имя динамического столбца до невозможно. Уточните, пожалуйста, несколько примеров.   -  person Zein Makki    schedule 11.07.2016
comment
@Kadir, что еще хуже, посмотрите на это тогда stackoverflow.com/help/no-one-answers   -  person Zein Makki    schedule 11.07.2016
comment
@ user3185569 Я имел в виду, что кто-то ответил, и мы немного обсудим, но я все еще не получил правильного ответа.   -  person Kadir    schedule 11.07.2016
comment
@Kadir Если вы готовы предложить часть своей репутации за ответ, добавьте награду за свой вопрос, и вы получите ответ быстрее. Вот что я имел в виду.   -  person Zein Makki    schedule 11.07.2016


Ответы (1)


Да, ты можешь. В одном подходе, который я использовал, в качестве поискового фильтра используется объект, который совпадает с вашим типом возвращаемого значения. Итак, если вы хотите выполнить поиск по имени клиента "Билл", вы установите Order.Customer.Name на Билл. При передаче этого объекта методу применяются все применимые поисковые запросы.

Для этого начните с определения списка доступных для поиска полей:

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[] массив для построения выражений для ваших критериев и применения их к набору данных.

Надеюсь, это поможет! Дайте знать, если у вас появятся вопросы.

person Daniel    schedule 11.07.2016