realm / realm-dotnet

Realm is a mobile database: a replacement for SQLite & ORMs
https://realm.io
Apache License 2.0
1.25k stars 165 forks source link

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

Closed CrimsonKyle closed 8 years ago

CrimsonKyle commented 8 years ago

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 commented 8 years ago

I am using RestSharp by the way for the HTTP client

fealebenpae commented 8 years ago

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 commented 8 years ago

This was a game changer!

UKDeveloper99 commented 8 years ago

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

kristiandupont commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

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

UKDeveloper99 commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

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

UKDeveloper99 commented 8 years ago

@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.

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.

            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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

@kristiandupont I'm creating a test project.

AndyDentFree commented 8 years ago

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

Or is DelayBind your own method?

UKDeveloper99 commented 8 years ago

@AndyDentFree I'm using MvvmCross yes correct.

UKDeveloper99 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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

UKDeveloper99 commented 8 years ago

One moment

UKDeveloper99 commented 8 years ago

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

UKDeveloper99 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

Using IList not Realm List

UKDeveloper99 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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