We have a pretty complex application that uses MongoDB App Services and Realm.
Realm database has a fantastic feature of lazy-loading data when the data is actually accessed. This has pretty exciting effects. Imagine you have a screen that shows a list of items. Those items can be tens of thousands. In the traditional database or API approach, one would implement some sort of data pagination - either automatic, when the user reaches the end of the list, the next chunk of data is loaded, or manual - the user manually selects the page that they should load. With realm, you can simply fetch all of the items of an entity and directly bind those items to a ListView control in Xamarin. For example
public class Product : RealmObject
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductsViewModel
{
private readonly Realm _realm;
public IEnumerable<Product> Products { get; set; }
public ProductsViewModel(Realm realm)
{
_realm = realm;
}
public void LoadProducts()
{
Products = _realm.All<Product>();
}
}
//The xaml page
<ListView
ItemsSource="{Binding Products}"
CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the above example, even if there are tens of thousands of products, the ListView will load instantly and will scroll smoothly because of the lazy-loading magic.
The issue arises when we need to transform the native realm object Product
into a different class. Imagine, we want to create a new view model out of the Product
class, called ProductViewModel
:
public class ProductViewModel
{
public ProductViewModel(Product product)
{
Name = product.Name;
FormattedPrice = $"{product.Price:C}";
}
public string Name { get; set; }
public string FormattedPrice { get; set; }
}
This is a trivial example where we simply format the currency into a string. In our real-world application, the scenario is much more complicated, but it’s good to show the issue we are facing. Once would guess that changing the ProductsViewModel
to something like this would do the job:
public class ProductsViewModel
{
private readonly Realms.Realm _realm;
public IEnumerable<ProductViewModel> Products { get; set; }
public ProductsViewModel(Realms.Realm realm)
{
_realm = realm;
}
public void LoadProducts()
{
Products = _realm.All<Product>().Select(p => new ProductViewModel(p));
}
}
However, this approach is naive and will essentially break the lazy-loading aspect of the list. What it will do is iterate over every item in the list; for every item, it will fetch the name and the price of the product, will create a new ProductViewModel
, and only after all of these operations are completed, the list will show any data. If the product collection is extensive, this will take a significant amount of time to complete.
How can we approach solving this problem the right way? Essentially, we need some reactive collection transform operator which does the following.
- It preserves the reactive notifications of the original collection. If the original Realm collection changes, the transformed collection should also omit collection change events
- Items of the collection should be evaluated lazily whenever the list shows
If we do not find a solution, we will have to add artificial pagination so that the transformed objects are created only for a subset of items, which won’t take a lot of time. But this is a super undesired approach, as it breaks all of the benefits that the Realm database has.
P.S.
If we go further, there’s also another issue we do have, which probably is outside of this specific topic and is a more complicated variant of this issue. Imagine you have a list that should show data from 2 different Realm collections. How would we manage this? We need a mechanism to merge 2 Realm collections reactively.