我在常见的情况下工作,我希望访问存储库的子集,而不用担心必须保持更新,例如'获得价格大于10的所有订单。我已经实现了一个解决方案,但它有两个问题(在最后列出)。
可以使用与
等效的东西来实现存储库的子集 var expensiveOrders = Repository.GetOrders().Where(o => o.Price > 10);
但这是IEnumerable
,并且在更新原始集合时不会更新。我可以为CollectionChanged
添加处理程序,但是如果我们想要访问另一个子集呢?
var expensiveOrdersFromBob = expensiveOrders.Where(o => o.Name == Bob);
我们还必须为这个收集更改集合。实时更新的概念让我想到了Rx,所以我开始构建一个 ObservableCache ,它包含自动更新自身的ObservableCollection
个项目和一个用于通知的RX流。 (流也是更新缓存下的缓存。)
class ObservableCache<T> : IObservableCache<T>
{
private readonly ObservableCollection<T> _cache;
private readonly IObservable<Tuple<T, CRUDOperationType>> _updates;
public ObservableCache(IEnumerable<T> initialCache
, IObservable<Tuple<T, CRUDOperationType>> currentStream, Func<T, bool> filter)
{
_cache = new ObservableCollection<T>(initialCache.Where(filter));
_updates = currentStream.Where(tuple => filter(tuple.Item1));
_updates.Subscribe(ProcessUpdate);
}
private void ProcessUpdate(Tuple<T, CRUDOperationType> update)
{
var item = update.Item1;
lock (_cache)
{
switch (update.Item2)
{
case CRUDOperationType.Create:
_cache.Add(item);
break;
case CRUDOperationType.Delete:
_cache.Remove(item);
break;
case CRUDOperationType.Replace:
case CRUDOperationType.Update:
_cache.Remove(item); // ToDo: implement some key-based equality
_cache.Add(item);
break;
}
}
}
public ObservableCollection<T> Cache
{
get { return _cache; }
}
public IObservable<T> Updates
{
get { return _updates.Select(tuple => tuple.Item1); }
}
public IObservableCache<T> Where(Func<T, bool> predicate)
{
return new ObservableCache<T>(_cache, _updates, predicate);
}
}
然后您可以像这样使用它:
var expensiveOrders = new ObservableCache<Order>(_orders
, updateStream
, o => o.Price > 10);
expensiveOrders.Updates.Subscribe
(o => Console.WriteLine("Got new expensive order: " + o));
_observableBoundToSomeCtrl = expensiveOrders.Cache;
var expensiveOrdersFromBob = expensiveOrders
.Where(o => o.Name == "Bob");
expensiveOrdersFromBob.Updates.Subscribe
(o => Console.WriteLine("Got new expensive order from Bob: " + o));
_observableBoundToSomeOtherCtrl = expensiveOrdersFromBob.Cache;
等等,这个想法是你可以继续将缓存投射到更窄更窄的子集中,而不必担心它不同步。那么我的问题是什么呢?
ISequenceableItem
接口。有没有更好的方法来做到这一点? RX非常棒,因为它可以为您处理所有线程。我想利用它。 答案 0 :(得分:1)
http://github.com/wasabii/OLinq的OLinq项目是为这种反应性更新而设计的,我认为ObservableView就是你所追求的目标。
答案 1 :(得分:1)
看看这两个项目,它们可以通过不同的方式实现您想要的目标:
https://github.com/RolandPheasant/DynamicData
https://bitbucket.org/mendelmonteiro/reactivetables [免责声明:这是我的项目]
答案 2 :(得分:1)
假设您有这样的定义:
class SetOp<T>
{
public T Value { get; private set; }
public bool Include { get; private set; }
public SetOp(T value, bool include)
{
Value = value;
Include = include;
}
}
使用Observable.Scan
和System.Collections.Immutable
,您可以执行以下操作:
IObservable<SetOp<int>> ops = ...;
IImmutableSet<int> empty = ImmutableSortedSet<int>.Empty;
var observableSet = ops
.Scan(empty, (s, op) => op.Include ? s.Add(op.Value) : s.Remove(op.Value))
.StartWith(empty);
使用不可变集合类型是关键技巧:observableSet
的任何观察者都可以使用推送它的值执行任何操作,因为它们是不可变的。添加它是有效的,因为它在连续值之间重用了大部分设置数据结构。
以下是ops
信息流和相应的observableSet
。
ops observableSet
-------- ------------------
{}
Add 7 {7}
Add 4 {4,7}
Add 5 {4,5,7}
Add 6 {4,5,6,7}
Remove 5 {4,6,7}
Add 8 {4,6,7,8}
Remove 4 {6,7,8}
答案 3 :(得分:0)
您不需要在_cache
内锁定ProcessUpdate
。如果您的源可观察currentStream
符合Rx指南,则您保证一次只能在OnNext
内进行一次调用。换句话说,当您仍在处理之前的值时,您将不会从流中收到其他值。
解决竞争条件的唯一可靠方法是确保在updateStream
开始生成数据之前创建缓存。
您可能需要查看Extensions for Reactive Extensions (Rxx)。我相信Dave已经构建了许多用于将UI控件绑定到可观察数据的实用程序。文档很少。我不知道你有什么东西在做什么。