1

I'm considering switching from SQLite to Realm in my UWP applications, as Realm seems fairly easier to setup, but I'm having trouble with some limitations that so far are a deal-breaker for me, the most important one being that apparently you can't use realm objects after disposing the Realm instance that was used to retrieve them. So I wonder how am I supposed to load data to throw in a ViewModel, as after loading the data items that Realm instance would be gone.

Here's an example of a really basic ViewModel:

public class MyViewModel : ViewModelBase // Assume this has INotifyPropertyChanged setup
{
    private IEnumerable<MyItem> _Items;

    public IEnumerable<MyItem> Items
    {
        get => _Items;
        private set => Set(nameof(Items), ref _Items, value);
    }

    public async Task LoadDataAsync()
    {
        using (Realm realm = await Realm.GetInstanceAsync())
        {
            Items = realm.All<MyItem>().ToArray();
        }
    }
}

Assume that MyItem is some example model, which inherits from RealmObject.

Now, if I were using SQLite, I'd have no problems whatsoever there, as once retrieved from the database, each model instance would be a standalone object that I could just store in my ViewModel. Instead, classes that inherit from RealObject are linked to their original Realm instance, so if I try to use them (ie. read one of their properties) after the source Realm instance has been disposed, everything falls apart (I get a nice Exception).

The obvious workaround would be to keep two classes for each model: one that maps to the Realm database, and another one that's a standalone object. But this has some drawbacks:

  • Twice the number of classes
  • Additional work (and memory waste) to copy each instance to the standalone class
  • This just seems wrong, I mean, come on

Another possibility would be to keep a singleton instance of a Realm object that is never closed, but doesn't sound like a good idea either (and it would have problems when using multiple threads too, so it's definitely not a valid solution).

I've seen that the Java version of the Realm library has some "getRawObject" method (or something like that) that allows you to get a standalone copy of a RealmObject, but still, I guess this actually creates a copy of the model instance, so it doesn't sound good either.

My question is:

Is there a way to just get the same flexibility of SQLite, where once loaded, you can pass the models around anywhere you want (including to other threads), use them in ViewModels, and then also eventually edit them and pass them back around to the database to update them on disk?

Bonus point: as another example, consider a library that loads some items from a Realm database and return them to the user. Once loaded, those objects would be read-only, meaning that even if they somehow were modified, they wouldn't be changed on the database itself. My current solution is again to copy each loaded model into another classes that doesn't inherit from RealmObject, but as I said, this is not scalable at all (imagine a large project with dozens of models, it would be impossible to do so).

Sergio0694
  • 4,447
  • 3
  • 31
  • 58
  • https://stackoverflow.com/a/38061292/2413303 is pretty much all I found for .Net (and that `copyFromRealm` doesn't exist in .NET https://github.com/realm/realm-dotnet/issues/661#issuecomment-336610046) – EpicPandaForce Feb 03 '18 at 19:13
  • I've created an extension function to deep clone object without including the realm properties. Please check my answer here https://stackoverflow.com/questions/37999630/how-to-get-a-standalone-unmanaged-realmobject-using-realm-xamarin/54361319#54361319 – Vineet Choudhary Jan 25 '19 at 08:22
  • Based from the [answer](https://stackoverflow.com/a/54361319/1641574), I've created NuGet package for deep copying Realm Objects https://www.nuget.org/packages/Realm.Clone/ Source code: https://github.com/pfedotovsky/realm-dotnet-clone/blob/master/Realm.Clone/Realm.Clone/RealmExtensions.cs – pfedotovsky Apr 20 '19 at 20:57

1 Answers1

1

In .NET, it seems to be possible to detach the managed RealmObject from the Realm only if you create a copy of it some way or another.

See https://stackoverflow.com/a/38061292/2413303

public Contact ToStandalone()
{
    return new Contact()
    {
        companyName = this.companyName,
        dateAdded = this.dateAdded,
        fullName = this.fullName,
        gender = this.gender,
        website = this.website
    };
}

Technically it doesn't have to be a different class, but it needs to be created with new.

As for having to make a copy - well, that's what you always did with SQLite too.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • Ok that's a bit better, at least I can reuse the same class. Still, I will definitely stick to SQLite in the future though as this approach is not scalable (you have to implement a copy method manually for each class, in addition to the downside of not being able to indirectly inherit from `RealObject`). Also, I'm not sure I understand what you mean by "that's what you always did with SQLite too". There I could just load models from the database and use them everywhere (and pass them back if I wanted to update them), where would I have made a copy? – Sergio0694 Feb 03 '18 at 19:28
  • The trick is that Realm-java generates exactly this kind of mapping via annotation processing. I guess the dot-net version doesn't. What I meant by copy is that you should treat a RealmObject class like a schema definition (CREATE TABLE blah blah). So in SQLite, you have a SQL-schema, but in Realm, your objects are the schema. The attached objects technically still belong to Realm world. So when you'd map from SQLite to actual Entities, that's like mapping a managed to unmanaged. Except Realm allows using the schema objects (managed) directly, which gives lazy loading and ability to observe. – EpicPandaForce Feb 03 '18 at 19:47
  • That's the same exact pattern I have in SQLite too (in SQLite.NET lib), I mean the version powered by the EntityFramework-like engine. So there, you'd just use attributes to map your properties to table columns (similar to Realm), and then all the queries and database operations are handled by the library, you just pass LINQ `Expression`s and they get converted to queries and mapped back to objects. But, model instances are 100% standalone there and not tied to a thread either, so it's more flexible. Also, you can have indirect inheritance without issues (unlike with a `RealmObject`). – Sergio0694 Feb 03 '18 at 19:55
  • Anyways, I marked the answer as valid as it does in fact answer my question just fine Also, I'm not sure if you actually work on the Realm team, I'd just like to say I wasn't criticizing the lib by itself at all, which instead is pretty great, I was just considering the limitations, at least on the .NET version of the lib (I understand it's hard to balance easy to use APIs for a generic usage and more features). – Sergio0694 Feb 03 '18 at 19:58
  • Nah, I don't work on the Realm team, I just answer questions as a habit at this point :D it's a shame it's not automatic in the .NET version. The lifecycle management of Realm is generally a bit tricky, with `close` or `autoreleasepool {` depending on platform. In Java you can make a thread-local cache (Realm has one internally too), that can manage the Realm instance without having to pass it as method argument. Either way, live objects are accessible only if the Realm itself is open on that thread - otherwise, copies can exist only! – EpicPandaForce Feb 03 '18 at 20:18
  • 1
    There's no out-of-the-box support for "detaching" an object from Realm as that is a poorly defined general-purpose requirement. For example, if your object has a collection of other objects - should these be detached as well? Then, if there are cycles, should they be resolved for you, etc. There are multitude of tools out there that are specifically designed for mapping one object to another, e.g. AutoMapper, that will save you the pain of manually copying objects. – Nikola Irinchev Feb 08 '18 at 21:17
  • Realm-java lets you provide a Depth parameter which describes down to what depth you want RealmObject relations to be mapped out. Linking objects aren't mapped out. – EpicPandaForce Feb 08 '18 at 22:16