Xamarin C# Realm Dictionary behavior

Hi,

I’m currently using a MongoDB Atlas Realm with a Xamarin App for mobile phones (iOS and Android) in Development Mode. I created a new RealmObject that contains several dictionaries that use a string as a key and the value contains an object similary to the classes below…

public class StringAndInt : EmbeddedObject
{
    [MapTo("name")]
    public string Name { get; set; }

    [MapTo("number")]
    public int Number { get; set; }

    public StringAndInt()
    {
    }

    public StringAndInt(string name, int number)
    {
        Name = name;
        DayNumber = number;
    }
}
public class App_Dictionaries : RealmObject
{
    [PrimaryKey]
    [MapTo("_id")]
    public ObjectId Id { get; set; } = ObjectId.GenerateNewId();

    [MapTo(Constants.Partition)]
    [Required]
    public string Partition { get; set; } = "a_partition"; //just an example

   [MapTo("string_and_int_dictionary")]
    public IDictionary<string,  StringAndInt> StringAndInt { get; }

}

In my code, I created a new instance of the App_Dictionaries, a new instance of StringAndInt, and added it successfully to the Realm. This worked fine for the first (to make sure it worked) time. However, when I subsequently added another IDictionary (say, an object with a string, an int, and another string) to the RealmObject above, instantiating it worked, but writing to the Realm did not. Turns out that even tho development mode was ON, creating a Realm Object with IDictionary like the above code created Schemas for all the collections in my database. I was curious if this was a feature or a problem. It took a bit of poking around before I realized these Schemas had been created even tho the app was set to Development Mode on. As far as I was able to test, even deleting the schema wouldn’t let me do any subsequent changes to the RealmObject without getting rid of it, deleting the schemas, and building it all again in one shot with no subsequent alterations. Might be a Fody Weave problem? Thanks

Hi @Josh_Whitehouse, thanks for your message.

I am a little bit confused about what is the issue here. If I understood correctly is that you added another dictionary property to App_Dictionaries and the schema got changed on Atlas, am I correct? If that is so, then this is by design with development mode. The main idea with development mode is that you don’t need to create the schema yourself in Atlas, but it’s synchronized automatically from the app. This doesn’t work for all changes, as some are incompatible, but it would work for an additive change like adding a new property.

If this was not your doubt maybe you can show some pieces of code of what your’re trying to do and what you’re getting and we can take a look at it.

Hi Ferdinando, it wasn’t clear to me that development mode created a schema. One hadn’t been created by my app until I added the collection containing embedded objects, each a dictionary. So, when working with these embedded dictionaries and not seeing the updates to them in the app, the creation of the schema (for all root collections did confuse me).

It looks like with that schema generated by the app, I can’t add any additional dictionaries as embedded objects to the collection I store them in. I tried from my app, but the new dictionary didn’t show in the atlas. So, in my example above, I tested and successfully created an “App_Dictionaries” RealmObject, added a IDictionary<string, StringAndInt> StringAndInt, and saved it to the Realm. However, if I tried to add an additional IDictionary as an embedded object (say an IDictioary<int, StringAndInt>) to the App_Dictionaries object, it didn’t make it into the Realm. I could only successfully load ALL the dictionaries into the App_Dictionaries object at one time, not incrementally.

Another related issue (as I was continuing to work with this) - it seems like there’s no way to get change notifications for these dictionaries that are embedded objects, other than the PropertyChanged for the collection holding the dictionaries (guessing because they are embedded?). So I can’t register for changes at the dictionary level (which would be more granular), only the collection containing the dictionaries. Is there a way to register for changes to a collection that are sub documents?

Thanks for your help here.
Josh

Hi @Josh_Whitehouse, I understand the source of confusion. Development mode actually was created exactly to infer the schema directly from the objects that are synced. You can find more info in the docs. In the same section you can also find information about how to update your schema, and also what kind of changes are considered breaking changes. If you didn’t do it yet, I would suggest giving those pages a look, as they explain how all of this works in practice.

Regarding why adding a new dictionary didn’t update the schema, I think there could be different things happening here. What could be happening is that you introduced a breaking change and so you need a client reset. You should check the logs in Atlas to verify that.
Another thing is that only dictionaries with strings as keys are supported, so if you’re using a property like IDictionary<int, StringAndInt> this will not work. Actually this should give a Fody error after compilation, and please let us know if it does not.

Regarding the notifications, you can actually use AsRealmCollection to get the underlying realm collection of a IDictionary property. This allows you to subscribe for notifications or even subscribe to INotifyCollectionChanged/INotifyPropertyChanged events.
So you can do something like:

var appDict = new App_Dictionaries();
appDict.StringAndIntDict.AsRealmCollection().SubscribeForNotifications(callback);
//Alternatively
appDict.StringAndIntDict.AsRealmCollection().CollectionChanged += handler;

I hope this helps some of your doubts.

Thanks, Ferdinand, for your response, it is helpful. I’ll do a test and look for a breaking change in the Atlas log and report if I see it. Likely it was, and I’d need to make sure of a client reset under these conditions.

As to the IDictionary<int, StringAndInt> example, I’m not actually using an int as a key, it was a quick and sloppy example on my part, I didn’t mean to introduce confusion with it.

I’ll try AsRealmCollection() for notifications. I wasn’ t receiving notifications when I did prior, but I also wasn’t getting notifications for the collection containing the dictionaries. I believe this was a threading context issue (where I was registering for the notifications, is this possible to do?) which I changed, and now I receive notifications for the collection containing the dictionaries fine. I’ll test AsRealmCollection() and let you know.

Thanks again for your help,

Josh

Regarding notifications, you should register for them on the main thread of your application, otherwise the realm doesn’t “advance” his internal version and notifications aren’t calculated. So yes, it could be a threading issue if you’re registering for them in another thread.

This was resolved by making sure I registered for notifications on the main thread.