Skip to content

[Bug]: ArgumentException: An item with the same key was already added with Filter on 9.4.31+ #1129

Description

@mycroes

Describe the bug 🐞

I ran into the following exception in my application:

System.ArgumentException: An item with the same key has already been added. Key: GroupId { Value = 2047 }
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at DynamicData.ChangeAwareCache`2.Add(TObject item, TKey key) in /_/src/DynamicData/Cache/ChangeAwareCache.cs:line 75
   at DynamicData.Cache.Internal.Filter.Static`2.<>c__DisplayClass0_1.<Create>b__1(IChangeSet`2 upstreamChanges) in /_/src/DynamicData/Cache/Internal/Filter.Static.cs:line 36
   at System.Reactive.Linq.ObservableImpl.Select`2.Selector._.OnNext(TSource value)

After investigation it appears in DynamicData 9.4.31 and newer and only seems to happen when multiple SourceCaches, multiple subscriptions and multiple schedulers are applied.

Step to reproduce

using System.Reactive.Linq;
using DynamicData;
using Microsoft.Reactive.Testing;

namespace FilterTests;

public class FilterTests
{
    private static TestScheduler scheduler = new TestScheduler();

    [Fact]
    public async Task Filter_Throws_ArgumentException()
    {
        var viewModels = new SourceCache<GroupViewModel, int>(x => x.Group.Id);
        var groups = new SourceCache<Group, int>(x => x.Id);

        viewModels.Connect().Subscribe();

        groups.Connect()
            .Subscribe(changes => {
                viewModels.Edit(inner => {
                    foreach (var change in changes) {
                        switch (change.Reason)
                        {
                            case ChangeReason.Add:
                                inner.AddOrUpdate(new GroupViewModel(change.Current, viewModels));
                                break;
                        }
                    }
                });
            });

        groups.Edit(inner => inner.AddOrUpdate([
                new (1, 0),
                new (0, null)
            ]));

        scheduler.AdvanceBy(10);
    }

    private record Group(int Id, int? Parent);

    private class GroupViewModel
    {
        public Group Group { get; }

        public GroupViewModel(Group group, SourceCache<GroupViewModel, int> masterList)
        {
            Group = group;

            masterList
                .Connect()
                .Filter(n => n.Group.Parent == group.Id)
                .ObserveOn(scheduler)
                .Subscribe();
        }
    }
}

Reproduction repository

See above.

Expected behavior

Should not throw ArgumentException.

DynamicData Version

9.4.31

Additional information ℹ️

Took me over 8 hours to get this reproduced, I hope you guys fix this once and for all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions