Unable to subscribe to notification on sorted results


#1

I get this exception when trying to subscribe to notifications on a sorted query,

results must be an instance of IRealmCollection<Question>.
Parameter name: results'
var questions = value.questions.OrderBy(s => s.number).AsQueryable().AsRealmCollection();
questionToken = questions.SubscribeForNotifications((sender, changes, error) =>
                    {…}

There is no compiler error but it throws an exception at runtime. Works fine without the OrderBy().


#2

Remove the call to AsQueryable - you don’t need it.


#3

If only…

Compiler error when I tried to use that.

Error CS1061 'IOrderedEnumerable<Question>' does not contain a definition for 'AsRealmCollection' and no accessible extension method 'AsRealmCollection' accepting a first argument of type 'IOrderedEnumerable<Question>' could be found (are you missing a using directive or an assembly reference?)

property definition of ‘questions’
public IList<Question> questions { get; }


#4

Ah… you’re sorting a List, that explains it. Unfortunately, filtering/sorting the list will cause it to lose its “liveliness” and you can’t subscribe for notifications on that. The reason is that applying an ordering via LINQ will return a new object that is not connected to the database anymore, so there’s no way to notify it that something has changed.


#5

But it’s OK to sort a query is it ?


#6

Yes, queries can be sorted and the “liveness” will be preserved, but you can’t convert a list to a query by casting it with AsQueryable. It’s still a list under the hood and sorting it will be done by LINQ-to-objects in memory.


#7

A bit counterintuitive - does this apply for any OS or is this a .Net feature? So one would need to store a reference to the parent as well and then query on that property and then apply the sort to get a live sorted list. Begs the question them why would one bother to use IList at all then. Easier to have a calculated property that returns the sorted children. Any plan to change this or is this driven by some technical limitation?

Might be worth added something to your documentation under notifications to explain some of these limitations and workarounds.


#8

This is just how LINQ works. When a LINQ query is run on an IQueryable, it translates this to a database query, which is then executed by Realm. When a LINQ query is run on IEnumerable, IList or any of the other collection types, it is executed by the LINQ-to-objects implementation and is completely unrelated to Realm. You’ll get the same result if you cast an IQueryable to IEnumerable or if you call .ToArray() on any query result - next time you run a LINQ query, that will be processed by LINQ-to-objects. The AsQueryable method provided by LINQ will not turn a collection to a Realm query (because LINQ is not aware of Realm at all) - it will just return the LINQ-to-objects queryable implementation that processes everything in memory.

Note that this applies to any databases that implement an IQueryable provider, not just Realm, which is why we have not explicitly outlined this in the documentation. But to go back to your immediate question - lists are useful when you care about the order of the elements (e.g. a TODO list where the user can reorder the tasks as they wish). If you don’t, then it’s better to use an inverse relationship, which is represented by an IQueryable property.

So a model like:

public class Exam : RealmObject
{
    public IList<Question> Questions { get; }
}

public class Question : RealmObject
{
}

can be reworked to use inverse relationships like:

public class Exam : RealmObject
{
    [Backlink(nameof(Question.Exam))]
    public IQueryable<Question> Questions { get; }
}

public class Question : RealmObject
{
    public Exam Exam { get; set; }
}

Then you can query, sort, and so on the Questions collection. That being said, if you only want to sort the questions by number in your original list, why not just insert them in the correct order in the list in the first place? Then you don’t need the sorting because the lists are inherently ordered.


#9

Brilliant explanation thanks - did I miss this in the docs, if not you should add this.

Bottom line is I shouldn’t be using lists, the sorting requirement varies depending on the situation.

RealmSwift appears to have no option other than List<> for ‘many’ relationships. Presumably the Swift Lists will remain ‘live’ objects when filtered or sorted and behave like the C# [Backlink].


#10

Swift also has the concept of backlinks, but it’s called LinkingObjects. And yes, I believe you can sort and filter a list property in Realm swift, but that is because they don’t have LINQ and have exposed custom methods on the List class. In c# it felt like it’s going to be confusing to have methods with names very similar to the LINQ names, that do almost the same thing, but keep the collection live.

Re: docs - I agree, we should consider adding a section that helps you choose which collection to use in your models - List vs backlinks.


#11

Given one of your main selling points is that the objects are ‘live’ it seems odd that you would then compromise that basic feature. Nevertheless I will see how using backlinks works out.