Setting up Custom User Data

Hi there,

Perhaps someone can help me, I am trying to set up custom user data but keep running into problems and I’m not sure the documentation is that clear. I am using the .NET SDK.

I have a series of RealmObjects which form my normal user data (such as Book, Person etc…). They are using a property _id as their primary key and a partition key of ownerId which is the user’s ID, all as suggested in the documentation, working very nicely.

Then following the documentation further I am adding a CustomUserData class. This is not a subclass of RealmObject. This class has a property _id which is the user’s ID and identifies it as belonging to that user. There is no ownerId property.

This is how I have sync set up in Realm:

And this is how I have Custom User Data set up:

As far as I can tell I have set everything up correctly as per the documentation. I get an error in the app when trying to access the data: Unknown: no rule exists for namespace UserData.CustomUserData. OK fair enough, so on Realm, in Data Access=>Rules I find the newly added CustomUserData and select the ‘Users can only read and write their own data’ template’. This required a ‘Field Name For User ID’. This is where I get a bit confused, it’s not really in the docs how this should be configured for custom user data. Is this then the _id property? After I have done this I get a schema error:

OK so I can go back to the app now, and custom user data works. I can then generate the schema in Realm and it looks like this:

{
  "title": "CustomUserDatum",
  "properties": {
    "Company": {
      "bsonType": "string"
    },
    "FirstName": {
      "bsonType": "string"
    },
    "LastName": {
      "bsonType": "string"
    },
    "_id": {
      "bsonType": "string"
    }
  }
}

So far so good. When I try to save the schema it’s not possible as I get the error schema for namespace (UserData.CustomUserData) must include partition key "ownerId". Oh dear. I can add it to keep it quiet but then I get the error: schema for namespace (UserData.CustomUserData) must have consistent types of partition key "ownerId" (expected: string, required: true; actual: string, required: false). If I would make it required I would need that property in the class too and… basically I am going around in a lot of circles and not sure exactly what I am supposed to be doing.

Could anyone tell me where I am going wrong here? I would suggest the documentation could be a little clearer too for people who aren’t the brightest such as me :stuck_out_tongue_winking_eye:

Many thanks!!

Will

Hi Will, thanks for the detailed explanation!

The first thing that I’d mention is that the collection for your custom user data doesn’t need to be in a different database (I’ve always used the same database as I’m using for synced data – I don’t think that would cause the problem you’re seeing though).

You shouldn’t need to add a schema or any rules in order for the custom user data to be readable from your app. Also, that data shouldn’t need to include the partition key.

I’ve not used the .NET SDK but from the docs, this is how to read the custom user data from your app:

await user.RefreshCustomDataAsync();

// Tip: define a class that represents the custom data
// and use the gerneic overload of GetCustomData<>()
var cud = user.GetCustomData<CustomUserData>();

Console.WriteLine($"User is cool: {cud.IsCool}");
Console.WriteLine($"User's favorite color is {cud.FavoriteColor}");
Console.WriteLine($"User's timezone is {cud.LocalTimeZone}");

From some of the errors you’re seeing, it sounds as though your app may be trying to read directly from your CustomUserData collection and/or sync it (e.g. by having a CustomUserData class that inherits from RealmObject) – you don’t need to do that. Messages saying that no rule is set up hints that you’re trying to read from the collection directly, messages that the partitioning key is missing hints that you’re trying to sync it.

As CustomUserData is not in the database that you set up sync for, I wouldn’t expect that to work (but you shouldn’t need to do it).

2 Likes

Hi Andrew,

Thanks for your reply. As far as I can tell I have implemented everything as you suggested and is suggested in the documents, the app is reading and writing data only as suggested and the CustomUserData class does not implement RealmObject. The only thing I am unsure of is what you mean when you say trying to read the collection directly?

Many thanks

Will

EDIT: This is the log entry:

Error:

Action on service 'mongodb-atlas' forbidden: arguments to 'findOneAndReplace' don't match rule criteria
Stack Trace:

FunctionError: no rule exists for namespace 'LogbookData.CustomUserData' at <eval>:16:4(4)
Details:
{
  "action": "findOneAndReplace",
  "reason": "arguments to 'findOneAndReplace' don't match rule criteria",
  "serviceName": "mongodb-atlas",
  "serviceType": "mongodb-atlas"
}
{
  "arguments": [
    {
      "database": "LogbookData",
      "collection": "CustomUserData",
      "filter": {
        "_id": "604a3ba17d7ff51c8c69f15e"
      },
      "update": {
        "_id": "604a3ba17d7ff51c8c69f15e",
...
      },
      "upsert": true
    }
  ],
  "name": "findOneAndReplace",
  "service": "mongodb-atlas"
}

Thanks

Hi Will, by reading the collection directly, I mean using the SDK to read or write from the UserData.CustomUserData collection explicitly (rather than vis the custom user data feature) – in which case, the Realm data access rules would be applied. https://docs.mongodb.com/realm/sdk/dotnet/examples/mongodb-remote-access/

Thanks Andrew,

The problem is occurring when trying to add data for the first time. I am following the instructions which seem to suggest adding data directly:

app = App.Create(myRealmAppId);
user = await app.LogInAsync(Credentials.Anonymous());

mongoClient = user.GetMongoClient("mongodb-atlas");
dbTracker = mongoClient.GetDatabase("tracker");
cudCollection = dbTracker.GetCollection<CustomUserData>("user_data");

var cud = new CustomUserData(user.Id)
{
    FavoriteColor = "pink",
    LocalTimeZone = "+8",
    IsCool = true
};

var insertResult = await cudCollection.InsertOneAsync(cud);

This is the point where the exception is thrown.

Will

Hi Will,

that makes sense now.

The issue is that Realm Sync is expecting every collection that’s configured in your Atlas service to be synced (and so require the partition key).

There’s no reason to sync CustomUserData and so you shouldn’t include it in your mongodb-atlas service. However, you need to configure the CustomUserData collection in your Realm app – I can see why this feels like you’re going round in circles!

The solution is to create a second MongoDB service in your Realm app that’s linked to the same Atlas cluster…

From your Realm app’s perspective, you now have two MongoDB services, but they’re actually working on the same Atlas cluster (so can access the same data).

You can now add a rule for the UserData.CustomUserData to your atlas-custom-user-data service…

Your code for writing to that collection would then look something like this (using the new service name):

app = App.Create(myRealmAppId);
user = await app.LogInAsync(Credentials.Anonymous());

mongoClient = user.GetMongoClient("atlas-custom-user-data");
dbUser = mongoClient.GetDatabase("UserData");
cudCollection = dbUser.GetCollection<CustomUserData>("user_data");

var cud = new CustomUserData(user.Id)
{
    FavoriteColor = "pink",
    LocalTimeZone = "+8",
    IsCool = true
};

var insertResult = await cudCollection.InsertOneAsync(cud);
1 Like

Thanks Andrew!

That works great, no sync issues and no errors showing on the Realm portal. The only thing that is slightly annoying is all classes are displayed here still:

but I think there is no way around that.

Many thanks for your help, all the best

Will

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.