Uploaded image for project: 'Realm .NET SDK'
  1. Realm .NET SDK
  2. RNET-704

[Bug]: NotifyCollectionChangedAction.Replace and Reset is not fired on RealmCollection

      What happened?

      Hi, I've noticed a bug where RealmCollection doesn't behave in the same way as ObservableCollection.
      From what I've observed, RealmCollection doesn't fire Replace & Reset from INotifyCollectionChanged Interface, thus my app is not updating the UI correctly when I switched from ObservableCollection to RealmDb in my ViewModels. I've made a very simple example to prove my point.

      Repro steps

      Launch provided code snippet in console application.

      Version

      net5.0 for console app & Xamarin for my main project issue

      What SDK flavour are you using?

      Local Database only

      What type of application is this?

      Xamarin

      Client OS and version

      Android, iOS, ConsoleApp in .net core Console app for macOS

      Code snippets

      using System;
      using System.Collections.Generic;
      using System.Collections.ObjectModel;
      using System.Collections.Specialized;
      using System.IO;
      using System.Threading.Tasks;
      using Nito.AsyncEx;
      using Realms;
      
      namespace ConsoleApp3
      {
          public class Program
          {
              public static async Task Main(string[] args)
              {
                  ObservableCollectionTest();
                  AsyncContext.Run(RealmCollectionTest);
              }
      
              private static void ObservableCollectionTest()
              {
                  var item0 = new RealmItem { Id = 0 };
                  var item1 = new RealmItem { Id = 1 };
                  var item2 = new RealmItem { Id = 2 };
                  // add items to collection 
                  var colllection = new MyCollection(new[] { item0, item1 });
      
                  // listen for notification
                  var actions = new List<NotifyCollectionChangedAction>(5);
                  colllection.CollectionChanged += (sender, eventArgs) =>
                  {
                      actions.Add(eventArgs.Action);
                  };
      
                  colllection.Add(item2); // Add items
                  colllection.MoveItem(1, 2); // Move items
                  colllection.Remove(item0); // Remove items
                  colllection[1] = item2; // Replace items
                  colllection.Clear(); // Reset items
                  
                  Console.WriteLine($"Actions for ObservableCollection: {string.Join(", ", actions)}");
              }
      
              private static void RealmCollectionTest()
              {
                  var path = Path.Combine(Environment.CurrentDirectory, "realm.realm");
                  var realm = Realm.GetInstance(new RealmConfiguration(path));
      
                  var item0 = new RealmItem { Id = 0 };
                  var item1 = new RealmItem { Id = 1 };
                  var item2 = new RealmItem { Id = 2 };
                  var colllection = new RealmWithCollection { Id = "col" };
                  // add items to persistent cache & to collection
                  realm.Write(() =>
                  {
                      realm.RemoveAll();
                      realm.Add(item0);
                      realm.Add(item1);
                      realm.Add(item2);
                      realm.Add(colllection);
                      colllection.Items.Add(item0);
                      colllection.Items.Add(item1);
                  });
      
                  // listen for notification
                  var actions = new List<NotifyCollectionChangedAction>(5);
                  colllection.Items.AsRealmCollection().CollectionChanged += (sender, eventArgs) =>
                  {
                      actions.Add(eventArgs.Action);
                  };
      
                  realm.Write(() => colllection.Items.Add(item2)); // Add items
                  realm.Write(() => colllection.Items.Move(item1, 2)); // Move items
                  realm.Write(() => colllection.Items.Remove(item0)); // Remove items
                  realm.Write(() => colllection.Items[1] = item2); // Replace items
                  realm.Write(() => colllection.Items.Clear()); // Reset items
      
                  Console.WriteLine($"Actions for RealmCollection: {string.Join(", ", actions)}");
              }
      
              class MyCollection : ObservableCollection<RealmItem>
              {
                  public MyCollection(IEnumerable<RealmItem> collection) : base(collection) { }
                  public new void MoveItem(int oldIndex, int newIndex)
                  {
                      base.MoveItem(oldIndex, newIndex);
                  }
              }
              public class RealmWithCollection : RealmObject
              {
                  [PrimaryKey] public string Id { get; set; }
                  public IList<RealmItem> Items { get; }
              }
              public class RealmItem : RealmObject
              {
                  [PrimaryKey] public int Id { get; set; }
              }
          }
      }
      
      Unable to find source-code formatter for language: csproj. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      <Project Sdk="Microsoft.NET.Sdk">
      
          <PropertyGroup>
              <OutputType>Exe</OutputType>
              <TargetFramework>net5.0</TargetFramework>
          </PropertyGroup>
      
          <ItemGroup>
            <PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
            <PackageReference Include="Fody" Version="6.6.0">
              <PrivateAssets>all</PrivateAssets>
              <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
            </PackageReference>
            <PackageReference Include="Nito.AsyncEx.Context" Version="5.1.2" />
            <PackageReference Include="Realm" Version="10.9.0" />
            <PackageReference Include="Realm.Fody" Version="10.9.0">
              <PrivateAssets>all</PrivateAssets>
              <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
            </PackageReference>
          </ItemGroup>
      
      </Project>
      

      Relevant log output

      Unable to find source-code formatter for language: shell. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      Actions for ObservableCollection: Add, Move, Remove, Replace, Reset
      Actions for RealmCollection: Add, Move, Remove
      

      ^ ObservableCollection & RealmCollection should fire INotifyCollectionChanged events in the same way

            Assignee:
            ferdinando.papale@mongodb.com Ferdinando Papale
            Reporter:
            unitosyncbot Unito Sync Bot
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: