The realm documentation states that we don’t have to worry about threads and everything is nice an simple. There it states that the most efficient thing would be to let the UI bind directly to the Realm objects. They also implement INotifyPropertyChanged.
I must be getting something fundamentally wrong because it appears as all loading must then be happening on the UI thread, i.e. no background loading, no progress bars or spinning wheels are possible. The UI freezes until the work is done.
If I attempt to move the load into a Task or other background scenario, I ultimately end up in the exception ‘System.Exception: Realm accessed from incorrect thread.’ when the UI is reading a nested object. I understand that the root object belongs to the worker thread and the lazy loading can only succeed from there.
My only two conclusions:
If I wanted to use background loading, I must not use Realm Objects on the UI due its thread affinity.
To avoid any trouble with threads, stop using Realm objects at all outside of the data tier.
Can you post some sample code illustrating the issues you’re facing? It’s difficult to make suggestions without seeing what is going wrong. Generally speaking though, when working on a background thread, you should re-open the Realm on that thread, perform any updates to it, then close it. Any changes made in the background will then raise change notifications on the UI thread and the UI can update itself. But again, it would be a lot easier if there was some code illustrating the problematic behavior which we can discuss.
public class SomeRepository
{
Task LoadAll()
{
IEnumerable<T> result = null;
await Task.Run(() =>
{
Thread.Sleep(2000); // Just to make it a bit longer
var realm = Realm.GetInstance();
result = realm.All<T>().ToArray();
});
// Any attempt outside of the action to access or map a nested object is resulting in the thread exception
// That is because the RealmObjects were loaded in the background task
Console.WriteLine(result.First().NestedObject.ToString());
}
}
// Any attempt outside of the action to access or map a nested object is resulting in the thread exception
// That is because the RealmObjects were loaded in the background task
Your comment is correct. In Realm, objects are thread-confined so you can’t access a referenced object across multiple threads in that way.
Also, by looking at your example you may be using a repository patter. We generally discourage that as it makes Realm’s live objects not that valuable anymore.
Anyway, back to your question. To achieve what you want you can use a ThreadsafeReference. Like so
var tsr = await Task.Run(() =>
{
using var realm = Realm.GetInstance();
var foos = realm.All<Foo>();
// Do something with foos
return ThreadSafeReference.Create(foos);
});
var mainRealm = Realm.GetInstance();
var mainFoos = mainRealm.ResolveReference(tsr);
As another little piece of advice, we generally recommend the following:
if you’re on the main thread, open a singleton Realm instance and just data-bind models to the UI and rely on notifications
In the background threads, open a new realm instance, perform any updates, and then close the Realm.
Thanks Andrea, you actually confirmed my (today’s) conclusions:
The repository pattern causes here more trouble than it solves:
generic queries are already on the Realm or can extend the IQueryable type
Write operations on repositories could be violating ACID since not all writes would be in one transaction.
I will use one realm for reading and showing data on the screen
In case reading noticeable takes time, I will use the ThreadSafeReference
In cases reading is fast, I won’t bother with tasks to embrace simplicity
Write operations in a background thread will only use a short living realm (no repository pattern) -the UI realm shall get the updates based on notifications.