-
I'm opening this discussion expecting to be told that this is unsupported behaviour we should not be relying on, but based on how useful it is to us in our usage scenario, feel it is best to get a definite answer. using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Realms;
namespace RealmPerformanceTest
{
[TestFixture]
public class RealmScratch
{
[Test]
public void TestNestedContexts()
{
var configuration = new InMemoryConfiguration("test");
using (var realm = Realm.GetInstance(configuration))
{
var transaction = realm.BeginWrite();
realm.Add(new TestRealmModel("a")
{
TestInt = 1,
Children = { new TestRealmModel("b") }
});
transaction.Commit();
TestRealmModel outerFetch = realm.Find<TestRealmModel>("a");
TestRealmModel innerFetch;
using (var innerRealm = Realm.GetInstance(configuration))
{
innerFetch = innerRealm.Find<TestRealmModel>("a");
}
// outer fetch should obviously work
Assert.AreEqual(1, outerFetch.TestInt);
// inner fetch still seems to work because the shared realm handle for this thread is still valid, even though the inner context was disposed.
Assert.AreEqual(1, innerFetch.TestInt);
// These also work
Assert.IsNotNull(outerFetch.Children.FirstOrDefault());
Assert.IsNotNull(innerFetch.Children.FirstOrDefault());
// Also works...
Assert.IsNotNull(outerFetch.Children.AsRealmQueryable().FirstOrDefault());
// ..but this one fails
Assert.IsNotNull(innerFetch.Children.AsRealmQueryable().FirstOrDefault());
}
}
}
public class TestRealmModel : RealmObject
{
[PrimaryKey] public string ID { get; set; }
public int TestInt { get; set; }
// ReSharper disable once UnassignedGetOnlyAutoProperty
public IList<TestRealmModel> Children { get; }
public TestRealmModel()
{
}
public TestRealmModel(string key)
{
ID = key;
}
}
} The failing line in the above code results in the follow exception: System.ObjectDisposedException : Safe handle has been closed.
Object name: 'SafeHandle'.
at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
at Realms.QueryHandle.NativeMethods.create_results(QueryHandle queryPtr, SharedRealmHandle sharedRealm, SortDescriptorHandle sortDescriptor, NativeException& ex)
at Realms.QueryHandle.CreateResults(SharedRealmHandle sharedRealm, SortDescriptorHandle sortDescriptor)
at Realms.RealmResultsVisitor.MakeResultsForQuery()
at Realms.RealmResultsVisitor.VisitMethodCall(MethodCallExpression node)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Realms.RealmResultsProvider.Execute(Expression expression)
at Realms.RealmResultsProvider.Execute[T](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
at RealmPerformanceTest.RealmScratch.TestNestedContexts() in /Users/dean/Projects/archived/RealmPerformanceTest/RealmPerformanceTest/Program.cs:line 48 We were developing around the assumptions that as long as one realm instance was open in the current thread/synchronisation context, a nested usage could be disposed/closed while fetches from that instance could still be accessed. This worked correctly until using The use case we have for finding this behaviour useful is that regardless of which thread we are on (main or arbitrary async), we could use the same access pattern for realm contexts: using (Realm context = realmFactory.CreateContext())
{
// do something
} Without being able to rely on the mentioned behaviour, we would need to make special cases for main thread usage which consumes and avoids disposing the main thread (singleton) realm context. ie. // main thread usage
Realm context = realmFactory.MainThreadRealm;
// do something
// threaded usage
using (Realm context = realmFactory.CreateContext())
{
// do something
} or we would need to wrap the realm context and handle the two cases of disposal requirements in the wrapper class (feasible but adds unwanted complexity). |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Unfortunately you are correct in saying that this is expected behaviour. When a query needs to be performed, we need to get a hold of a realm handle, that in your case has been disposed. This does not happen with property access, as realm objects have their specific object handle they can use. Regarding how to deal with realm instances, having a different behaviour for the main thread and background threads. To add to what you already wrote, you don't necessarily need to have a singleton in the main thread, but you can also just create an instance when you need it, in a similar way to what you did with |
Beta Was this translation helpful? Give feedback.
Unfortunately you are correct in saying that this is expected behaviour. When a query needs to be performed, we need to get a hold of a realm handle, that in your case has been disposed. This does not happen with property access, as realm objects have their specific object handle they can use.
Regarding how to deal with realm instances, having a different behaviour for the main thread and background threads. To add to what you already wrote, you don't necessarily need to have a singleton in the main thread, but you can also just create an instance when you need it, in a similar way to what you did with
Realm context = realmFactory.CreateContext()
. Realms on the main thread are cached, so …