Как изменить предикаты IObservable Fun c во время выполнения в C# - PullRequest
1 голос
/ 15 апреля 2020

Я новичок в динамических c данных и в целом в мире реактивных расширений, и в настоящее время я сталкиваюсь со следующей проблемой, где я хотел бы изменить предикаты IObservable<Func<T,bool>> во время выполнения с помощью динамического c пакет данных и, следовательно, реактивные расширения в. NET (C#).

Учитывая следующую ситуацию, у меня есть DataGrid с некоторыми столбцами типа integer , допустим, A, B, C. Кроме того, имеется пользовательский интерфейс фильтра, в который пользователь может добавить несколько фильтров, например A == 6 , или комбинацию выражений фильтров, например A == 7 || A == 3 || B == 5 , et c. Таким образом, мой метод, возвращающий делегат Func<T, bool>, выглядит следующим образом:

private Func<T, bool> FilterOnA(string id)
{
    return n => n.Id == int.Parse(id);
}

И вызов метода Filter в конвейере данных:

    // sourceList is used to fill the ReadOnlyObservableCollection<T> while receiving data from an event pattern

    sourceList.Connect()                        // SourceList<T>
              .Filter(filterViewModel.FilterOnA)
              .Bind(out _itemsBinding)          // private ReadOnlyObservableCollection<T>
              .DisposeMany()
              .Subscribe();

Как я упоминал выше, пользователь должен быть в состоянии добавить / удалить / изменить и еще более важно объединить выражения фильтра все вместе.

Поскольку метод фильтра данных Dynami c принимает Func<T,bool> или IObservable<Func<T,bool>>, одно из возможных решений может выглядеть следующим образом:

public IObservable<Func<T,bool>> Filter1 {get;} 
public IObservable<Func<T,bool>> Filter2 {get;}
public IObservable<Func<T,bool>> Filter3 {get;}
public IObservable<Func<T,bool>> FilterX {get;}

public IObservable<Func<T,bool>> AllFiltersCombined => Filter1.CombineLatest(Filter2,Filter3,FilterX, (f1,f2,f3,fx) => AggregatePredicatesAnd(f1,f2,f3,fx));


public static Func<T,Bool> AggregatePredicatesAnd(params Func<T,bool>[] predicates) 
{
    return predicates.Aggregate<Func<T,bool>>((fa,fb) => (T t) => fa(t) && fb(t));
}

Теперь моя проблема есть, как написать это более обобщенным c способом? Как комбинировать например 0 to n Фильтры? А что касается различных типов фильтров, например, комбинация A <= 7 && A! = 5 </strong>?

Ответы [ 2 ]

1 голос
/ 30 апреля 2020

Вы можете использовать что-то вроде операции ApplyFilters, показанной здесь:

public static class Extensions
{
    public static List<T> Apply<T>(this List<T> list, Action<List<T>> action)
    {
        action(list);

        return list;
    }

    public static IObservable<T> ApplyFilters<T>(this IObservable<T> source, IObservable<Func<T, bool>> AddFilter, IObservable<Func<T, bool>> RemoveFilter)
    {
        // Project AddFilter to a func that adds a filter to a list of filters
        var adding = AddFilter.Select(func => (Action<List<Func<T, bool>>>)(list => list.Add(func)));

        // Project RemoveFilter to a func that removes a filter from a list of filters
        var removing = RemoveFilter.Select(func => (Action<List<Func<T, bool>>>)(list => list.Remove(func)));

        // Return an observable that...
        return Observable
            // ... merges the add and remove operations ...
            .Merge(adding, removing)
            // ... and applies them to an initially empty list ...
            .Scan(new List<Func<T, bool>>(), (list, change) => list.Apply(change))
            // ... and project every list change to a new observable
            // by applying all operations to the source observable ...
            .Select(list => list.Aggregate(source, (s, f) => s.Where(f)))
            // ... and finally subscribing to the new observable
            .Switch();
    }
}

public class ViewModel
{
    public IObservable<Item> _source;

    public IObservable<Func<Item, bool>> _addFilter;

    public IObservable<Func<Item, bool>> _removeFilter;

    public ViewModel()
    {
        FilteredItems = _source.ApplyFilters(_addFilter, _removeFilter);
    }

    public IObservable<Item> FilteredItems { get; }
}

public class Item  {  }

Ограничения:

  1. Не принималось во внимание эквивалентность Func<T, bool>. Возможно, вам придется строго ввести каждый фильтр fun c, чтобы убедиться, что он может быть правильно добавлен / удален из внутреннего списка фильтров.

  2. В отношении группировки && и || не принималось во внимание операции (т.е. (A == 7 && B == "Hello") || C == -1)). Если это важно, вам обязательно нужно будет строго ввести фильтры (за 1) и добавить идентификатор группы. Затем вы можете использовать GroupBy в Списке до выполнения Aggregate в наблюдаемом.

0 голосов
/ 30 апреля 2020

В отличие от ответа ibeebs, я также нашел другое возможное решение. Чтобы построить возможные комбинации фильтров, можно использовать Dynami c Выражения lib: https://github.com/zzzprojects/System.Linq.Dynamic/wiki/Dynamic-Expressions и создавать выражения, подобные этому:

Expression<Func<T, bool>> e2 = DynamicExpression.ParseLambda<T, bool>("Id == 7 or Id == 9");

или более сложные.

Поскольку в своем вопросе я упомянул, что пользователь может создавать фильтры с помощью пользовательского интерфейса, создание таких строковых выражений, как "Id == 7 or Id == 9", не составляет особого труда.

Такие Expression могут быть просто скомпилированы в Func<T, bool>.

...