C#, dynamic, Realm

Using Realm Notifications in .Net

There is a documentation on Realm.io, that describes how to hook up on a ROS (Realm Object Server) to observe changes on specific Realms, RealmObjects or whole instances. So there is no need to actually download the whole Realm, since it “sees” the changes directly on the server. It is also pretty cool, that you don’t have to load the assemblies that contain the correct RealmObjects (and version), because it uses the .Net DynamicObject feature.

https://docs.realm.io/sync/backend-integration/data-change-events#integrating-with-a-3rd-party-api

Sadly, this only works with node.js not in .Net…

There is currently only one work around, that really works with .Net Standard, that I have found. You open the specific Realm in dynamic mode and use the method SubscribeForNotifications. Be sure to have a separate Thread running for the notifications to work properly.

static async Task Main(string[] args)
{
    var adminUser = await User.LoginAsync(Credentials.UsernamePassword("aUser", "****", false), new Uri("https://rosurl"));
    var realmSyncConfig = new Realms.Sync.FullSyncConfiguration(
            new Uri("SomeRealm", UriKind.Relative), adminUser);
    realmSyncConfig.IsDynamic = true;
    Nito.AsyncEx.AsyncContext.Run(async () =>
    {
        var realm = await Realm.GetInstanceAsync(realmSyncConfig);
        var realmCollection = realm.All("Organization") as RealmCollectionBase<RealmObject>;
        var token = realmCollection.SubscribeForNotifications(RealmChanged);

        while (true)
        {
            // you can use a cancellation token here 
            await Task.Delay(1000);
        }
        token.Dispose();
        realm.Dispose();
    });
    Console.ReadLine();
}

private static void RealmChanged(IRealmCollection<RealmObject> sender, ChangeSet changes, Exception error)
{
     /* here you can handle changes */
}

There are some disadvantages when you use this solution:
1. The Realm that you want to subscribe changes on will be fully synced. (So it’s not a good solution for very large Realms)
2. You can’t just watch your whole instance, you need to actually know the full name of your Realm

C#, dynamic, Realm, Reflection

Copy an entire synced Realm

In our company, we use realm.io as mobile Database with the ROS Cloud to sync data between mobile devices and the server. Main advantage is, that we can build Apps in a fraction of time, without juggling with synchronization.

One of our main challenges was the disability to copy data from one realm to another. But there is still a good solution for this, even though you have to do a lot of coding for that.

Motivation

There are a lot of reasons, why copying realms is a really neat feature. First of all would be versioning your data. But it’s also very neat, when you have several instances (for development, testing etc.) to copy the whole data when you go productive or need some test data in development.

Challenge 1: generating the Schema

When we copy one realm to another, we actually don’t know the used types. We could include a library which contains the types, but then our “RealmCopy” will only work for a specific realm, which is really baloney. That’s as if you develop a copy-program, that can only copy text-files.

So we need to read the source realm, get the schema and create the same schema in the destination. In .Net we can open a realm dynamically, which is pretty cool, because this gives us a chance to get the information we need. Only problem – we can not create or change a schema on a dynamically opened realm.

Solution? We can create dynamic types, and open the destination realm, this will automatically create a schema on the destination realm. (It’s really a little bit awkward, but that’s currently the only chance in .Net – with Javascript, we can mutate the realm directly, but that’s probably due to the nature of that language)

RealmCopy

Open a realm as dynamic:

var username = "";
var password = "";
var realmUrl = "";
var realmName = "TestRealm";
var user = await User.LoginAsync(Credentials.UsernamePassword(username, password, false), new Uri(realmUrl));

var configuration = new FullSyncConfiguration(new Uri(realmName, UriKind.Relative), user)
{
IsDynamic = true
};
var srcRealm = await Realm.GetInstanceAsync(configuration);

now we can go through the scheme and create types:

var types = new List();
foreach (var scheme in schemasToProcess)
{
// create an assembly for our type
var assemblyName = new AssemblyName($"SomeAssemblyName{name}");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
// create an module for the type
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
// get the typebuilder as
var typeBuilder = moduleBuilder.DefineType(name,
TypeAttributes.Public |
TypeAttributes.Class
, parent);
// add properties to type
foreach (var prop in schema)
{
var attributes = new List();
Type propType;
// woven property is needed, so Realm will add it to the destination schema
attributes.Add(typeof(WovenPropertyAttribute));
if (prop.IsPrimaryKey)
{
// add primary key attribute
attributes.Add(typeof(PrimaryKeyAttribute));
}
else if (prop.IsIndexed)
{
// add indexed attribute
attributes.Add(typeof(IndexedAttribute));
}

// get the property type
propType = prop.GetPropertyType(knownTypes);

// set required attribute, when its not nullable
if ((prop.Type & PropertyType.Nullable) != PropertyType.Nullable)
{
attributes.Add(typeof(RequiredAttribute));
}

// create the property
MyTypeBuilder.CreateProperty(typeBuilder, prop.Name, propType, attributes.ToArray());
}
types.Add(typeBuilder.CreateType());
}

In one of my previous blog posts I mentioned how to create types using the TypeBuilder, so I won’t code it out here.
The function GetPropertyType is an extension, it’s just a mapping of the Realm-PropertyType to a CLR type.
Now we can open the destination Realm:

var configuration = new FullSyncConfiguration(new Uri(realmName, UriKind.Relative), user);
configuration.ObjectClasses = types;
var destRealm = await Realm.GetInstanceAsync(configuration);

Challenge 2: copying the realm

This doesn’t actually seem to be a challenge. We go through all objects in source realm and copy it to a new RealmObject, which is created in destination. You can actually use our generated types to open the source realm and use straight reflection for mapping all properties. But be careful, if you did something wrong, you would probably mutate the source realm (what you might not want). You can stick to the dynamic Realm instead and use CallSiteCache to read the properties and reflection to set it to the destination object. (more infos)

Challenge 3: cascaded objects

An object may not only reference atomic types, but also have other RealmObjects as properties. It can even contain cycles. This is really tricky, I solved it by initially creating a TypeBuilder for each SchemaItem, then we can use this TypeBuilder as a PropertyType, without building the actual type. Creating objects can be done recursively, but keep track of the objects, you already created, so you can avoid cycles.

If you have any questions, feel free to ask. ..have fun coding.

C#, dynamic, Reflection, Uncategorized

read and write properties on dynamic objects in .NET/C#

Ok, the headline sounds a little bit funny, because first of all, dynamic objects have no properties. Also you can not use reflection on a dynamic type, because it’s dynamic. What I actually want to do, is to access values of a dynamic object by its name – but I don’t know the name of the property on compile time, but during execution.

I want to do something like that:

dynamic obj = GetDynamicObject();
string myPropertyValue = DynamicExtension.GetValue(obj, "MyProperty");
DynamicExtension.SetValue(obj, "MyProperty", "some cool value");

So how can we do this? Reflection is not possible, and not all dynamic types impement IDictionary like ExpandoObject.

But how can we achieve this? By looking at the disassembled code, of a dynamic class, and the way it is accessed, we can find out, how we can access it ourself…

dynamic dynObj = new TestClass
{
  TestProperty = "Hello World",
  TestInt = 20
};
var val = dynObj.TestProperty;
dynObj.TestProperty = "Hello C#";

Because I don’t want to flood this post, I just copied the essentials from the IL. Basically there are 2 parts:
1. create a Binder
2. create a CallSite to execute the getter/setter.

// the getter
            var binder = Binder.GetMember(
                CSharpBinderFlags.None,
                "TestProperty",
                typeof(object),
                new CSharpArgumentInfo[] { 
                 
 CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, null)
                });
            
            var getter = CallSite<Func>.Create(binder);
            var val = getter.Target(getter,  dynObj);

// the setter
var setterBinder = Binder.SetMember(
    CSharpBinderFlags.None,
    "TestProperty",
    typeof(object),
    new CSharpArgumentInfo[] {
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) });
// the IL actually uses string as value type, but we use object here, to be more flexible
var setter = CallSite<Func>.Create(setterBinder);
setter.Target(setter, dynObj, "Hello C#");

So now just wrap it up to our two functions:

    public static class DynamicHelper
    {
        public static object GetValue(object obj, string name)
        {
            var binder = Binder.GetMember(
                        CSharpBinderFlags.None,
                        name,
                        typeof(object),
                        new CSharpArgumentInfo[] {
                            CSharpArgumentInfo.Create(
                                CSharpArgumentInfoFlags.NamedArgument,
                                null)
                        });

            var getter = CallSite<Func>.Create(binder);
            return getter.Target(getter, obj);
        }

        public static void SetValue(object obj, string name, object value)
        {
            var setterBinder = Binder.SetMember(
                CSharpBinderFlags.None,
                name,
                typeof(object),
                new CSharpArgumentInfo[] {
                  CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                  CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) });
            // the IL actually uses string as value type, but we use object here, to be more dynamic
            var setter = CallSite<Func>.Create(setterBinder);
            setter.Target(setter, obj, value);
        }
    }