Search code examples
c#.netrealm

How can you add properties annotated with PrimaryKey to existing RealmObject model objects in Realm .NET?


I am using Realm .NET to store model objects in a realm database. I have an existing model object called Employee with a single property:

public class Employee : RealmObject
{
    public string Username { get; set; }
}

The Realm database already contains a few instances of this type.

I am now trying to add a new property to the object which should be annotated with the [PrimaryKey] attribute. The desired new version of the object should look like this:

public class Employee : RealmObject
{
    [PrimaryKey]
    public string Id { get; set; }

    public string Username { get; set; }
}

Since the new Id property will not contain suitable values for the existing database objects, I am trying to use the migration feature in Realm to seed their values. The code to perform the Realm migration looks like so:

private void MigrateToSchemaVersionWithEmployeeIds(Migration migration)
{
    var employees = migration.NewRealm.All<Employee>();
    foreach (var employee in employees)
    {
        employee.Id = Guid.NewGuid().ToString();
    }
}

When starting the app, the migration code is executed but when assigning to the Id property, an Exception is thrown:

System.InvalidOperationException: Once set, primary key properties may not be modified.

As a workaround, I have determined that you can first add the property without the [PrimaryKey] attribute, have its values seeded in the migration code (no exception is thrown in this case), stop the app, add the [PrimaryKey] attribute to the property, increase the schema version and relaunch the app. As this is no workable solution for when an app is already deployed in production, I am looking for a way to achieve this without having to use such a workaround.

How can a property with a [PrimaryKey] attribute be added to an existing model object in Realm .NET?


Solution

  • That is unfortunately, not possible. As a workaround, you can map your Employee class to a new table like:

    [MapTo("Employee2")]
    public class Employee : RealmObject
    {
        [PrimaryKey]
        public string Id { get; set; }
    
        public string Username { get; set; }
    }
    

    It will allow you to keep using it as Employee for all intents and purposes, but in the database itself it will be persisted as Employee2. Then in the migration block, you could do:

    private void MigrateToSchemaVersionWithEmployeeIds(Migration migration)
    {
        var employees = migration.OldRealm.All("Employee");
        foreach (var employee in employees)
        {
            var newEmployee = new Employee
            {
                Id = Guid.NewGuid().ToString(),
                Name = employee.Name
            };
            migration.NewRealm.Add(newEmployee);
        }
    }