Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage a Standalone RealmObject from a JSON response deserialized into a RealmObject #514

Closed
CrimsonKyle opened this issue May 4, 2016 · 44 comments
Assignees
Labels

Comments

@CrimsonKyle
Copy link

CrimsonKyle commented May 4, 2016

I need Realm to Manage a JSON result that is deserialized into a RealmObject. What is the best way of doing this?

Error: System.Reflection.TargetInvocationException: "Exception has been thrown by the target of an invocation."

public void FetchParent ()

{
    // get the JSON object for the parent object
 
    var client = new RestClient ();
  
    client.BaseUrl = new Uri ("https://baseurl.com”); 
 
    var request = new RestRequest ("service/12345”, Method.GET);
   
    request.XmlSerializer.ContentType = "applicaiton/json";

   
    IRestResponse response = client.Execute (request);
   
    var object = JsonConvert.DeserializeObject<Parent> (response.Content);
   

    // manage the standalone deserialized realm object
    realm.Write (() => {

        realm.Manage<Parent>(object);
    
         } );

}

//_MODELS_**//

public class Parent : RealmObject
{
    [ObjectId]
    public string ParentId { get; set; }
    public DateTimeOffset ObjectDatestamp { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public RealmList<Dogs> Dogs { get; }
}

public class Dog : RealmObject
{
    [ObjectId]
    public string DogId { get; set; }
    public DateTimeOffset ObjectDatestamp { get; set; }

    public string Type { get; set; }
    public string Name { get; set; }
}
@CrimsonKyle
Copy link
Author

I am using RestSharp by the way for the HTTP client

@fealebenpae
Copy link
Member

Hey @CrimsonKyle, standalone objects do not support RealmList properties right now, but we're continuously working to improve and this functionality will be available soon.

In the mean time, you can workaround this issue by using JsonConvert.PopulateObject:

realm.Write(() => {
    var parent = realm.Create<Parent>();
    JsonConvert.PopulateObject(response.Content, parent);
});

@CrimsonKyle
Copy link
Author

This was a game changer!

@fealebenpae fealebenpae self-assigned this May 6, 2016
@UKDeveloper99
Copy link

Any word on when this might be supported? I'm having to rewrite large chunks of my code because of this issue.

@kristiandupont
Copy link
Contributor

This was actually solved in the recently released version 0.77.0, but we unlisted it because there was a bug in the weaver code. We are fixing it and will have a patched release ready very soon.

@UKDeveloper99
Copy link

@kristiandupont Oh man, yeah I made an issue about that. But it was a duplicate. Well I guess I can exercise a little patience here. Looking forward to the release!

@UKDeveloper99
Copy link

UKDeveloper99 commented Jul 29, 2016

@kristiandupont Hi Kristian sorry to bother you, I've just downloaded the latest version of Realm 0.77.1.
When I deserialize the object like so:

var object = JsonConvert.DeserializeObject<T> (response.Content);


The RealmList isn't populated with the list (array) data. Am I doing something wrong here?


    public class ReportChart : RealmObject
    {
        public string Title { get; set; }
        public string Url { get; set; }
    }

    public class Report : RealmObject
    {
        ...
        public RealmList<ReportChart> EntryCharts { get; }
    }

No calls have been made to db.Manage() at this stage. The object is still standalone.

@kristiandupont
Copy link
Contributor

kristiandupont commented Jul 29, 2016

Hi @UKDeveloper99, try replacing your declaration from RealmList<ReportChart> with IList<ReportChart> -- that way, a realm object can be created as standalone even with a list. When you then Realm.Manage() it in a transaction, it will all be persisted properly.

@UKDeveloper99
Copy link

It's working hallelujah!!!!!!!!!!

@UKDeveloper99
Copy link

UKDeveloper99 commented Jul 29, 2016

@kristiandupont Sorry it's me again I'll stop bothering you soon I promise. Since updating I keep getting a load of these exceptions throughout my code.

Realms.RealmInvalidObjectException has been thrown
Attempted to access detached row.

You'll probably have a much better idea of what throws this exception than I will.

Bit more info: This is when I'm using the objects after they're persisted in the database. I've made sure to call .ToList() on all the lists etc. So it's not operating on the live list.

@kristiandupont
Copy link
Contributor

It's alright, no bother at all :-)

This error means that you are trying to read or write a property on an object that is no longer attached to a Realm. This could either be because it has been deleted from the Realm, or it could be because the Realm was closed. If none of those apply, it must be some other bug, potentially in our code so please let me know if this seems to be the case.

@UKDeveloper99
Copy link

I'll look in to it and get back to you.

@UKDeveloper99
Copy link

UKDeveloper99 commented Aug 1, 2016

@kristiandupont Hi Kristian, starting to bang my head against the wall with this one. Feeling under pressure, this project was ready to be shipped and is now delayed (we are supposed to release today). This Realm issue I'm having is breaking everything. It's only been introduced since v0.77.1. It was working fine in 0.76.1.

I don't want to revert to 0.76.1, I'm a big advocate for Realm I like it a lot, I'll be honest there's some mixed feelings about it in the office. Long story short, if I revert to Realm 0.76.1 it will require a modification on the back end to change back to the old way of passing data which is related to the problem we were having above. Passing a List of objects in the Report object from the api. So the guys in the office will know the new version is broken again and they'll likely want to steer away from it for good.

So moving on, to describe this issue I'm having I'll do my best to explain the setup as I really don't have the time constraints to put together a test project for the issue. If we can fix this with your knowledge of Realm then that would be a bonus.

So there are 2 versions of the project both of which I'm having the same issue. a Xamarin.IOS project with a core PCL sub project. And a Xamarin.Android project with a core PCL sub project. They both use the same core.
It's all built on MvvmCross with data bindings.
So here is how the data comes in specifically in regards to the Realm parts.

  • The ReportService class makes a web request and deserializes all the reports in to an IList container. These objects are standalone. The reports are then persisted to the Realm in a write transaction (db.Manage). Then the whole IList is passed back to the view model like so.
    return Reports.ToList();
  • Up until this point we're fine, from this point the core has a list of data (reports) that it needs to give to the IOS or Android project to display via data bindings. The data is stored in a List inside the ReportViewModel. After this point I just want to treat it as regular data, no more Realm transactions just pass it to the View and display it.
  • So the exception occurs where the binding happens.
    this.DelayBind(() =>
    {
    var set = this.CreateBindingSet<ReportItemView, Report>();
    set.Bind(LabelTitle).To("TradeType + ' ' + Title");
    set.Bind(LabelDate).To(vm => vm.RelevanceDate).WithConversion("DateLocalization");
    set.Bind(LabelStatus).To(vm => vm.Status);
    set.Bind(LabelStatus).For(l => l.TextColor).To(vm => vm.Status).WithConversion("StatusLabelColor");
    set.Bind(_imageCellViewLoader).To(vm => vm.TradeType).WithConversion("ResourceImage");
    set.Apply();
    });
    @kristiandupont Hi Kristian, starting to bang my head against the wall with this one. Feeling under pressure, this project was ready to be shipped and is now delayed (we are supposed to release today). This Realm issue I'm having is breaking everything. It's only been introduced since v0.77.1. It was working fine in 0.76.1.

I don't want to revert to 0.76.1, I'm a big advocate for Realm I like it a lot, I'll be honest there's some mixed feelings about it in the office. Long story short, if I revert to Realm 0.76.1 it will require a modification on the back end to change back to the old way of passing data which is related to the problem we were having above. Passing a List of objects in the Report object from the api. So the guys in the office will know the new version is broken again and they'll likely want to steer away from it for good.

So moving on, to describe this issue I'm having I'll do my best to explain the setup as I really don't have the time constraints to put together a test project for the issue. If we can fix this with your knowledge of Realm then that would be a bonus.

So there are 2 versions of the project both of which I'm having the same issue. a Xamarin.IOS project with a core PCL sub project. And a Xamarin.Android project with a core PCL sub project. They both use the same core.
It's all built on MvvmCross with data bindings.
So here is how the data comes in specifically in regards to the Realm parts.

  • The ReportService class makes a web request and deserializes all the reports in to an IList container. These objects are standalone. The reports are then persisted to the Realm in a write transaction (db.Manage). Then the whole IList is passed back to the view model like so.
    return Reports.ToList();
  • Up until this point we're fine, from this point the core has a list of data (reports) that it needs to give to the IOS or Android project to display via data bindings. The data is stored in a List inside the ReportViewModel. After this point I just want to treat it as regular data, no more Realm transactions just pass it to the View and display it.
  • So the exception occurs where the binding happens.
            this.DelayBind(() =>
            {
                var set = this.CreateBindingSet<ReportItemView, Report>();
                set.Bind(LabelTitle).To("TradeType + ' ' + Title");
                set.Bind(LabelDate).To(vm => vm.RelevanceDate).WithConversion("DateLocalization");
                set.Bind(LabelStatus).To(vm => vm.Status);
                set.Bind(LabelStatus).For(l => l.TextColor).To(vm => vm.Status).WithConversion("StatusLabelColor");
                set.Bind(_imageCellViewLoader).To(vm => vm.TradeType).WithConversion("ResourceImage");
                set.Apply();
            });

Each bind function binds the text value of a label (or something else) to properties in the view model. In this case the Reports list data is bound to table source. Then the above code is the bindings for each cell in the table. This is inside a class that is used by DequeueReuseableCell an iOS function.
This used to work fine before the update. But now as soon as it reaches this point it's getting the
Realms.RealmInvalidObjectException has been thrown
Attempted to access detached row.

As far as I'm aware there shouldn't be an issue here, I'm attempted to access the persisted realm data via data bindings from the view model. Off the top of my head is there a way I can create a list that is read only and no longer affected by the realm, I only want to use it for the displaying of data. Because it seems like somewhere along the line Realm thinks the data I have isn't associated with a realm. I don't have a single call to Realm.Close in my entire project.

@UKDeveloper99
Copy link

I think I've narrowed the problem down to just the binding of TableView cells. So somehow it's using data for the cell that is not attached to a Realm. As far as realm is concerned. It probably wasn't catching that in previous versions because I imagine that exception code is new in this version.

@kristiandupont
Copy link
Contributor

Hi @UKDeveloper99,

I will try and see if I can replicate your bug somehow but I suspect it might be a bit hard. I am sorry about this situation -- I hope we can resolve it. Are you sure that

  1. the realm you are binding to is still open (i.e. not scoped in a using statement etc), and
  2. the items being bound are not deleted, say, on another thread?

@UKDeveloper99
Copy link

UKDeveloper99 commented Aug 1, 2016

Thanks @kristiandupont
I imagine you can replicate it quite easily, just create an iOS project with a PCL. Persist some objects in the PCL (Add a view model with a list container property of those objects). Bind the list to a table view source. Bind the object properties inside the table cell class. I'll try and put together a test project this evening if I haven't fixed it by then.
Will look in to your other comments now.

@UKDeveloper99
Copy link

UKDeveloper99 commented Aug 1, 2016

@kristiandupont
Here's some code.

        public static readonly string Key = "ProductItemView";

        private readonly MvxImageViewLoader _imageViewLoader;

        public ProductItemView(IntPtr handle) : base(handle)
        {
            _imageViewLoader = new MvxImageViewLoader(() => this.ImageProductLogo);

            this.DelayBind(() =>
            {
                // Debug realm code
                var db = Realms.Realm.GetInstance(Settings.RealmFile);
                var products = db.All<Product>().ToList();

                var set = this.CreateBindingSet<ProductItemView, Product>();
                set.Bind(LabelName).To(vm => vm.Title);
                set.Bind(_imageViewLoader).For(imageLoader => imageLoader.ImageUrl).To(vm => vm.Icon);
                set.Apply();
            });
        }

I've put this debug Realm code right before the binding happens. (Products are essentially the same as reports in regards to the way they are bound to a table source/cell). This code gives me a list of 4 Products (which is correct). Realm reports them all as managed (true). So it's more likely the way the binding happens for table cells that is breaking something than Realm itself.

@UKDeveloper99
Copy link

@kristiandupont I'm creating a test project.

@AndyDentFree
Copy link
Contributor

@UKDeveloper99 Looking at your code, DelayBind stood out to me. Are you using MVVMCross?

Or is DelayBind your own method?

@UKDeveloper99
Copy link

@AndyDentFree I'm using MvvmCross yes correct.

@UKDeveloper99
Copy link

I've made the test project but can't seem to reproduce the problem. I shall endeavor further. Here it is just incase you guys want to take a look.
https://www.dropbox.com/s/61zohanjz9sg3c7/Sandbox.zip?dl=0

@eyadon
Copy link

eyadon commented Aug 1, 2016

I've been seeing similar issues with 0.77.1 and databinding. We're not using MvvmCross, and the same code works fine with 0.76.1. I'll see if I can pull mine out into a reproducible test project.

@eyadon
Copy link

eyadon commented Aug 1, 2016

Well, I don't seem to be able to reproduce the issue in a test project either. The real app has a rather complex navigation structure that I did not port over, and I suspect that might be part of the issue.

@UKDeveloper99
Copy link

Exactly the same as you. I'm only getting the error consistently in one part of my app. So trying to narrow down what's causing the issue.

@UKDeveloper99
Copy link

I'm just sorta throwing out a guess here, but I highly suspect that prior to 0.77.1
A lot of people's projects were littered with this detached row accessing but because there was no handling for it they didn't know about it. Now that exception check is in place it's popping up everywhere.

@UKDeveloper99
Copy link

Realms.RealmInvalidObjectException: Attempted to access detached row
at Realms.NativeException.ThrowIfNecessary () [0x00013] in :0
at Realms.RowHandle.get_RowIndex () [0x00009] in :0
at Realms.RealmObject.GetStringValue (System.String propertyName) [0x0001c] in :0
at FaradayAppV2.Core.DataModels.Report.get_Status () [0x00006] in :0
at at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00038] in /Users/builder/data/lanes/3412/3cf8aaed/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:295

Just can't get rid of it and no idea how to create it either.

@kristiandupont
Copy link
Contributor

kristiandupont commented Aug 1, 2016

@UKDeveloper99 Try this: add an additional property to your realm object, like this:

public string Debug 
{
    get
    {
        Console.WriteLine(this.Realm.IsClosed);
        return "";
    }
    set 
    {
        Console.WriteLine(this.Realm.IsClosed);
    }
}

Databind something to this property and see what is printed. Unbind the other properties so it stops crashing. Now, if at any point in time your console says true, we know that the problem is that the realm has been closed. If not, it must have been deleted somewhere. That will at least get us a bit further.

@UKDeveloper99
Copy link

UKDeveloper99 commented Aug 1, 2016

I modified it to this

public string DebugProp
{
    get
    {
        Debug.WriteLine(Realm.GetInstance(Settings.RealmFile).IsClosed);
        return "";
    }
    set
    {
        Debug.WriteLine(Realm.GetInstance(Settings.RealmFile).IsClosed);
    }
}

I'm not able to get access this.Realm from the realm object.

I got false for every bound report.

@kristiandupont
Copy link
Contributor

I'm not able to get access this.Realm from the realm object.

You should be, it's in the RealmObject class, which should be your base class? Calling GetInstance() is not the same as this will create a new realm if it was closed (albeit to the same file).

@UKDeveloper99
Copy link

UKDeveloper99 commented Aug 1, 2016

My object inherits from RealmObject but there is no property to access the Realm for the object. Only methods such as IsManaged etc. this.Realm is undefined.

You could confirm this by trying it yourself in the test project I sent.

The only visible properties in the parent class RealmObject are IsVisible and IsValid

@kristiandupont
Copy link
Contributor

Ah right, it's internal, sorry! Try to set a breakpoint and inspect it with the debugger instead.

@UKDeveloper99
Copy link

One moment

@UKDeveloper99
Copy link

In the locals inspector the IsClosed property under Realm is false for every one.

@UKDeveloper99
Copy link

It must be something to do with the way DequeueReusableCell works on iOS. And template selector on Android. I'm trying to think of workarounds for now.

The only thing I can think of is to copy the realm object to an intermediate object and bind to that instead.

@UKDeveloper99
Copy link

You said that the only other way the exception is thrown is if the object has been deleted. Is there a way like above to determine if the object has been deleted.

@UKDeveloper99
Copy link

If I step through the binding for every report, eventually I find one where every property has a yellow symbol. And says Realms.RealmInvalidObjectException: Attempted to access detached row. That must mean either some of the reports that are managed are corrupted somehow or it's related to this dequeue reusable cell.

@kristiandupont
Copy link
Contributor

The if you are running on the iOS simulator, you can keep the database open simultaneously in the Realm Browser. You can find the file as shown here: http://stackoverflow.com/a/28465803/1417293

@CrimsonKyle
Copy link
Author

I am running into the exact same situation that is occurring above and I googled for like 3 hours until I realized you all were talking about it under "my issue"... anyways I am trying to pass a list of deeply nested realm objects between pages and it is immediately throwing detached row errors. (this was working correctly in 0.76.1) also my locals variables for my realm isClosed = false

@CrimsonKyle
Copy link
Author

So I am querying a List of a parent object with 4 levels of nested realm lists under them into a List of objects in a list view. when I select a row in the list view it is pushing to a page detail view on this view the data is detached.
screen shot 2016-08-01 at 5 58 51 pm

@CrimsonKyle
Copy link
Author

Using IList not Realm List

@UKDeveloper99
Copy link

Feels good I'm not the only one having that issue. It's also happening on my nested objects, so that may be the issue.

@kristiandupont
Copy link
Contributor

Do you guys have any Realm.Remove() statements anywhere in your code? I don't know if it's feasible at all, but if you do, can you out-comment those and see if you get the same error? That should be the only other way an object can become detached.

@UKDeveloper99
Copy link

Morning @kristiandupont, this is probably not what you want to hear, but I'm fairly certain there are no calls made to Realm.Remove in my code before the error occurs :(. I've already reverted to 0.76.1. Going to have to release the project like that.

@kristiandupont
Copy link
Contributor

I've created #746 for this issue, please move further discussion there.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

6 participants