Why PropertyChanged is raised only after realm.Refresh() even if property value is changed immediately?


#1

I noticed that even if value is changed immediately in all managed realm object instances, PropertyChanged is raised after new transaction is opened or realm.Refresh() is called. Is that ok?

    public class Dog : RealmObject
    {
        private string Name_ { get; set; }

        public string Name
        {
            get => Name_;
            set => Name_ = value;
        }

        protected override void OnPropertyChanged(string propertyName)
        {
            switch (propertyName)
            {
                case nameof(Dog.Name_):
                    RaisePropertyChanged(nameof(Dog.Name));
                    break;
            }
        }
    }

    [TestFixture]
    public class DogTests
    {
        [Test]
        public void DogTest()
        {
            var config=new InMemoryConfiguration(Guid.NewGuid().ToString())
            {
                ObjectClasses = new[]
                {
                    typeof(Dog)
                }
            };
            var realm = Realm.GetInstance(config);

            Assert.That(realm.IsClosed, Is.False);

            var myDog=new Dog()
            {
                Name = "Rex"
            };
            realm.Write(()=>realm.Add(myDog));

            Assert.That(myDog.IsManaged, Is.True);

            var resultDog = realm.All<Dog>().FirstOrDefault();

            Assert.That(myDog.Name, Is.EqualTo(resultDog.Name)); 
            Assert.That(myDog, Is.Not.SameAs(resultDog));

            bool resultDogPropertyChangedFired = false;
            resultDog.PropertyChanged += (o, e) =>
            {
                if (e.PropertyName == nameof(Dog.Name))
                {
                    resultDogPropertyChangedFired = true;
                }
            };

            realm.Write(() => { myDog.Name = "Max"; });

            Assert.That(resultDog.Name, Is.EqualTo(myDog.Name)); 
            Assert.That(resultDogPropertyChangedFired, Is.False); 

            realm.Refresh();

            Assert.That(resultDogPropertyChangedFired, Is.True); 

     
        }
    }

#2

One more example. When object is modified from separate thread, instances are synced and PropertyChanged is raised same time, after realm.Refresh(). It seems to be expected, but I have one more question, how often realm refresh itself? Is there an way to control it?

        [Test]
        public void DogMultiThreadsTest()
        {
            var config = new InMemoryConfiguration(Guid.NewGuid().ToString())
            {
                ObjectClasses = new[]
                {
                    typeof(Dog)
                }
            };
            var realm = Realm.GetInstance(config);

            Assert.That(realm.IsClosed, Is.False);

            var myDog = new Dog()
            {
                Name = "Rex"
            };
            realm.Write(() => realm.Add(myDog));

            bool myDogPropertyChangedFired = false;
            myDog.PropertyChanged += (o, e) =>
            {
                if (e.PropertyName == nameof(Dog.Name))
                {
                    myDogPropertyChangedFired = true;
                }
            };

            bool realmChangedFired = false;
            realm.RealmChanged += (o, e) =>
            {
                realmChangedFired = true;
            };

            Task.Run(() =>
            {
                var realm2 = Realm.GetInstance(config);

                Assert.That(realm2.IsClosed, Is.False);
                Assert.That(realm2, Is.Not.SameAs(realm));

                var resultDog = realm2.All<Dog>().First();

                Assert.That(resultDog.Name,Is.EqualTo("Rex"));
                Assert.That(myDog, Is.Not.SameAs(resultDog));

                realm2.Write(() => { resultDog.Name = "Max"; });

                realm2.Refresh();

            }).Wait();
            
            Assert.That(realmChangedFired, Is.False);
            Assert.That(myDog.Name,Is.EqualTo("Rex"));
            Assert.That(myDogPropertyChangedFired, Is.False);

            realm.Refresh();

            Assert.That(realmChangedFired, Is.True);
            Assert.That(myDog.Name, Is.EqualTo("Max"));
            Assert.That(myDogPropertyChangedFired, Is.True);
        }

#3

On the main thread, the Realm refreshes whenever the thread is idle. In your tests if you replace realm.Refresh with await Task.Yield(), you’d see similar results. Here’s an example from our test suite: https://github.com/realm/realm-dotnet/blob/master/Tests/Tests.Shared/PropertyChangedTests.cs#L537