例如,我有一些可观察的集合,它指示对象的状态(我通过 REST API 定期获取它)。
class User
{
int Id { get; }
string Name { get; }
string Status { get; }
}
IObservable<User> source;
我想创建一个
DynamicCache
对象并在每次 source
给出新结果时更新它。所以我写道:
var models = new SourceCache<User,int>(user => user.Id);
models.Connect()
.Transform(u => new UserViewModel() {...})
...
.Bind(out viewModels)
.Subscribe();
source.Subscribe(ul => models.EditDiff(ul, (a, b) => a.Status == b.Status));
但现在每次用户更改其状态时,
.Transform(...)
方法都会创建 UserViewModel
的新实例,这不是所需的行为。
当具有相同 Id 的源项发生更改时,我是否可以以某种方式确定更新现有 ViewModel 属性(在派生集合中)的规则,而不是每次都创建一个新属性?
答案是您需要创建一个自定义运算符。我在这里发布了一个要点 TransformWithInlineUpdate 您可以将其复制到您的解决方案中。用法示例是:
var users = new SourceCache<User, int>(user => user.Id);
var transformed = users.Connect()
.TransformWithInlineUpdate(u => new UserViewModel(u), (previousViewModel, updatedUser) =>
{
previousViewModel.User = updatedUser;
});
为了答案的完整性,这里是代码:
public static IObservable<IChangeSet<TDestination, TKey>> TransformWithInlineUpdate<TObject, TKey, TDestination>(this IObservable<IChangeSet<TObject, TKey>> source,
Func<TObject, TDestination> transformFactory,
Action<TDestination, TObject> updateAction = null)
{
return source.Scan((ChangeAwareCache<TDestination, TKey>)null, (cache, changes) =>
{
//The change aware cache captures a history of all changes so downstream operators can replay the changes
if (cache == null)
cache = new ChangeAwareCache<TDestination, TKey>(changes.Count);
foreach (var change in changes)
{
switch (change.Reason)
{
case ChangeReason.Add:
cache.AddOrUpdate(transformFactory(change.Current), change.Key);
break;
case ChangeReason.Update:
{
if (updateAction == null) continue;
var previous = cache.Lookup(change.Key)
.ValueOrThrow(()=> new MissingKeyException($"{change.Key} is not found."));
//callback when an update has been received
updateAction(previous, change.Current);
//send a refresh as this will force downstream operators to filter, sort, group etc
cache.Refresh(change.Key);
}
break;
case ChangeReason.Remove:
cache.Remove(change.Key);
break;
case ChangeReason.Refresh:
cache.Refresh(change.Key);
break;
case ChangeReason.Moved:
//Do nothing !
break;
}
}
return cache;
}).Select(cache => cache.CaptureChanges()); //invoke capture changes to return the changeset
}
更新:这已包含在库中。