Enforce Referential Integrity


#1

I want to ensure every Job has a valid JobType.

public class JobType : RealmObject
{
    [PrimaryKey]
    public string JobTypeId { get; set; } = Guid.NewGuid().ToString();

    [Backlink(nameof(Job.JobType))]
    internal IQueryable<Job> Jobs { get; }
}

public class Job : RealmObject
{
    [PrimaryKey]
    public string JobId { get; set; } = Guid.NewGuid().ToString();
    public JobType JobType { get; set; } 
}

However this still allows a Job.JobType = null. I could initialise JobType, then use the setter to prevent it from being null. Does Realm have a mechanism to ensure referential integrity?


#2

@Nosl We don’t support this at this time


#3

Given Realm does not enforce referential integrity, how should we design to enforce referential integrity? I’ve tried enforcing referential integrity in the property setter like this:

internal class Customer : RealmObject
{
    [PrimaryKey]
    public string CustomerId { get; set; }

    [Backlink(nameof(Order.Customer))]
    internal IQueryable<Order> Orders { get; }
}
internal class Order : RealmObject
{
    [PrimaryKey]
    public string OrderId { get; set; }
    private Customer _Customer;
    public Customer Customer
    {
        get => _Customer;
        set => _Customer = value ?? throw new System.ArgumentNullException("Customer cannot be null for Order");
    }
}

On startup this throws

Schema validation failed due to the following errors:

  • Property ‘Order.Customer’ declared as origin of linking objects property ‘Customer.Orders’ does not exist

Using Realm Browser I am able to see that none of the Orders in the test database has a null Customer, so why do we get this error?


#4

The actual name of the property in the Realm database is _Customer, so the backlinks property needs to point to that (Customer is unknown to Realm because it’s not an automatic property). You have two options:

  1. Update the Backlink attribute and tell it to use _Customer. Since this is a private property, you’ll need to explicitly type the string value like: Backlink("_Customer"). _Customer also needs to be a property, not a field for Realm to pick it up.

or

  1. Add a MapTo attribute on the _Customer property:
[MapTo(nameof(Customer)]
private Customer _Customer { get; set; }

#5

Thank you @nirinchev however changing the Customer property on the Order class to this

[MapTo(nameof(Customer))]
private Customer _Customer;
public Customer Customer
{
    get => _Customer;
    set => _Customer = value ?? throw new System.ArgumentNullException("Customer cannot be null for Order");
}

gives this error
Attribute 'MapTo' is not valid on this declaration type. It is only valid on 'class, property, indexer' declarations.


#6

Yes, as I mentioned, you need to make _Customer an automatic property to be picked up by Realm.


#7

but how then do we do ensure the property is not null?


#8

Well, it’s a private property, so you wouldn’t be able to set it, except when using the Customer property which does the validation.


#9

So on the Order class we have

private Customer _Customer;
public Customer Customer
{
    get => _Customer;
    set => _Customer = value ?? throw new System.ArgumentNullException("Customer cannot be null for Order");
}

and on the Customer class

[Backlink("_Customer")]
internal IQueryable<Order> Orders { get; }

Does that look right?


#10

Apologies @nirinchev I was being a bit obtuse there. I missed that you had indicated both public and private setters. That seems to work. Thanks for your help.


#11

One hopefully minor point. When I decorate the Customer property of the Order class

[MapTo(nameof(Customer))]
private Customer _Customer { get; set; }
public Customer Customer
{
    get => _Customer;
    set => _Customer = value ?? throw new System.ArgumentNullException("Customer cannot be null for Order");
}

I get a successful build but a warning:

Fody/RealmWeaver: Order.Customer is not an automatic property but its type is a RealmObject which normally indicates a relationship.

Is that something to worry about?


#12

No, that’s fine - we added this warning because people sometimes assume they can write any complex property and have it persisted into Realm. Since you’re doing that on purpose, you can ignore the warning.