From a07621c6034d9532d963d424aa1e1a3d2140f4a4 Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Fri, 24 Feb 2023 14:14:16 +0100 Subject: [PATCH] [v11] 3. Remove obsolete WriteAsync overloads (#3234) --- CHANGELOG.md | 16 ++ Realm/Realm/Realm.cs | 257 ------------------ Tests/Realm.Tests/Database/AsyncTests.cs | 81 ------ Tests/Realm.Tests/Database/InstanceTests.cs | 5 +- .../Database/PropertyChangedTests.cs | 28 +- Tests/Realm.Tests/Database/WriteOverloads.cs | 190 ------------- 6 files changed, 36 insertions(+), 541 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7848a5ab3..9c5d005615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ * `RealmObjectBase.DynamicApi.CreateObject(string)` can be used to create an object without a primary key. * `RealmObjectBase.DynamicApi.CreateObject(string, string/long?/ObjectId?/Guid?)` can be used to create an object with a primary key of the corresponding type. * The API exposed by `Realm.DynamicApi` no longer return `dynamic`, instead opting to return concrete types, such as `IRealmObject`, `IEmbeddedObject`, and so on. You can still cast the returned objects to `dynamic` and go through the dynamic API, but that's generally less performant than using the string-based API, such as `IRealmObjectBase.DynamicApi.Get/Set`, especially on AOT platforms such as iOS or Unity. (Issue [#2391](https://github.com/realm/realm-dotnet/issues/2391)) +* Removed `Realm.WriteAsync(Action)` in favor of `Realm.WriteAsync(Action)`. The new `WriteAsync` method introduced in 10.14.0 is more efficient and doesn't require reopening the Realm on a background thread. While not recommended, if you prefer to get the old behavior, you can write an extension method like: + ```csharp + public static async Task WriteAsync(this Realm realm, Action writeAction) + { + await Task.Run(() => + { + using var bgRealm = Realm.GetInstance(realm.Config); + bgRealm.Write(() => + { + writeAction(bgRealm); + }); + }); + + await realm.RefreshAsync(); + } + ``` ### Enhancements diff --git a/Realm/Realm/Realm.cs b/Realm/Realm/Realm.cs index 376a6057ca..b6bbb76433 100644 --- a/Realm/Realm/Realm.cs +++ b/Realm/Realm/Realm.cs @@ -887,54 +887,6 @@ public async Task BeginWriteAsync(CancellationToken cancellationTok return _activeTransaction = new Transaction(this); } - /// - /// Execute an action inside a temporary on a worker thread, if called from UI thread. If no exception is thrown, - /// the will be committed. - /// - /// - /// Opens a new instance of this Realm on a worker thread and executes inside a write . - /// s and s/s are thread-affine, so capturing any such objects in - /// the action delegate will lead to errors if they're used on the worker thread. Note that it checks the - /// to determine if Current is null, as a test to see if you are on the UI thread - /// and will otherwise just call Write without starting a new thread. So if you know you are invoking from a worker thread, just call Write instead. - /// - /// - /// - /// await realm.WriteAsync(tempRealm => - /// { - /// var pongo = tempRealm.All<Dog>().Single(d => d.Name == "Pongo"); - /// var missis = tempRealm.All<Dog>().Single(d => d.Name == "Missis"); - /// for (var i = 0; i < 15; i++) - /// { - /// tempRealm.Add(new Dog - /// { - /// Breed = "Dalmatian", - /// Mum = missis, - /// Dad = pongo - /// }); - /// } - /// }); - /// - /// Note that inside the action, we use tempRealm. - /// - /// - /// Action to execute inside a , creating, updating, or removing objects. - /// - /// An awaitable . - [Obsolete("Use Realm.WriteAsync(Action action) instead.")] - public Task WriteAsync(Action action) - { - ThrowIfDisposed(); - - Argument.NotNull(action, nameof(action)); - - return WriteAsync(tempRealm => - { - action(tempRealm); - return true; - }); - } - /// /// Execute an action inside a temporary . If no exception is thrown, the will be committed. /// If the method is not called from a thread with a (like the UI thread), it behaves synchronously. @@ -970,70 +922,6 @@ public Task WriteAsync(Action action, CancellationToken cancellationToken = defa }, cancellationToken); } - /// - /// Execute a delegate inside a temporary on a worker thread, if called from UI thread. If no exception is thrown, - /// the will be committed. - /// - /// - /// Opens a new instance of this Realm on a worker thread and executes inside a write . - /// s and s/s are thread-affine, so capturing any such objects in - /// the action delegate will lead to errors if they're used on the worker thread. Note that it checks the - /// to determine if Current is null, as a test to see if you are on the UI thread - /// and will otherwise just call Write without starting a new thread. So if you know you are invoking from a worker thread, just call Write instead. - /// - /// - /// - /// var dog = await realm.WriteAsync(tempRealm => - /// { - /// return tempRealm.Add(new Dog - /// { - /// Breed = "Dalmatian", - /// }); - /// }); - /// - /// Note that inside the action, we use tempRealm. - /// - /// - /// Delegate with one return value to execute inside a , creating, updating, or removing objects. - /// - /// The type returned by the input delegate. - /// An awaitable with return type . - [Obsolete("Use Realm.WriteAsync(Func function) instead.")] - public async Task WriteAsync(Func function) - { - ThrowIfDisposed(); - - Argument.NotNull(function, nameof(function)); - - // If running on background thread, execute synchronously. - if (!AsyncHelper.TryGetValidContext(out _)) - { - return Write(() => function(this)); - } - - // If we are on UI thread the SynchronizationContext will be set (often also set on long-lived workers to use Post back to UI thread). - var result = await Task.Run(() => - { - using var realm = GetInstance(Config); - var writeAction = realm.Write(() => function(realm)); - if (writeAction is IRealmObjectBase rob && rob.IsManaged && rob.IsValid) - { - return (object)ThreadSafeReference.Create(rob); - } - - return writeAction; - }); - - await RefreshAsync(); - - if (result is ThreadSafeReference.Object tsr) - { - return (T)(object)ResolveReference(tsr); - } - - return (T)result; - } - /// /// Execute a delegate inside a temporary . If no exception is thrown, the will be committed. /// If the method is not called from a thread with a (like the UI thread), it behaves synchronously. @@ -1080,151 +968,6 @@ public async Task WriteAsync(Func function, CancellationToken cancellat return result; } - /// - /// Execute a delegate inside a temporary on a worker thread, if called from UI thread. If no exception is thrown, - /// the will be committed. - /// - /// - /// Opens a new instance of this Realm on a worker thread and executes inside a write . - /// s and s/s are thread-affine, so capturing any such objects in - /// the action delegate will lead to errors if they're used on the worker thread. Note that it checks the - /// to determine if Current is null, as a test to see if you are on the UI thread - /// and will otherwise just call Write without starting a new thread. So if you know you are invoking from a worker thread, just call Write instead. - /// - /// - /// - /// var dogs = await realm.WriteAsync(tempRealm => - /// { - /// tempRealm.Add(new Dog - /// { - /// Breed = "Dalmatian", - /// }); - /// - /// tempRealm.Add(new Dog - /// { - /// Breed = "Poddle", - /// }); - /// - /// return tempRealm.All<Dog>(); - /// }); - /// - /// Note that inside the action, we use tempRealm. - /// - /// - /// Delegate with return type to execute inside a , creating, updating, or removing objects. - /// - /// The type of data in the . - /// An awaitable with return type . - [Obsolete("Use Realm.WriteAsync(Func function) instead.")] - public async Task> WriteAsync(Func> function) - where T : IRealmObjectBase - { - ThrowIfDisposed(); - - Argument.NotNull(function, nameof(function)); - - // If running on background thread, execute synchronously. - if (!AsyncHelper.TryGetValidContext(out _)) - { - return Write(() => function(this)); - } - - // If we are on UI thread the SynchronizationContext will be set (often also set on long-lived workers to use Post back to UI thread). - var result = await Task.Run(() => - { - using var realm = GetInstance(Config); - var writeResult = realm.Write(() => function(realm)); - if (writeResult is RealmResults rr && rr.IsValid && rr.IsManaged) - { - return (object)ThreadSafeReference.Create(writeResult); - } - - return writeResult; - }); - - await RefreshAsync(); - - if (result is ThreadSafeReference.Query tsr) - { - return ResolveReference(tsr); - } - - return (IQueryable)result; - } - - /// - /// Execute a delegate inside a temporary on a worker thread, if called from UI thread. If no exception is thrown, - /// the will be committed. - /// - /// - /// Opens a new instance of this Realm on a worker thread and executes inside a write . - /// s and s/s are thread-affine, so capturing any such objects in - /// the action delegate will lead to errors if they're used on the worker thread. Note that it checks the - /// to determine if Current is null, as a test to see if you are on the UI thread - /// and will otherwise just call Write without starting a new thread. So if you know you are invoking from a worker thread, just call Write instead. - /// - /// - /// - /// var markDogs = await realm.WriteAsync(tempRealm => - /// { - /// var mark = tempRealm.All<Person>().Single(d => d.Name == "Mark"); - /// - /// mark.Dogs.Add(new Dog - /// { - /// Breed = "Dalmatian", - /// }); - /// - /// mark.Dogs.Add(new Dog - /// { - /// Breed = "Poodle", - /// }); - /// - /// return mark.Dogs; - /// }); - /// - /// Note that inside the action, we use tempRealm. - /// - /// - /// Delegate with return type to execute inside a , creating, updating, or removing objects. - /// - /// The type of data in the . - /// An awaitable with return type . - [Obsolete("Use Realm.WriteAsync(Func function) instead.")] - public async Task> WriteAsync(Func> function) - { - ThrowIfDisposed(); - - Argument.NotNull(function, nameof(function)); - - // If running on background thread, execute synchronously. - if (!AsyncHelper.TryGetValidContext(out _)) - { - return Write(() => function(this)); - } - - // If we are on UI thread the SynchronizationContext will be set (often also set on long-lived workers to use Post back to UI thread). - var result = await Task.Run(() => - { - using var realm = GetInstance(Config); - var writeResult = realm.Write(() => function(realm)); - if (writeResult is RealmList rl && rl.IsValid && rl.IsManaged) - { - return (object)ThreadSafeReference.Create(writeResult); - } - - return writeResult; - }); - - await RefreshAsync(); - - if (result is ThreadSafeReference.List tsr) - { - return ResolveReference(tsr); - } - - return (IList)result; - } - /// /// Update the instance and outstanding objects to point to the most recent persisted version. /// diff --git a/Tests/Realm.Tests/Database/AsyncTests.cs b/Tests/Realm.Tests/Database/AsyncTests.cs index cfdc579b77..a829ce1eb9 100644 --- a/Tests/Realm.Tests/Database/AsyncTests.cs +++ b/Tests/Realm.Tests/Database/AsyncTests.cs @@ -30,87 +30,6 @@ namespace Realms.Tests.Database [TestFixture, Preserve(AllMembers = true)] public class AsyncTests : RealmInstanceTest { - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void AsyncWrite_ShouldExecuteOnWorkerThread() - { - TestHelpers.RunAsyncTest(async () => - { - var currentThreadId = Environment.CurrentManagedThreadId; - var otherThreadId = currentThreadId; - - Assert.That(_realm.All().Count(), Is.EqualTo(0)); - Assert.That(SynchronizationContext.Current != null); - await _realm.WriteAsync(realm => - { - otherThreadId = Environment.CurrentManagedThreadId; - realm.Add(new Person()); - }); - - Assert.That(_realm.All().Count(), Is.EqualTo(1)); - Assert.That(otherThreadId, Is.Not.EqualTo(currentThreadId)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void AsyncWrite_WhenOnBackgroundThread_ShouldExecuteOnSameThread() - { - TestHelpers.RunAsyncTest(async () => - { - await Task.Run(async () => - { - var currentThreadId = Environment.CurrentManagedThreadId; - var otherThreadId = -1; - - Assert.That(_realm.All().Count(), Is.EqualTo(0)); - Assert.That(SynchronizationContext.Current == null); - - await _realm.WriteAsync(realm => - { - otherThreadId = Environment.CurrentManagedThreadId; - realm.Add(new Person()); - }); - - Assert.That(_realm.All().Count(), Is.EqualTo(1)); - Assert.That(otherThreadId, Is.EqualTo(currentThreadId)); - - _realm.Dispose(); - }); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void AsyncWrite_UpdateViaPrimaryKey() - { - TestHelpers.RunAsyncTest(async () => - { - IntPrimaryKeyWithValueObject obj = null; - _realm.Write(() => - { - obj = _realm.Add(new IntPrimaryKeyWithValueObject { Id = 123 }); - }); - - await _realm.WriteAsync(realm => - { - var dataObj = realm.Find(123); - dataObj.StringValue = "foobar"; - }); - - // Make sure the changes are immediately visible on the caller thread - Assert.That(obj.StringValue, Is.EqualTo("foobar")); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void AsyncWrite_ShouldRethrowExceptions() - { - TestHelpers.RunAsyncTest(async () => - { - const string message = "this is an exception from user code"; - var ex = await TestHelpers.AssertThrows(() => _realm.WriteAsync(_ => throw new Exception(message))); - Assert.That(ex.Message, Is.EqualTo(message)); - }); - } - [Test] public void RefreshAsync_Tests() { diff --git a/Tests/Realm.Tests/Database/InstanceTests.cs b/Tests/Realm.Tests/Database/InstanceTests.cs index 479ac01e14..46335a2e3d 100644 --- a/Tests/Realm.Tests/Database/InstanceTests.cs +++ b/Tests/Realm.Tests/Database/InstanceTests.cs @@ -550,7 +550,7 @@ await Task.Run(() => }); } - [Test, Obsolete("Tests deprecated WriteAsync API")] + [Test] public void UsingDisposedRealm_ShouldThrowObjectDisposedException() { TestHelpers.RunAsyncTest(async () => @@ -575,7 +575,8 @@ public void UsingDisposedRealm_ShouldThrowObjectDisposedException() Assert.That(() => realm.RemoveAll(), Throws.TypeOf()); Assert.That(() => realm.Write(() => { }), Throws.TypeOf()); - await TestHelpers.AssertThrows(() => realm.WriteAsync(_ => { })); + await TestHelpers.AssertThrows(() => realm.WriteAsync(() => { })); + await TestHelpers.AssertThrows(() => realm.RefreshAsync()); other.Dispose(); }); diff --git a/Tests/Realm.Tests/Database/PropertyChangedTests.cs b/Tests/Realm.Tests/Database/PropertyChangedTests.cs index 7687390a14..2501d6ede2 100644 --- a/Tests/Realm.Tests/Database/PropertyChangedTests.cs +++ b/Tests/Realm.Tests/Database/PropertyChangedTests.cs @@ -135,20 +135,22 @@ public void ManagedObject_WhenAnotherInstanceChanged() }); } - [Test, Obsolete("Tests deprecated WriteAsync API")] + [Test] public void ManagedObject_WhenAnotherThreadInstanceChanged() { TestHelpers.RunAsyncTest(() => { return TestManagedAsync(async (_, name) => { - await _realm.WriteAsync(otherRealm => + await Task.Run(() => { - var otherPersonInstance = otherRealm.All().First(); - otherPersonInstance.FirstName = name; + using var bgRealm = GetRealm(_realm.Config); + bgRealm.Write(() => + { + var otherPersonInstance = bgRealm.All().First(); + otherPersonInstance.FirstName = name; + }); }); - - await Task.Delay(50); }); }); } @@ -474,7 +476,7 @@ public void ManagedObject_WhenChanged_CallsOnPropertyChanged() Assert.That(notifiedPropertyNames, Is.EquivalentTo(new[] { nameof(AgedObject.Birthday), nameof(AgedObject.Age) })); } - [Test, Obsolete("Tests deprecated WriteAsync API")] + [Test] public void ManagedObject_WhenChangedOnAnotherThread_CallsOnPropertyChanged() { TestHelpers.RunAsyncTest(async () => @@ -492,13 +494,17 @@ public void ManagedObject_WhenChangedOnAnotherThread_CallsOnPropertyChanged() notifiedPropertyNames.Add(e.PropertyName); }; - await _realm.WriteAsync(r => + await Task.Run(() => { - var otherThreadInstance = r.All().Single(); - otherThreadInstance.Birthday = DateTimeOffset.UtcNow.AddYears(-6); + using var bgRealm = GetRealm(_realm.Config); + bgRealm.Write(() => + { + var otherThreadInstance = bgRealm.All().Single(); + otherThreadInstance.Birthday = DateTimeOffset.UtcNow.AddYears(-6); + }); }); - await Task.Yield(); + _realm.Refresh(); Assert.That(notifiedPropertyNames, Is.EquivalentTo(new[] { nameof(AgedObject.Birthday), nameof(AgedObject.Age) })); }); diff --git a/Tests/Realm.Tests/Database/WriteOverloads.cs b/Tests/Realm.Tests/Database/WriteOverloads.cs index 003308def8..6c712e3cc9 100644 --- a/Tests/Realm.Tests/Database/WriteOverloads.cs +++ b/Tests/Realm.Tests/Database/WriteOverloads.cs @@ -16,7 +16,6 @@ // //////////////////////////////////////////////////////////////////////////// -using System; using System.Linq; using NUnit.Framework; @@ -102,194 +101,5 @@ public void Write_ShouldReturnCollection() var queried = _realm.All().First().ListOfDogs; Assert.That(queried, Is.EqualTo(dogs)); } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnPrimitive() - { - TestHelpers.RunAsyncTest(async () => - { - var primitive = await _realm.WriteAsync(realm => - { - return 2; - }); - Assert.That(primitive, Is.EqualTo(2)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnRealmObject() - { - TestHelpers.RunAsyncTest(async () => - { - var pko = await _realm.WriteAsync(realm => - { - return realm.Add(new IntPrimaryKeyWithValueObject - { - Id = 1, - StringValue = "bla" - }); - }); - - var queried = _realm.Find(1); - Assert.That(queried, Is.EqualTo(pko)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnUnmanagedRealmObject() - { - TestHelpers.RunAsyncTest(async () => - { - var pko = new IntPrimaryKeyWithValueObject - { - Id = 1, - StringValue = "bla" - }; - - var writeResult = await _realm.WriteAsync(realm => - { - return pko; - }); - - Assert.That(writeResult, Is.EqualTo(pko)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnQueryable() - { - TestHelpers.RunAsyncTest(async () => - { - var queryable = await _realm.WriteAsync(realm => - { - realm.Add(new IntPrimaryKeyWithValueObject - { - Id = 1, - StringValue = "bla" - }); - - realm.Add(new IntPrimaryKeyWithValueObject - { - Id = 2, - StringValue = "ble" - }); - - return realm.All(); - }); - - var queried = _realm.All(); - Assert.That(queried, Is.EqualTo(queryable)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnUnmanagedQueryable() - { - var pko1 = new IntPrimaryKeyWithValueObject - { - Id = 1, - StringValue = "bla" - }; - - var pko2 = new IntPrimaryKeyWithValueObject - { - Id = 2, - StringValue = "ble" - }; - - var queryable = new IntPrimaryKeyWithValueObject[] { pko1, pko2 }.AsQueryable(); - - TestHelpers.RunAsyncTest(async () => - { - var queryableResult = await _realm.WriteAsync(realm => - { - return queryable; - }); - - Assert.That(queryable, Is.EqualTo(queryableResult)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnCollection() - { - TestHelpers.RunAsyncTest(async () => - { - var dogs = await _realm.WriteAsync(realm => - { - var owner = realm.Add(new Owner - { - Name = "Owen" - }); - - owner.ListOfDogs.Add(new Dog - { - Name = "Puppy1" - }); - - owner.ListOfDogs.Add(new Dog - { - Name = "Puppy2" - }); - - return owner.ListOfDogs; - }); - - var queried = _realm.All().First().ListOfDogs; - Assert.That(queried, Is.EqualTo(dogs)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_ShouldReturnUnmanagedCollection() - { - var owner = new Owner - { - Name = "Owen" - }; - - owner.ListOfDogs.Add(new Dog - { - Name = "Puppy1" - }); - - owner.ListOfDogs.Add(new Dog - { - Name = "Puppy2" - }); - - TestHelpers.RunAsyncTest(async () => - { - var collectionResult = await _realm.WriteAsync(realm => - { - return owner.ListOfDogs; - }); - - Assert.That(owner.ListOfDogs, Is.EqualTo(collectionResult)); - }); - } - - [Test, Obsolete("Tests deprecated WriteAsync API")] - public void WriteAsync_WhenReturningManagedObjectIndirectly_ShouldThrow() - { - TestHelpers.RunAsyncTest(async () => - { - var pko = await _realm.WriteAsync(realm => - { - var ipo = realm.Add(new IntPrimaryKeyWithValueObject - { - Id = 1, - StringValue = "bla" - }); - - return new - { - IPO = ipo, - }; - }); - - Assert.Throws(() => { _ = pko.IPO.Id != 3; }); - }); - } } }