Skip to content

Latest commit

 

History

History
215 lines (146 loc) · 11.2 KB

linqsupport.md

File metadata and controls

215 lines (146 loc) · 11.2 KB

LINQ support in Realm Xamarin

To make a query with Realm, you use the Realm.All<T>() method to get a IQueryable<T> instance. On this you can then apply the operators listed below.

Restriction Operators

Where is supported. OfType is not but it would be redundant as a query in Realm will always consist of a collection of the class initially specified.

Where takes a predicate. To see the supported operations for predicates in Realm queries, refer to the Predicate Operations section.

Example:

var oldDogs = realm.All<Dog>().Where(dog => dog.Age > 8);

Ordering Operators

OrderBy, OrderByDescending, Thenby, and ThenByDescending are all supported. Reverse is not yet supported. Currently, you can only order by persisted properties on the class that you are querying. This means that dogs.OrderBy(dog => dog.Owner.FirstName) and the like is not yet supported.

Example:

var contacts = realm.All<Person>().OrderBy(p => p.LastName).ThenBy(p => p.FirstName);

Conversion Operators

ToArray, ToList, ToDictionary, and ToLookup are all supported. Cast isn't, but it would be redundant as a query in Realm will always consist of a collection of the class initially specified.

Example:

var phoneBook = realm.All<Person>().ToDictionary(person => person.PhoneNumber);

Element Operators

All of the main element operators are supported:

  • First and FirstOrDefault
  • Last and LastOrDefault
  • Single and SingleOrDefault

These methods take an optional predicate. To see the supported operations for predicates in Realm queries, refer to the Predicate Operations section.

Access to a single element by an index is supported by ElementAt and ElementAtOrDefault.

Note that, as is standard C# behaviour of default(T), the variants ...OrDefault return a single null RealmObject if there is no matching element.

DefaultIfEmpty is not yet supported.

Quantifiers

Any is supported.

All and Contains are not yet supported.

Any takes an optional predicate. To see the supported operations for predicates in Realm queries, refer to the Predicate Operations section.

Aggregate Operators

Count is supported.

LongCount, Sum, Min, Max, and Average are not yet supported.

Count takes an optional predicate. To see the supported operations for predicates in Realm queries, refer to the Predicate Operations section.

Predicate Operations

As a general rule, you can only create predicates with conditions that rely on data in Realm. Imagine a class

class Person : RealmObject
{
    // Persisted properties
    public string FirstName { get; set; }
    public string LastName { get; set; }

    // Non-persisted property
    public string FullName => FirstName + " " + LastName;
}

Given this class, you can create queries with conditions that apply to the FirstName and LastName properties but not to the FullName property. Likewise, properties with the [Ignored] attribute cannot be used.

Note that currently, the property must be the left side of a condition. This means that

var oldDogs = realm.All<Dog>().Where(dog => 7 < dog.Age); // INVALID query, do not copy

is illegal and would have to be changed into the equivalent

var oldDogs = realm.All<Dog>().Where(dog => dog.Age > 7); // Fixed

Relational Operators

Equality operators can be applied to all property types: ==, !=

Furthermore, the following can be used for numerical types: <, <=, >, >=

String Operators

With strings, you can use: Contains, StartsWith, and EndsWith, and Equals.

Example:

var peopleWhoseNameBeginsWithJ = realm.All<Person>.Where(p => p.FirstName.StartsWith("J"));

By default, Realm will perform a case-sensitive comparison, but you can provide StringComparison.OrdinalIgnoreCase argument to overwrite that. Since there is no overload for Contains that accepts StringComparison, we've provided a convenience extension method that can be used when querying:

var peopleWhoseNameContainsA = realm.All<Person>().Where(p => p.FirstName.Contains("a", StringComparison.OrdinalIgnoreCase));

Composition

You can use parentheses and the || and && operators to compose queries.

Example:

var PuppyRexes = realm.All<Dog>().Where(dog => dog.Age < 2 && dog.Name == "Rex");

A note on liveness

Realm queries are live, in the sense that they will continue to represent the current state of the database.

realm.Write(() => 
{
    realm.Add(new Person { FirstName = "John" });
    realm.Add(new Person { FirstName = "Peter" });
});

var js = realm.All<Person>().Where(p => p.FirstName.StartsWith("J"));

foreach(var j in js)
{
    Console.WriteLine(j.FirstName); // ==> John
}

realm.Write(() =>
{
    realm.Add(new Person { FirstName = "Joe" });
});

foreach(var j in js)
{
    Console.WriteLine(j.FirstName); // ==> John, Joe
}

This differs from the typical behavior of object/relational mappers (ORM's) where the result of a query is fetched and kept in memory as it was.

However, it also differs from the behavior of LINQ to Objects, where every iteration will reevaluate expressions, meaning that changes to both sides of a condition will affect the result. A Realm query will evaluate the right-hand sides of the conditions on the first run. So imagine you have a query like this:

var recentLogEntries = realm.All<LogEntry>().Where(l => l.TimeStamp > DateTime.Now.AddHours(-1));

Here, the recentLogEntries variable will contain all the log entries that have a TimeStamp later than one hour before the time when the query was first run (via foreach, ToList etc.). Newly added log entries will be included on subsequent runs, but the time they are compared to will not be updated.

Not yet supported

The following features are not yet supported. A few of them will not be supported as the Realm architecture renders them unnecessary.

Grouping Operators

GroupBy is not supported.

Set Operators

Distinct, Union, Intersect, and Except are not supported.

Partitioning Operators

Take, Skip, TakeWhile, and SkipWhile are not yet supported.

These are less important than when using an ORM. Given Realm's zero-copy pattern, data is only read from the database when the properties on the objects are accessed, so there is little overhead in simply iterating over a part of a result.

Projection Operators

Select and SelectMany are not yet supported.

The select keyword used with the query syntax is supported as long as you select the RealmObject itself and not some derivative:

var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;

Concatenation Operators

Concat is not supported.

Join Operators

Join and GroupJoin are not supported.

Note that joins are less vital when using Realm than when using a relational database and an ORM. Instead of using keys to identify relations, you simply refer to the related object.

So given a class

public class Address : RealmObject
{
    public string StreetName { get; set; }
    public int Number { get; set; }
}

you can simply use that in another class like so:

public class Customer : RealmObject
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

This works like an ordinary reference in C# which means that if two Customer instances are assigned the same Address object, changes to that address will apply to both customer objects.