Deleting From RealmDB - XamarinForms

Hello , I’m having problem when deleting from Realm, I’m using Xamarin Forms with ReactiveUI. Adding , Updating works fine , but Deleting not , I’m getting Realms.Exceptions.RealmInvalidObjectException: 'Attempted to access detached row' , I have a service class that have all methods for CRUD operations . There I’m creating instance of Realm object. My service class is singleton.

@Dragan_Blanusa I think you need to use .NET’s .freeze() method for this, especially for ReactiveUI. You can see a code snippet here:
https://www.mongodb.com/article/realm-database-and-frozen-objects

A more detailed example can be seen here -

Its hard to say without seeing your code but this will likely help you

What should I do with frozen object if I can’t write and subscribe to it ?? My problem is when I remove something from server side and my mobile app is in background , and goes from background to foreground , app just crash with exception I wrote .

Can you isolate a small repro case that we can use to investigate the issue? Generally what happens is that when an object is deleted, the collection containing the object emits a collection changed event that will tell the container to remove an element. Unfortunately, some binding engines will attempt to read data from the removed item prior to the removal, which then results in RealmInvalidObjectException. We have some custom logic to handle this when using Xamarin.Forms:

Essentially, what this does is provide a custom property getter that the XF binding engine uses that will return the property’s default value instead of throwing an exception, if the object is deleted. I imagine whatever framework you’re using for ReactiveUI is doing things slightly differently, which is why it doesn’t go through that piece of code. In any case, having a small repro project will allow us to explore ways to support it.

@Dragan_Blanusa You can open an issue in this repo if you are are able to provide a reproduction case:

1 Like

I just raised a feature request about this exact kind of behaviour -

It’s interesting to see that the Realm team went down the route of adding custom handling to ‘fix’ this issue for Xamarin Forms binding. To me that’s 1) Indicative of a larger issue, and 2) Doesn’t help scenarios where Xamarin Forms binding isn’t the cause (seemingly in @Dragan_Blanusa’s case, and also in scenarios I refer to in the feature request).

Realm’s behaviour of completely invalidating objects on deletion to the point where you can’t even access properties (and hence can’t call any overriden GetHashCode() or Equals() methods, for starters) makes it fundamentally incompatible with various reactive components/libraries, and also certain UI binding approaches. Can we please (optionally!) allow a more pragmatic approach that would make life much easier?

We can create a mode of operation where accessing deleted object properties returns the default value for that type and is something we’re evaluating in relation to compiled data bindings. It is unlikely we’ll freeze objects prior to the deletion in the short-medium term as that is a fairly involved task with a lot of potential to be a footgun.

1 Like

That may help in the specific data binding scenario, but unfortunately doesn’t help at all for the reactive pipeline scenarios.

The workaround we have to employ currently is to duplicate all necessary properties on each Realm object, where the copied properties are [Ignored], and we try to ensure they are kept in sync with the persisted properties, and then use those duplicated properties in Equals() and GetHashCode() so that they may still be called by reactive library implementations without blowing up the whole app when an item is deleted.

It’s an ugly, imperfect hack, but I can’t see a better way to do it. I’d love to hear any suggestions!

Thanks.

Why does the reactive library need to access properties of deleted objects? If you do have a small example that highlights the issues, I’d be happy to take it for a spin and see if there’s something clever we can do to support it.

I wholeheartedly concur. I’ve used Realm on 5 projects now, and this issue shows up again and again in the app center crash logs. The only “solution” is to do as Matt says, to copy properties from the model to the view model.

The question as to why something like ReactiveUI needs access to properties of a detached/delated object is mute. It does, as I can confirm from the unit testing exception screen in front of me.

Please give us the option of not throwing an exception when we access the properties of a detached row. Really. It’s the only thing I dread when I recommend Realm for mobile projects.

Thanks.
Tim

As I mentioned above, happy to try and do something about it, if I can get a simple repro case. Right now we have a limitation that we can’t access deleted objects, meaning that even if we did remove the exception, all properties of the deleted object would be set to their default values, meaning we’re likely going to hit a different issue.

If we can get a repro project with the concrete reactive framework you use, that would enable us to investigate a fully fledged solution rather than a band-aid.

1 Like

If I’m using database I would like to at least be able to delete data from it. Realm just throw some awkward exceptions so I should really dig under the hood to undestand what to do with this “Attempted to access detached row”.

Not sure how this is related. You can delete data from the database and accessing a detached row means you’re trying to read data from the database, that is no longer there. If you want to be able to reference realm object data after deleting it, you need to extract this data into an in-memory object.

1 Like
  private async Task UpdateRecentlyBookingIdList(string bookingId)
        {
            var recentlyOpenedBookings = RealmInstance.All<RecentlyOpenedBookingModel>().AsEnumerable().ToList();
            if (recentlyOpenedBookings?.Any(s => s.BookingId == bookingId)??false) return;
            var model = new RecentlyOpenedBookingModel()
            {
                BookingId = bookingId
            };
            await RealmInstance.WriteAsync(() => RealmInstance.Add(model, true));
            if (recentlyOpenedBookings is {Count:>=MaxRecentlyBookingCount})
                await RealmInstance.WriteAsync(() => RealmInstance.Remove(recentlyOpenedBookings[0]));
        }

Well that’s my code. So from time to time I’m getting this exception. Maybe you could give me a clue at least on what I’m doing wrong? I don’t use realm objects in the app I always convert them to non realm objects cause I don’t need all this “smartish” sync thing Realm does. I’m using it as a plain database (not sure that it’s really possible).

Can you post the stacktrace of the exception you’re getting as well? The code for deleting a booking seems reasonable to me, though there are multiple optimizations that’ll make it more efficient. Are you getting the exception from this part of the code or from somewhere else?

PS: you can totally use Realm as a dumb database, though you’ll be missing out on a lot of the benefits it brings to the table.

It’s really hard to reproduce it so that’s all what I have for now is a stacktrace from our AppCenter logs:

PersistentStorage+<>c__DisplayClass9_0`1[T].b__0 ()

SIGABRT: Attempted to access detached rowCrashVersion 1.8.1896 (1.8.1896)1 user1 report 

* NativeException.ThrowIfNecessary (System.Func`2[T,TResult] overrider)

* ObjectHandle.RemoveFromRealm (Realms.SharedRealmHandle realmHandle)

* Realm.Remove (Realms.IRealmObjectBase obj)

* PersistentStorage+<>c__DisplayClass9_0`1[T].<RemoveAsync>b__0 ()

* Realm+<>c__DisplayClass71_0.<WriteAsync>b__0 ()

* Realm.WriteAsync[T] (System.Func`1[TResult] function, System.Threading.CancellationToken cancellationToken)

* PersistentStorage.RemoveAsync[T] (T item)

* RecentlySearchBookingsStorage.UpdateRecentlyBookingIdList (System.String bookingId)

* RecentlySearchBookingsStorage.AddRecentlyOpenedBookingIdAsync (System.String bookingId)

* ArchiveViewModel.NavigateToBookingDetails (System.String id, System.String paxName)

* ArchiveViewModel.ShowDetails (CTeleport.Mobile.Core.ViewModels.Archive.BaseBookingViewModel booking)

* AsyncMethodBuilderCore+<>c.<ThrowAsync>b__7_0 (System.Object state)

* NSAsyncSynchronizationContextDispatcher.Apply ()

* (wrapper managed-to-native) UIKit.UIApplication.xamarin_UIApplicationMain(int,string[],intptr,intptr,intptr&)

* UIApplication.UIApplicationMain (System.Int32 argc, System.String[] argv, System.IntPtr principalClassName, System.IntPtr delegateClassName)

* UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName)

* Application.Main (System.String[] args)

p.s. yes the code requires optimiztion indeed. But the goal for now to make it stable.

There seem to be two major points of vulnerability from a quick look at your code snippet.

  1. You are making the decision to remove an item by comparing MaxRecentlyBookingCount but in an async method can other things influence that count before that test?
  2. The removal code assumes recentlyOpenedBookings[0] is the correct object to remove.
  1. MaxRecentlyBookingCount itself is a constant
  2. How can it be incorrect? I’ve just read it from the db. Or should I use Freeze()?

This is async code. It seems you’re assuming only ONE invocation of this can be running but my understanding of async/await is there is no guarantee you only have one thread doing it.
Even within a single mobile app, can users inadvertently “stutter” a button or could animation code cause enough delay that more than on delete happens?

I would be logging thread identifiers to eliminate this.