我对动态数据和一般的反应式扩展世界是新手,我目前面临以下问题,我想在运行时通过使用动态数据包来更改IObservable<Func<T,bool>>
谓词,从而在.NET(C#(中更改反应式扩展。
考虑到以下情况,我有一个DataGrid,其中一些列的类型为integer(比方说a、B、C(。此外,还有一个过滤器UI,用户可以在其中添加多个过滤器,如a==6或过滤器表达式的组合,如a==7||a==3||B==5等。因此,基本上,我返回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();
正如我上面提到的,用户应该能够添加/删除/修改,更重要的是,将过滤器表达式组合在一起。
由于动态的数据过滤器方法采用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));
}
现在,我的问题是,如何用一种更通用的方式来写这篇文章?如何组合例如0 to n
过滤器?不同的过滤器类型是什么,例如a<=7&;A!=5?
您可以使用这里显示的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 { }
限制:
未考虑
Func<T, bool>
等效性。您可能需要强键入每个筛选器函数,以确保能够正确地将其添加到筛选器的内部列表中/从中删除。没有考虑分组&;和||操作(即
(A == 7 && B == "Hello") || C == -1)
(。如果这很重要,你肯定需要强烈键入过滤器(根据1(并添加一个组标识符。然后,在对可观察对象执行Aggregate
之前,可以在列表中使用GroupBy
。
与ibeebs的答案相比,我还发现了另一种可能的解决方案。为了构建可能的过滤器组合,可以使用动态表达式库:https://github.com/zzzprojects/System.Linq.Dynamic/wiki/Dynamic-Expressions并创建这样的表达式:
Expression<Func<T, bool>> e2 = DynamicExpression.ParseLambda<T, bool>("Id == 7 or Id == 9");
或者更复杂的。
由于我在问题中提到用户可以通过UI创建过滤器,因此构建像"Id == 7 or Id == 9"
这样的字符串表达式也没什么大不了的。
这样的CCD_ 11可以被简单地编译为CCD_。