Backlinks and Many-To-Many Relationships


#1

Mailing lists have many customers on them. Each customer is on a number of mailing lists

internal class Customer : RealmObject
{
    [Backlink(nameof(MailingList.Customers))]
    internal IQueryable<MailingList> MailingLists { get; }
}
internal class MailingList : RealmObject
{
    [Backlink(nameof(Customer.MailingLists))]
    internal IQueryable<Customer> Customers { get; }
}

Throws build errors

Fody/RealmWeaver: The property ‘MailingList.Customers’ does not constitute a link to ‘Customer’ as described by ‘Customer.MailingLists’
and
Fody/RealmWeaver: The property ‘Customer.MailingLists’ does not constitute a link to ‘MailingList’ as described by ‘MailingList.Customers’

Realm docs use Ship and Captain as an example of a to-many relationship, but in that case a Captain is a property of Ship. In our case Customer is not a property of MailingList and MailingList is not a property of Customer.

How is this model best implemented in Realm?


#2

You need at least one of these to be an IList<Other>, e.g.

internal class Customer : RealmObject
{
    [Backlink(nameof(MailingList.Customers))]
    internal IQueryable<MailingList> MailingLists { get; }
}
internal class MailingList : RealmObject
{
    internal IList<Customer> Customers { get; }
}

Then when you add customers to a mailing list, their MailingLists property will be updated to contain all lists they’ve been added to.


#3

Thank you @nirinchev. That seems to have done the trick.


#4

It seems @nirinchev that this goes wrong if we try to add an existing customer to a mailing list.

using (Realm realm = RealmService.GetRealm()) realm.Write(() =>
{
    MailingList.Customers.Add(customer);
    realm.Add(customer, update: true);
});

throws

RealmDuplicatePrimaryKeyValueException A Customer object already exists with primary key property CustomerId == ‘9437eeab-e861-4106-8f34-0b1e121220d7’

How do we add an existing customer to a mailing list? We don’t really want to delete the customer then recreate them as the customer participates in multiple collections.


#5

Invert the order of operations:

realm.Add(customer, update: true);
MailingList.Customers.Add(customer);

The reason it matters is that customer is unmanaged when you try to add it to the MailingList.Customers collection. This will trigger logic to try and add it to the Realm but with an update: false flag because the IList method signature doesn’t allow to pass update parameter. This then fails because it’s a duplicate entry. When you inverse the order of operations, the existing customer object will be updated and the customer instance will be attached to the Realm. Then adding it to the Customers collection will succeed because we won’t attempt to re-add it.


#6

That works a treat thank you @nirinchev. And thank you for the explanation.