< Summary

Information
Class: MyNet.Observable.MergeManyEx<T1, T2, T3, T4>
Assembly: MyNet.Observable
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Extensions/MergeManyEx.cs
Tag: 323_28699572109
Line coverage
100%
Covered lines: 27
Uncovered lines: 0
Coverable lines: 27
Total lines: 103
Line coverage: 100%
Branch coverage
50%
Covered branches: 3
Total branches: 6
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%66100%
Run()100%11100%
ForwardWhenRemove(...)100%11100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Extensions/MergeManyEx.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="MergeManyEx.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Linq;
 9using System.Reactive.Linq;
 10using DynamicData;
 11
 12#pragma warning disable IDE0130 // Namespace does not match folder structure
 13namespace MyNet.Observable;
 14#pragma warning restore IDE0130 // Namespace does not match folder structure
 15
 16/// <summary>
 17/// Forwards inner change sets when outer items are added and synthesizes remove change sets when outer items are remove
 18/// </summary>
 19/// <remarks>
 20/// <paramref name="observableSelector"/> is invoked again on outer remove; it must return the same inner snapshot
 21/// for the same outer item (no side effects, stable child collection).
 22/// </remarks>
 23internal sealed class MergeManyEx<T, TDestination>(IObservable<IChangeSet<T>> source,
 24    Func<T, IObservable<IChangeSet<TDestination>>> observableSelector)
 25    where T : notnull
 26    where TDestination : notnull
 27{
 28    private readonly IObservable<IChangeSet<T>> _source = source ?? throw new ArgumentNullException(nameof(source));
 29    private readonly Func<T, IObservable<IChangeSet<TDestination>>> _observableSelector = observableSelector ?? throw ne
 30
 31    public IObservable<IChangeSet<TDestination>> Run() => System.Reactive.Linq.Observable.Create<IChangeSet<TDestination
 32        observer =>
 33        {
 34            var locker = new object();
 35            return _source
 36
 37                // SubscribeMany will forward initial cascaded inner items when it observers new item,
 38                // but if one item was removed, any item belonged to it won't be forwarded to observer.
 39                .SubscribeMany(t => _observableSelector(t).Synchronize(locker).Subscribe(observer.OnNext))
 40
 41                // So I add an observer here to subscribe all items belonged to the removed item, buildup a
 42                // ChangeSet and forward it to the original observer.
 43                .Subscribe(t =>
 44                {
 45                    foreach (var item in t.GetRemovedItems()) ForwardWhenRemove(observer, item);
 46                },
 47                observer.OnError);
 48        });
 49
 50    private void ForwardWhenRemove(IObserver<IChangeSet<TDestination>> observer, T sourceItem)
 51    {
 52        var observableList = _observableSelector(sourceItem).AsObservableList();
 53        var changeset = new ChangeSet<TDestination>(
 54        [
 55            new(ListChangeReason.RemoveRange, observableList.Items)
 56        ]);
 57        observableList.Dispose();
 58        observer.OnNext(changeset);
 59    }
 60}
 61
 62/// <inheritdoc cref="MergeManyEx{T, TDestination}"/>
 63internal sealed class MergeManyEx<T, TKey, TDestination, TDestinationKey>(IObservable<IChangeSet<T, TKey>> source,
 64    Func<T, IObservable<IChangeSet<TDestination, TDestinationKey>>> observableSelector,
 65    Func<TDestination, TDestinationKey> observableKeySelector)
 66    where T : notnull
 67    where TDestination : notnull
 68    where TKey : notnull
 69    where TDestinationKey : notnull
 70{
 671    private readonly IObservable<IChangeSet<T, TKey>> _source = source ?? throw new ArgumentNullException(nameof(source)
 672    private readonly Func<T, IObservable<IChangeSet<TDestination, TDestinationKey>>> _observableSelector = observableSel
 673    private readonly Func<TDestination, TDestinationKey> _observableKeySelector = observableKeySelector ?? throw new Arg
 74
 675    public IObservable<IChangeSet<TDestination, TDestinationKey>> Run() => System.Reactive.Linq.Observable.Create<IChang
 676        observer =>
 677        {
 678            var locker = new object();
 679            return _source
 680
 681                // SubscribeMany will forward initial cascaded inner items when it observers new item,
 682                // but if one item was removed, any item belonged to it won't be forwarded to observer.
 683                .SubscribeMany(t => _observableSelector(t).Synchronize(locker).Subscribe(observer.OnNext))
 684
 685                // So I add an observer here to subscribe all items belonged to the removed item, buildup a
 686                // ChangeSet and forward it to the original observer.
 687                .Subscribe(t =>
 688                {
 689                    foreach (var item in t.GetRemovedItems()) ForwardWhenRemove(observer, item);
 690                },
 691                observer.OnError);
 692        });
 93
 94    private void ForwardWhenRemove(IObserver<IChangeSet<TDestination, TDestinationKey>> observer, T sourceItem)
 95    {
 996        var observableList = _observableSelector(sourceItem).AsObservableCache();
 997        var changeset = new ChangeSet<TDestination, TDestinationKey>();
 998        changeset.AddRange(observableList.Items.Select(x => new Change<TDestination, TDestinationKey>(ChangeReason.Remov
 999        observableList.Dispose();
 9100        observer.OnNext(changeset);
 9101    }
 102}
 103