Immutable Models in MVVM

10-22-04 025
Source: flickr/ Jeff Attaway

MVVM

The first M stands for Model – an implementation of the application’s domain model that includes a data model along with business and validation logic. Examples of model objects include repositories, business objects, data transfer objects (DTOs), Plain Old CLR Objects (POCOs), and generated entity and proxy objects.

definition source

Immutability

In object-oriented and functional programming, an immutable object(unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created.

definition source

Why bother?

Imagine the next simple situation, your application downloads a JSON,  deserialises it to an object and then presents the downloaded data. You would expect the downloaded data to be one-to-one to the data on the remote server, however the data can be accidentally or intentionally mutated.

Here is an example of a mutable Model:

public class MyMutableObject
{
    public long Id { get; set; }
    public string Title { get; set; }
    public List<string> Contents { get; set; }
}

All the properties has public setters which means that any of those can be changed after the object creation. Even if we make all the setters private, it could be still possible to manipulate data within the List.

Here is an example of immutable Model:

public sealed class MyImmutableObject
{
    public long Id { get; }
    public string Title { get; }
    public IReadOnlyCollection<string> Contents { get; }

    public MyImmutableObject(
        long id,
        string title,
        IReadOnlyCollection<string> contents)
    {
        Id = id;
        Title = title;
        Contents = contents;
    }
}

There are no public setters available and mutable collection type is replaced by “immutable” one. There is no option to extend this class as well as changing the data after the object is created.

Note:
IReadOnlyCollection is not a real immutable collection but an immutable facade. It is not thread safe and possible to cast to IList and try to manipulate the collection, however System.NotSupportedException will be thrown.

Alternatively we can add a System.Collections.Immutable NuGet package and replace IReadOnlyCollection<T> by IImmutableList<T> if we want a real immutable collection.

JSON Deserialization

Often Models used for deserialization and it can be tricky to deserialize JSON to immutable object. Luckily with Json.NET it is not an issue. We can easily serialize and deserialize MyImmutableObject:

var myImmutableObject = new MyImmutableObject(
        id: 1,
        title: “Test”,
        contents: new List<string> { “One”, “Two”, “Three” });
var json = JsonConvert.SerializeObject(myImmutableObject);
var deserializedObject = JsonConvert.DeserializeObject<MyImmutableObject>(json);

Please note that if your Model has more than one constructor you will need to mark  one for deserialization explicitly,  by adding a JsonConstructor attribute.

ORM

With .NET Standard we can use EntityFramework Core in our Xamarin.Forms applications which is great! And it is great twice since we can have a code first  immutable model fully supported by EF.

Example:

[Table(“ToDo”)]
public sealed class ToDoModel
{
    [Key]
    public int Id { get; private set; }
    public string Title { get; private set; }
    public string Notes { get; private set; }
    public DateTimeOffset CreatedAt { get; private set; }
    public DateTimeOffset? UpdatedAt { get; private set; }

    ToDoModel() { /* EF requires a parameterless constructor. */ }

    public ToDoModel(
        int id,
        string title,
        string notes,
        DateTimeOffset createdAt,
        DateTimeOffset? updatedAt)
    {
        Id = id;
        Title = title;
        Notes = notes;
        CreatedAt = createdAt;
        UpdatedAt = updatedAt;
    }      
}

When using an “immutable” Model with EF keep in mind:

  • Parameterless constructor – is required, should be private.
  • Setters – are required, should private.
  • Object tracking – should be disabled.

Notes:

  • SQLite.NET did not work properly with private constructor and setters.
  • We have to count with private setters since it is still possible to change a value with a private setter within the same class.

Conclusion

In this blogpost we discussed how to make immutable Models and checked few common scenarios. I would recommend to start immutable and change to mutable if necessary. Here are few recommendations to keep in mind:

  • Forget about private setters: prop -> propg
  • Make publicly available properties readonly
  • Use constructors
  • Seal classes
  • Use immutable collections or at least IReadOnlyCollection<T>
  • The goal is not to get 100% immutability but to improve code quality

Immutability is a very interesting topic which has pros and cons. For example it may harm performance or introduce unnecessary complexity in some cases, so use it wisely. There are few great resources that I would recommend to get familiar with:

 

Advertisements

Converting JSON file to a SQLite database

When it comes to a data storage for your application, there are multiple options. Depends on your needs you may choose a JSON file or a proper SQLite database. Some times you may need to convert a JSON to SQLite DB and that is the topic of the current post.

First idea that you may get is to solve the problem in code. Well, we are developers and we are best in it after all. However, SQLite CLI is powerful enough to solve our issue or at least to solve it partly. SQLite CLI supports multiple data sources but not JSON. We will have to convert JSON to CSV and depending on the data structure it could be complex or an easy task to do. Here you could write some code or use an existing solutions (google it) in order to make the conversion happen.

The interesting part is the data import itself and it’s pitfalls. I had to replace a ‘,’ separator by “|” since the SQLite CLI ignored my quoted nested commas. Another thing to keep in mind that leading and trailing spaces are not ignored and they are a part of the value. You can define the table scheme before the import or alter it after the import, depends on your needs. For doing it before you just have to create a table manually, otherwise you will have to use the SQLite CLI or a SQLite browser to alter an auto-generated table.

Let’s take a look on a simple case where we will import a CSV file with a custom separator without defining the table scheme before the import.
CSV contents:

A|B|C
1|2|3
4|5|6
SQLite CLI:
sqlite3 dummy.db
SQLite version 3.19.3 2017-06-27 16:48:08
Enter “.help” for usage hints.
sqlite> .mode csv
sqlite> .separator |
sqlite> .import dummy.csv ABC
sqlite> select * from ABC;
1|2|3
4|5|6
sqlite>
As you see it is quite simple. The first argument is the output file, in our case it is a “dummy.db”. The rest are basically dot-commands that define the input mode “csv”, the separator “|”, the data source “dummy.csv” and the table name to import the data  to “ABC”.
It is very easy and intuitive to use. I would recommend to get familiar with the SQLite CLI just to know what it is capable of in order to save your time and nerves.