Extending the InspectCollectionWithObservable example to nested observable collections #973
Replies: 2 comments
-
After sleeping on this problem, I've refactored this example to maintain a single cache (instead of distributing it among many For others looking to achieve a similar goal, the refactored example is: using System.Reactive.Disposables;
using System.Reactive.Subjects;
using DynamicData;
var rootObservable =
new BehaviorSubject<Dictionary<Parent, Dictionary<Child, int>>>(new Dictionary<Parent, Dictionary<Child, int>>());
var parents = new SourceList<Parent>();
parents.Connect().SubscribeMany(p =>
{
rootObservable.Value[p] = new Dictionary<Child, int>();
return new CompositeDisposable(
p.Children.Connect().SubscribeMany(c => new CompositeDisposable(
c.Value.Subscribe(v =>
{
rootObservable.Value[p][c] = v;
rootObservable.OnNext(rootObservable.Value);
}),
Disposable.Create(() =>
{
rootObservable.Value[p].Remove(c);
rootObservable.OnNext(rootObservable.Value);
}))
).Subscribe(),
Disposable.Create(() =>
{
rootObservable.Value.Remove(p);
rootObservable.OnNext(rootObservable.Value);
})
);
}).Subscribe();
rootObservable.Subscribe(cache =>
Console.WriteLine(string.Concat("Cache: ",
string.Join(',', cache.Values.Select(v => $"[{string.Join(',', v.Values)}]"))))
);
var parent1 = new Parent();
parent1.AddChild(new Child(1));
parent1.AddChild(new Child(2));
parent1.AddChild(new Child(3));
var parent2 = new Parent();
parent2.AddChild(new Child(4));
parent2.AddChild(new Child(5));
parent2.AddChild(new Child(6));
parents.Add(parent1);
parents.Add(parent2);
var child7 = new Child(7);
parent2.AddChild(child7);
child7.ChangeValue(8);
parents.Remove(parent1);
internal record Parent
{
public readonly SourceList<Child> Children = new();
public void AddChild(Child child) => Children.Add(child);
}
internal class Child(int value)
{
public BehaviorSubject<int> Value { get; } = new(value);
public void ChangeValue(int value) => Value.OnNext(value);
} The output is:
I suspect there might be further optimisations available by using a |
Beta Was this translation helpful? Give feedback.
-
The thing you'll get with public class ItemViewModel
{
public long Id { get; init; }
public long? ParentId { get; init; }
public string Name { get; init; }
public IObservable<int> Data { get; }
}
using var items = new SourceCache<ItemViewModel, long>();
var parentItems = items
.Connect()
.TransformToTree(static item => item.ParentId)
.AsObservableCache();
using var subscription = Observable.Merge(
parentItems
.Connect()
.Transform(...)
.ToCollection(),
parentItems
.Connect()
.Transform(static parent => parent.Item.Children)
.Transform(...)
.ToCollection())
.Subscribe(...); Something to this effect, I think. It's probably not directly applicable to your scenario, but maybe it gives you some new options. |
Beta Was this translation helpful? Give feedback.
-
I am trying to extend the InspectCollectionWithObservable example to nested collections and have encountered a couple of issues that are likely not bugs, just my misunderstanding of how to implement the pattern.
My goal is to have a root object that has a
SourceList
of parent objects that each have aSourceList
of child objects. Each child object has an observable. When a new value is observed in a child's observable (or children are added and removed to the parent collection), I'd like to produce an enumerable of the latest observed values in each child. Similarly, as parents are added and removed from the rootSourceList
, I'd like to perform some action with an enumerable of the parent objects and their enumerable of their children's values.An illustration of my goal:
The expected output is:
This code has a couple of problems:
ArgumentOutOfRangeException
.child7
is added to a parent collection and then its value is changed, the cached values in eachChildObserver
are never populated (i.e. thechild.Value.Do(v => Value = v)
code never runs).What insight have I missed here? Or is there a better way to achieve my goal?
Many thanks.
Beta Was this translation helpful? Give feedback.
All reactions