< Summary

Information
Class: MyNet.Observable.DynamicDataExtensions
Assembly: MyNet.Observable
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Extensions/DynamicDataExtensions.cs
Tag: 323_28699572109
Line coverage
89%
Covered lines: 26
Uncovered lines: 3
Coverable lines: 29
Total lines: 145
Line coverage: 89.6%
Branch coverage
70%
Covered branches: 28
Total branches: 40
Branch coverage: 70%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
MergeManyEx(...)75%44100%
MergeManyEx(...)50%44100%
RemoveMany(...)100%88100%
GetAddedItems(...)0%7280%
GetRemovedItems(...)100%88100%
GetRemovedItems(...)100%44100%
SubscribeAll(...)100%11100%
SubscribeAll(...)100%210%
ObserveOnOptional(...)75%44100%

File(s)

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

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="DynamicDataExtensions.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections;
 9using System.Collections.Generic;
 10using System.ComponentModel;
 11using System.Linq;
 12using System.Reactive.Concurrency;
 13using System.Reactive.Linq;
 14using DynamicData;
 15using DynamicData.Binding;
 16using DynamicData.Kernel;
 17
 18#pragma warning disable IDE0130 // Namespace does not match folder structure
 19namespace MyNet.Observable;
 20#pragma warning restore IDE0130 // Namespace does not match folder structure
 21
 22public static class DynamicDataExtensions
 23{
 24    /// <summary>
 25    /// Like DynamicData <c>MergeMany</c>, but forwards a remove change set for inner items when an outer item is remove
 26    /// </summary>
 27    /// <remarks>
 28    /// <para>
 29    /// <paramref name="observableSelector"/> is invoked again when an outer item is removed to build the forwarded
 30    /// remove change set. For the same outer item it must return an observable that reflects the same inner items
 31    /// (no side effects, no new disposable resources that leak).
 32    /// </para>
 33    /// </remarks>
 34    [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Ju
 35    public static IObservable<IChangeSet<TDestination>> MergeManyEx<T, TDestination>(
 36        this IObservable<IChangeSet<T>> source,
 37        Func<T, IObservable<IChangeSet<TDestination>>> observableSelector)
 38        where T : notnull
 39        where TDestination : notnull
 940        => source == null
 941            ? throw new ArgumentNullException(nameof(source))
 942            : observableSelector == null
 943                ? throw new ArgumentNullException(nameof(observableSelector))
 944                : new MergeManyEx<T, TDestination>(source, observableSelector).Run();
 45
 46    /// <summary>
 47    /// Keyed variant of <see cref="MergeManyEx{T, TDestination}"/>.
 48    /// </summary>
 49    /// <remarks>
 50    /// <inheritdoc cref="MergeManyEx{T, TDestination}(IObservable{IChangeSet{T}}, Func{T, IObservable{IChangeSet{TDesti
 51    /// </remarks>
 52    [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Ju
 53    public static IObservable<IChangeSet<TDestination, TDestinationKey>> MergeManyEx<T, TKey, TDestination, TDestination
 54        this IObservable<IChangeSet<T, TKey>> source,
 55        Func<T, IObservable<IChangeSet<TDestination, TDestinationKey>>> observableSelector,
 56        Func<TDestination, TDestinationKey> observableKeySelector)
 57        where T : notnull
 58        where TDestination : notnull
 59        where TKey : notnull
 60        where TDestinationKey : notnull
 661        => source == null
 662            ? throw new ArgumentNullException(nameof(source))
 663            : observableSelector == null
 664                ? throw new ArgumentNullException(nameof(observableSelector))
 665                : new MergeManyEx<T, TKey, TDestination, TDestinationKey>(source, observableSelector, observableKeySelec
 66
 67    public static void RemoveMany<T>(this ICollection<T> source, IEnumerable<T> itemsToRemove)
 68    {
 2469        ArgumentNullException.ThrowIfNull(source);
 70
 2171        ArgumentNullException.ThrowIfNull(itemsToRemove);
 72
 1573        var toRemoveArray = itemsToRemove.ToList();
 74
 75        // match all indices and remove in reverse as it is more efficient
 1576        var toRemove = source.IndexOfMany(toRemoveArray)
 1577            .OrderByDescending(x => x.Index)
 1578            .ToList();
 79
 80        // if there are duplicates, it could be that an item exists in the
 81        // source collection more than once - in that case the fast remove
 82        // would remove each instance
 1583        var hasDuplicates = toRemove.Duplicates(t => t.Item).Any();
 84
 1585        if (!hasDuplicates && source is IList list)
 86        {
 87            // Fast remove because we know the index of all, and we remove in order
 688            toRemove.ForEach(t => list.RemoveAt(t.Index));
 89        }
 90        else
 91        {
 92            // Slow remove but safe
 993            toRemoveArray.ForEach(t => source.Remove(t));
 94        }
 995    }
 96
 97    extension<T>(IChangeSet<T> changes)
 98        where T : notnull
 99    {
 100        /// <summary>Gets items added in this change set (single add and add range).</summary>
 0101        public IEnumerable<T> GetAddedItems() => changes.Where(y => y.Reason == ListChangeReason.Add).Select(z => z.Item
 102
 103        /// <summary>Gets items removed in this change set (single remove and remove range).</summary>
 15104        public IEnumerable<T> GetRemovedItems() => changes.Where(y => y.Reason == ListChangeReason.Remove).Select(z => z
 105    }
 106
 107    extension<T, TKey>(IChangeSet<T, TKey> changes)
 108        where T : notnull
 109        where TKey : notnull
 110    {
 111        /// <summary>Gets outer items removed in this keyed change set (each <see cref="ChangeReason.Remove"/> entry).</
 21112        public IEnumerable<T> GetRemovedItems() => changes.Where(c => c.Reason == ChangeReason.Remove).Select(c => c.Cur
 113    }
 114
 115    /// <summary>
 116    /// Invokes <paramref name="action"/> when the change set updates and when any item raises
 117    /// <see cref="INotifyPropertyChanged.PropertyChanged"/>.
 118    /// </summary>
 119    /// <remarks>
 120    /// <paramref name="action"/> runs once immediately when the outer subscription receives a change set, and again
 121    /// for each item property change. After the initial bind, expect at least one call per batch of changes.
 122    /// </remarks>
 123    public static IDisposable SubscribeAll<T>(this IObservable<IChangeSet<T>> source, Action action)
 124        where T : INotifyPropertyChanged
 3125        => source.SubscribeMany(x => x.WhenAnyPropertyChanged().Subscribe(_ => action()))
 3126            .Subscribe(_ => action());
 127
 128    /// <inheritdoc cref="SubscribeAll{T}(IObservable{IChangeSet{T}}, Action)"/>
 129    public static IDisposable SubscribeAll<T, TKey>(this IObservable<IChangeSet<T, TKey>> source, Action action)
 130        where T : INotifyPropertyChanged
 131        where TKey : notnull
 0132        => source.SubscribeMany(x => x.WhenAnyPropertyChanged().Subscribe(_ => action()))
 0133            .Subscribe(_ => action());
 134
 135    /// <summary>
 136    /// Like ObserveOn(IScheduler), but if <paramref name="scheduler"/> is null, returns the source observable without o
 137    /// </summary>
 138    /// <param name="source">The source observable sequence.</param>
 139    /// <param name="scheduler">The scheduler to observe on, or null to observe on the current thread.</param>
 140    /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
 141    /// <returns>An observable sequence that observes on the specified scheduler, or the source sequence if the schedule
 142    /// <exception cref="ArgumentNullException">Thrown if the source is null.</exception>
 564143    public static IObservable<TSource> ObserveOnOptional<TSource>(this IObservable<TSource> source, IScheduler? schedule
 144}
 145