Kentico Xperience Design Patterns: Modeling Missing Data with The Null Object Pattern
Tags: xperience, kentico, aspnetcore, csharp
Published: 2021-05-05 12:57:31

---
title: Kentico Xperience Design Patterns: Modeling Missing Data with The Null Object Pattern
published: true
description: Learn about the Null Object Pattern and how to use it in a Kentico Xperience application to model our content accurately
tags: xperience, kentico, aspnetcore, csharp
series: Kentico Xperience Design Patterns
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/hcusgcjdsp4aozej50g9.jpg
---

In [a previous post](https://dev.to/seangwright/kentico-xperience-design-patterns-modeling-missing-data-with-nullable-reference-types-57dj) we looked at the importance 🤓 of modeling content in Kentico Xperience and the impacts of that content modeling on our code.

We asked the question _"How well does our code match the content model? Especially, when working with optional or missing data."_

We saw that [nullable reference types](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types) can help express that data is missing, but they aren't the only tool 🔨 at our disposal for handling or modeling missing data in our Kentico Xperience applications.

In this post we're going to look at one of those alternative tools - the [Null Object Pattern](https://martinfowler.com/eaaCatalog/specialCase.html).

## 📚 What Will We Learn?

- What are the problems with modeling our code using _only_ nullable reference types
- Implementing the Null Object Pattern with our Call To Action `ImageViewModel`
- Where do we fall short with the Null Object Pattern?

## 🍃 A Refresher - Our Call To Action

Let's quickly look back on our example Call To Action Page Type which represents our content model in code:

```csharp
public class CallToAction : TreeNode
{
    public bool HasImage { ... }
    public string ImagePath { ... }
    public string ImageAltText { ... }
}
```

This Page Type has some optional content - the "Image" - which is represented by the three properties in the `CallToAction` class.

If `CallToAction.HasImage` is `true`, then we expect `ImagePath` and `ImageAltText` to have values, if it is `false`, then we will ignore the values and act as though we have no Image for our Call To Action.

Now, let's look again at our `HomeController` where we were trying to work with our Call To Action:

```csharp
public class HomeController : Controller
{
    private readonly IPageRetriever pageRetriever;
    private readonly IPageDataContextRetriever contextRetriever;

    // ... Constructor

    public ActionResult Index()
    {
        var home = contextRetriever.Retrieve().Page;

        var cta = retriever.Retrieve<CallToAction>(
            q => q.WhereEquals("NodeGUID", home.Fields.CTAPage))
            .FirstOrDefault();

        var viewModel = new HomeViewModel
        {
            Title = home.Fields.Title,
            Image = cta.HasImage
                ? new ImageViewModel
                  { 
                      Path = cta.ImagePath,
                      AltText = cta.AltText
                  }
              : null
        };

        return View(viewModel);
    }
}
```

We use a property, `CTAPage`, on our `Home` Page Type to find our Call To Action and then create our `HomeViewModel` with an optional `Image`:

```csharp
public class HomeViewModel
{
    public string Title { get; set; }
    public ImageViewModel? Image { get; set; }
}

public class ImageViewModel
{
    public string Path { get; set; }
    public string AltText { get; set; }
}
```

Since `HomeViewModel.Image` is nullable, our code models the true nature of this content 👏🏽 - it is optional and might not exist.

When we go and render our `HomeViewModel` in the Razor View, we can ensure we account for this potentially missing content:

```html
@model HomeViewModel

<h1>@Model.Title</h1>

@if (Model.Image is not null)
{
    <img src="@(Model.Image.Path)" alt="@(Model.Image.AltText)" />
}
```

And with that, we've ensured that our code models the content accurately which means we are handling the business requirements and writing a more robust application 💪🏾.

## 🕳 The Problems with Null

### What Does Null Really Mean?

Using `null` to model missing data is a great first step, but its a slightly awkward 🙃 tool sometimes because the C# that represents our content model isn't the only place we will find `null` in our codebase. This leads us to the following questions:

- In our code, where is the value `null` found and what does it mean?
- If I'm a new developer coming into this code base and I see a method returning a nullable value, do `null` results imply something I should expect? (Should I log/display an error or not?)
- Is `null` a normal value that represents the content model (intentionally missing) or is it the result of bad data (unintentionally missing)?

When we think about the core types of C# as a language, **`null` is not that different from `int`, `bool`, and `string` - they're all primitives and have no inherit meaning to the business concerns of an application**.

> Relying on too many primitive types to represent our content model could be considered a case of [primitive obession](https://refactoring.guru/smells/primitive-obsession).

The only thing that `null` tells us is 'There's nothing here' but it doesn't answer the question of 'why?' there's nothing.

### Null is a Special Case

As we start to use nullable reference types in our code, we'll see that each time we come across a `null` value, we have to treat it as a special case. This is a **good** thing and helps us prevent `NullReferenceException`s at runtime. At the same time, our code becomes cluttered with these 'special cases'.

An example of this can be seen using the code that populates our `HomeViewModel`:

```csharp
var viewModel = new HomeViewModel
{
    Title = home.Fields.Title,
    Image = cta.HasImage
        ? new ImageViewModel
        {
            Path = cta.ImagePath,
            AltText = cta.AltText
        }
        : null
};
```

We now have a `HomeViewModel` with an `Image` that is potentially `null`.

If we want to set a default `AltText` we need to make sure we don't cause a `NullReferenceException`, so we guard 🛡 against that condition:

```csharp
if (viewModel.Image is object && 
    string.IsNullOrWhiteSpace(viewModel.Image.AltText))
{
    viewModel.Image.AltText = "An image";
}
```

Any time we want to perform some operations on the nullable property, we need to first ensure its not `null`, and for more complex View Models it's not hard to imagine examples with lots and lots of checks like this.

We know this is a problem, but what's its cause 🤷🏼‍♀️?

### Nullable Reference Types are Unions Types

The core issue is that `null` and `ImageViewModel` are two completely different types and using nullable reference types is really a way of creating a new type that the C# compiler can reason about. That new type is **ImageViewModel or null** (sometimes written as `ImageViewModel | null`). This is called a [Tagged Union Type](https://en.wikipedia.org/wiki/Tagged_union) 🧐.

Since these two types have different properties and methods (different 'shapes'), namely that `null` has **no** properties or methods, we have to check first to see which type we're actually working with.

> Talking about nullable reference types this way shows how glaring of a problem `null` was in C# before nullable reference types were added. The C# compiler treated the two types `ImageViewModel` and `ImageViewModel | null` as **the exact same type**, despite the problems we've just shown this can cause! Ooof! 🤦🏻‍♂️

What we'd like to do is change that `ImageViewModel | null` type back into a plain old `ImageViewModel`, so its not as complex to work with, but also handle scenarios where the content is missing 🤔.

## 🧩 The Null Object Pattern

Fortunately for us, there's a design pattern that does exactly what we are looking for, and it's called the [Null Object Pattern](https://www.dotnetforall.com/null-object-c-design-pattern/).

The Null Object Pattern lets us get rid of `null`, using the custom type we've already defined, while also handling the scenarios where we have no data to populate an instance of this type 😀.

### Creating a 'Null' ImageViewModel

Looking at our `ImageViewModel`, we could implement the Null Object as follows:

```csharp

public record ImageViewModel(string Path, string AltText)
{
    public static ImageViewModel NullImage { get; } = 
        new ImageViewModel("", "");

    public bool IsEmpy => this == NullImage;
    public bool IsNotEmpty => this != NullImage;
}
```

First, we change from a C# `class` to a C# 9.0 `record` (which you can read about in the [Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record)) for reasons that will soon become apparent.

We also add a `public static` property that is read-only (it only has a getter) and name it `NullImage`. This property is the same type as the enclosing class (`ImageViewModel`), which means anywhere we need an `ImageViewModel` we can use `NullImage`.

`NullImage` also has some default values for its properties, which means any interactions with it will behave how we would expect (it's not dangerous to operate on the way `null` is 😉).

This is a key point of the Null Object Pattern - we want to represent the `null` special case using our type, which gets rid of the `null`, and we also want our `null` case to _behave_ the same as all other cases so we don't have to guard against those scenarios everywhere 😅.

Notice that by making the `NullImage` `static` and read-only, it's effectively a [Singleton](https://en.wikipedia.org/wiki/Singleton_pattern). This is another powerful feature because it lets us check if any `ImageViewModel` instance is the `NullImage` by performing a comparison.

With C# record types, equality of objects is defined by the equality of the values in the those objects, not by the references to a spot in memory, like with C# classes 🤓. This means any `ImageViewModel` created with an empty `string` for both the `Path` and `AltText` will be 'equal' to the `NullImage`.

```csharp
var house = new ImageViewModel("/path/to/image.jpg", "A House");

Console.WriteLine(house == ImageViewModel.NullImage); // False

var emptyImage = new ImageViewModel("", "");

Console.WriteLine(emptyImage == ImageViewModel.NullImage); // True
```

Finally we create some convenience properties, `IsEmpty` and `IsNotEmpty`, on the `ImageViewModel` which compare the current instance to `NullImage`.

If we need to modify the `Title` or `AltText` of objects we've created, we can use the C# record `with` syntax, which lets us clone an object into a new one while also selectively updating values 🧐:

```csharp
var image = new ImageViewModel("/path/to/image.jpg", "A");

var imageUpdated = image with { AltText = "A House" };

Console.WriteLine(image.AltText); // A
Console.WriteLine(imageUpdated.AltText); // A House
```

> There's lots of different ways to write the `ImageViewModel` and add a "Null" object - inheretance with virtual methods, private backing fields - but those details are specific to the business domain, not the Null Object Pattern itself 👍🏿.

### Using our 'Null' ImageViewModel

Now let's use our updated `ImageViewModel` in the `HomeController`:

```csharp
public class HomeController : Controller
{
    private readonly IPageRetriever pageRetriever;
    private readonly IPageDataContextRetriever contextRetriever;

    // ... Constructor

    public ActionResult Index()
    {
        var home = contextRetriever.Retrieve().Page;

        var cta = retriever.Retrieve<CallToAction>(
            q => q.WhereEquals("NodeGUID", home.Fields.CTAPage))
            .FirstOrDefault();

        var viewModel = new HomeViewModel
        {
            Title = home.Fields.Title,
            Image = cta.HasImage
                ? new ImageViewModel(cta.ImagePath, cta.AltText)
                : ImageViewModel.NullImage
        };

        if (string.IsNullOrWhiteSpace(viewModel.Image.AltText))
        {
            viewModel.Image = viewModel.Image with 
            { 
                AltText = "An Image" 
            };
        }

        return View(viewModel);
    }
}
```

We still have a conditional (as a ternary) checking if the Call To Action has an image, but we don't need to special case any interactions with the `HomeViewModel.Image` because in the case of missing content, we still have an instance of the `ImageViewModel` to work with 😎.

Our Razor View can be updated as well to use the `IsNotEmpty` property:

```html
@model HomeViewModel

<h1>@Model.Title</h1>

@if (Model.Image.IsNotEmpty)
{
    <img src="@(Model.Image.Path)" alt="@(Model.Image.AltText)" />
}
```

By updating our code to model missing content in the `ImageViewModel` type itself, we remove the need to guard against `NullReferenceException`s and we can still conditionally render the content if it was intentionally missing from the Page Type data.

## 🏔 Where the Null Object Pattern Falls Short

I'm a big fan of the Null Object Pattern because it encodes business logic into our types and classes, modeling our special cases but in a way that makes our code more robust and readable 🎉.

However, there are still some places where this pattern isn't ideal 😑:

- We have to remember to define the "Null" version of each type and ensure they have consistent naming (ex `NullImage`) across the application.
- The convenience methods `IsEmpty` and `IsNotEmpty` also need to be implemented. This could be done with a base class but then we need to remember to inherit from it.
- Picking default values for `string` properties is pretty easy, but what about other types? Is `0` always the best value for `int` properties? What about `false` for `bool`?
    - Remember, we'd like to treat our "Null" case and normal case the same in as much of our code as possible.
    - Nested objects can get especially verbose since we'll want to continue our Null Object Pattern for all of those types as well.
- The `ImageViewModel` has started to become more complex because it handles both the modeling of the content and the special case of missing content.

The overarching issue is that we've pushed the 'missing content' problem _into_ the class, giving it more responsibility and complexity 😔.

## 🤨 Conclusion?

Null reference types are a great way of exposing the hidden ⛅ `null` values that lurk within our code, bringing them out into the light of day 🌞 using the C# type system.

They are our first step in battling the dreaded `NullReferenceException` and modeling code to match our content!

However, `null` isn't a very ergonomic type to work with and doesn't represent our content very well - all it says is "There's nothing here".

The Null Object Pattern lets us get rid of `null` completely by representing the 'missing content' in our classes, treating it as a special case that behaves the same as the normal cases.

Unfortunately, the Null Object Pattern comes along with a bit of baggage that _could_ be hard to maintain in larger applications.

Nullable reference types and the Null Object Pattern are great tools to have in our toolbox, but as we will see there are even more ways to model missing content in our Kentico Xperience applications.

In my next post on modeling missing data, we'll see how we can use a simple functional 👨🏽‍🔬 programming pattern called the 'Maybe' (or 'Option') to pull the representation of missing content out of our class, which simplifies it, while still avoiding a Union Type with `null` 😎.

As always, thanks for reading 🙏!

## References

- [Kentico Xperience Design Patterns: Modeling Missing Data with Nullable Reference Types](https://dev.to/seangwright/kentico-xperience-design-patterns-modeling-missing-data-with-nullable-reference-types-57dj)
- [The Special Case (Null Object)](https://martinfowler.com/eaaCatalog/specialCase.html)
- [C# Nullable Reference Types](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references) (Microsoft Docs)
- [Tagged Union Types](https://en.wikipedia.org/wiki/Tagged_union)
- [C# Null Object Pattern](https://www.dotnetforall.com/null-object-c-design-pattern/)
- [The Singleton Pattern](https://en.wikipedia.org/wiki/Singleton_pattern)
- [C# 9.0 Record Types](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record)

---

<figcaption>Photo by <a href="https://unsplash.com/@jordanmadrid">Jordan Madrid
</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

---

We've put together a list over on [Kentico's GitHub account](https://github.com/Kentico/Home/blob/master/RESOURCES.md) of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico or Xperience tags here on DEV.

{% tag kentico %}

{% tag xperience %}

Or my [Kentico Xperience blog series](https://dev.to/seangwright/series), like:

- [Kentico Xperience Xplorations](https://dev.to/seangwright/series/8185)
- [Kentico Xperience MVC Widget Experiments](https://dev.to/seangwright/series/9483)
- [Bits of Xperience](https://dev.to/seangwright/series/8740)