DynamicData icon indicating copy to clipboard operation
DynamicData copied to clipboard

DynamicData.MissingKeyException

Open pravin271 opened this issue 11 months ago • 2 comments

Describe the bug 🐞

Hello, first of all thank you for the great library. Handling data updates becomes easy after using this library.

I am having trouble fixing one of the exceptions that is coming when I delete one of the item from the source. I have a source collection of itineraries. Based on this collection, I am creating date headers and middle-view items between two itineraries and the footer at the bottom.

Upon updating the complete source collection (basically, reload) after modification, I am getting the following exception. This happens only when any of the itinerary item is deleted.

DynamicData.MissingKeyException: 4190954a-e2b7-483b-9e05-0581edc94e7c is missing from previous value on update. Object type Iratrips.Itineraries.ItemViewModels.BaseItineraryItem, Key type System.String, Group key type System.DateTime at DynamicData.Kernel.OptionExtensions.ValueOrThrow[T] (DynamicData.Kernel.Optional1[T] source, System.Func1[TResult] exceptionGenerator) [0x00033] in :0 at DynamicData.Cache.Internal.GroupOn3+Grouper+<>c__DisplayClass7_1[TObject,TKey,TGroupKey].<HandleUpdates>b__3 (DynamicData.ICacheUpdater2[TObject,TKey] groupUpdater) [0x000fc] in :0 at DynamicData.Cache.Internal.ReaderWriter2[TObject,TKey].DoUpdate (System.Action1[T] updateAction, System.Action1[T] previewHandler, System.Boolean collectChanges) [0x000d9] in <dbb0a3f2a5e94f98b0dc738801005b20>:0 at DynamicData.Cache.Internal.ReaderWriter2[TObject,TKey].Write (System.Action1[T] updateAction, System.Action1[T] previewHandler, System.Boolean collectChanges) [0x0000e] in :0 at DynamicData.ObservableCache2[TObject,TKey].UpdateFromIntermediate (System.Action1[T] updateAction) [0x00068] in :0 at DynamicData.IntermediateCache2[TObject,TKey].Edit (System.Action1[T] updateAction) [0x00000] in :0 at DynamicData.Cache.Internal.ManagedGroup3[TObject,TKey,TGroupKey].Update (System.Action1[T] updateAction) [0x00000] in :0 at DynamicData.Cache.Internal.GroupOn3+Grouper+<>c__DisplayClass7_0[TObject,TKey,TGroupKey].<HandleUpdates>b__2 (System.Linq.IGrouping2[TKey,TElement] group) [0x00056] in :0 at DynamicData.Kernel.EnumerableEx.ForEach[T] (System.Collections.Generic.IEnumerable1[T] source, System.Action1[T] action) [0x00010] in :0 at DynamicData.Cache.Internal.GroupOn3+Grouper[TObject,TKey,TGroupKey].HandleUpdates (System.Collections.Generic.IEnumerable1[T] changes, System.Boolean isRegrouping) [0x00055] in :0 at DynamicData.Cache.Internal.GroupOn3+Grouper[TObject,TKey,TGroupKey].Update (DynamicData.IChangeSet2[TObject,TKey] updates) [0x00000] in :0 at System.Reactive.Linq.ObservableImpl.Select`2+Selector+_[TSource,TResult].OnNext (TSource value) [0x00000] in <3ad0cc868be642ac8d0c22906bb12ddb>:0

var finalItinerariesObservable = connection
    .Sort(SortExpressionComparer<PlainItinerary>.Ascending(k => k.Sequence))
    .Transform((itinerary) =>
    {
           var item = new ItineraryItem(itinerary, itinerary.TripStartDateTime, place, showPhotos, creator, this)
           {
               SyncMode = null,
           };

           return (BaseItineraryItem)item;
    }, transformOnRefresh: true)
    
    .Group(k => k.Date.Date)
    .TransformMany(k =>
    {
        //Date Header
        BaseItineraryItem item = new ItineraryDateItem(k.Key, GuidUtil.GenerateGuid(k.Key.ToString(CultureInfo.InvariantCulture)), this);
        var obs = new ObservableCollection<BaseItineraryItem>() { item };
        var osbSet = obs.ToObservableChangeSet<BaseItineraryItem, string>(m => m.Id);

        //Gaps
        var gapObservable = k.Cache
            .Connect()
            .Transform(m =>
            {
                var item = ((ItineraryItem)m);
                var dt = item.Itinerary.GetGuestStartTime().Value.AddMilliseconds(1);
                var id = GuidUtil.GenerateGuid(item.Itinerary.ItineraryId + "nxt");
                var placeEmpty = !item.Itinerary.HasPlace();

                var gap = new ItineraryGapItem(item.Itinerary.Id, dt, id, this);
                return (BaseItineraryItem)gap;

            }, transformOnRefresh: true);

        var groupObservable = k.Cache
            .Connect();

        var disposable = groupObservable
            .Merge(osbSet)
            .Merge(gapObservable)
            .Bind(out var items)
            .Subscribe();

        Disposables.Add(disposable);

        return items;

    }, x => x.Id)
    .Merge(footerbSet)
    .Sort(SortExpressionComparer<BaseItineraryItem>.Ascending(item => item.Date));


   var disposable = finalItinerariesObservable
       .ObserveOn(MainThread.AppContext)
       .Bind(ItineraryItems)
       .Subscribe();

The modification is happening like below, One of item is deleted from modifiedItineraries collection,

_itineraries.Edit(source =>
{
    source.Clear();
    source.AddOrUpdate(modifiedItineraries);
});

Any help would be great. Thanks

Step to reproduce

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Reproduction repository

https://github.com/reactivemarbles/DynamicData

Expected behavior

This should happen...

Screenshots 🖼️

No response

IDE

Visual Studio 2022

Operating system

Android

Version

No response

Device

No response

DynamicData Version

7.1.16

Additional information ℹ️

..

pravin271 avatar Mar 20 '24 15:03 pravin271

Sorry, this is not a bug in the library. But the issue that I am facing.

pravin271 avatar Mar 20 '24 15:03 pravin271

I suspect the issue is because the standard rx merge operator does not work on changesets as it is not change set aware. You may need to use one of the combinator operators, perhaps even the new MergeManyChangeSets. It's hard to tell however as I am looking at this from my phone. I'll look in more details when I am back on my laptop.

RolandPheasant avatar Mar 20 '24 18:03 RolandPheasant