Kentico Xperience Design Patterns: Modeling Missing Data with Nullable Reference Types
Tags: xperience, kentico, aspnetcore, csharp
Published: 2021-03-08 14:52:26

---
title: Kentico Xperience Design Patterns: Modeling Missing Data with Nullable Reference Types
published: true
description: Learn how to handle a content model with optional or missing data using nullable reference types
tags: xperience, kentico, aspnetcore, csharp
series: Kentico Xperience Design Patterns
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/hcusgcjdsp4aozej50g9.jpg
---

[Content modeling](https://docs.kontent.ai/tutorials/set-up-kontent/content-modeling/what-is-content-modeling) is [part of the foundation](https://docs.xperience.io/developing-websites/defining-website-content-structure) of any [successful Kentico Xperience application](https://advantage.kentico.com/planning-planning-content-management) (and is  often under-valued ☹).

It's up to software developers to implement a content model in Xperience's [Page Types](https://docs.xperience.io/developing-websites/defining-website-content-structure/managing-page-types/creating-page-types) and other content management features...

- Related Pages or Linked Pages?
- Normalized or de-normalized data?
- Attachments or the Media Library?
- Page Type names, Scopes, and Parent/Child hierarchies?
- Content + design or design agnostic content?

There are many important decisions to make, but once some content has been modeled, developers are always faced with another... how do we structure our code to best align with the content model 🤷🏽‍♀️?

**What do we do with content models that inevitably include optional or missing data**?

## 📚 What Will We Learn?

- Modeling code to match Content
- An Example 'Call To Action' Page Type
- Using [nullable reference types](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types)
- Where do we fall short with nullable reference types?

## 🏗 Modeling the Models

We like it when there are well defined rules around how our code works, and when our code works with data, we also prefer that data to be orderly and predictable 👩🏻‍🔬.

When using Kentico Xperience, we want to create [Page Types](https://docs.xperience.io/developing-websites/defining-website-content-structure/managing-page-types) that most accurately model the content (data) being stored in the application.

The field names should [match the words the stakeholders use](https://www.martinfowler.com/bliki/UbiquitousLanguage.html) and we don't want overly complex Page Types, with too many fields, that make content management difficult and violate the [SOLID Design principles](https://en.wikipedia.org/wiki/SOLID).

At the same time, we want to be flexible where it counts, making content management easier...

### 🤸🏾‍♀️ Flexible Page Types: A Call To Action

Let's imagine we heed to model content that represents a [call to action](https://en.wikipedia.org/wiki/Call_to_action_(marketing)).

Some call to actions in our site might have images, while others might not.

If we are modeling our content with precision, we will up with two different Page Types for our call to action - one with an image field, and one without.

However, this probably isn't a good approach...

Why? 🤔

Any given Call To Action might have an image added or removed from it in the future, and we don't want to force the content managers to create/delete a Page just to make this change - that's a bad (content management) user experience.

It's also likely we'll want to display both imaged and image-less call to actions in the same places on the site, without having to query for the data of two different Page Types.

The existence or absence of an Image from a Call to Action **is part of the content model**, not something we should try to avoid 😮.

Instead, we should allow flexibility in the Page Type and handle the existing or missing image in our code 👍🏾.

## ❔ Displaying Optional Values

So, let's assume we've created a Page Type, `CallToAction`, that has a set of optional fields for our image (I'm leaving out a lot of the auto-generated Page Type code for brevity):

```csharp
public class CallToAction : TreeNode
{
    /// A checkbox ✅ in the form
    [DatabaseField]
    public bool HasImage
    {
        get => ValidationHelper.GetBoolean(
            GetValue("HasImage"), false);
        set => SetValue("HasImage", value);
    }

    /// A URL Selector form control
    [DatabaseField]
    public string ImagePath
    {
        get => ValidationHelper.GetString(
            GetValue("ImagePath"), "");
        set => SetValue("ImagePath", value);
    }

    /// A Text field form control
    [DatabaseField]
    public string ImageAltText
    {
        get => ValidationHelper.GetString(
            GetValue("ImageAltText"), "");
        set => SetValue("ImageAltText", value);
    }

    // Other Call To Action fields ...
}
```

Now, let's write our C# to retrieve this content and present it in a Razor View:

> I am putting data access code in an MVC `Controller` for brevity. In practice these operations should be [behind an abstraction](https://dev.to/seangwright/kentico-12-design-patterns-part-12-database-query-caching-patterns-43hc) ... not in the presentation layer of our application 👎.

```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 = // What do we do here?

        return View(viewModel);
    }
}
```

Retrieving our content is easy with Xperience's `IPageRetriever` service, but what about creating our View Model that we'll use to send our Call to Action content to the Razor View? How should this class be defined

Let's try modeling it 🙂:

```csharp
public class HomeViewModel
{
    public string Title { get; set; }
    public bool HasImage { get; set; }
    public string ImagePath { get; set; }
    public string ImageAltText { get; set; }
}
```

I have a few concerns 😒 about this model:

- `HasImage` needs to be checked before trying to render the image, but nothing about the model expresses or enforces this
- `ImagePath` could have a value while `HasImage` is `false` if the content manager unchecked the "Has Image?" checkbox without removing the "Image Path" value
- `HasImage` is about whether or not some content should be displayed, where `ImagePath` and `ImageAltText` are the actual pieces of content

So, let's try again to resolve these issues:

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

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

Now we are grouping the content in a new `ImageViewModel` class and the `HomeViewModel` has an instance of it.

However, here we're writing our code with C# 8.0 (or newer), so let's use [nullable reference types](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references) and get rid of the `HasImage` property:

> Here's a [full step by step guide](https://agirlamonggeeks.com/2019/10/20/how-to-turn-c-8-nullable-reference-types/) to enabling nullable reference types in your application.

> While we're at it, we should [set all warnings from nullable reference types as errors](https://stackoverflow.com/a/62116924/939634), otherwise it's easy to ignore the benefit they bring 😉.

```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; }
}
```

Now, if `CallToAction.HasImage` is `false`, the `HomeViewModel.Image` value will be `null`:

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

In our Razor view we can add a check for a `null` value and render the image conditionally:

> _Note_: I use the C# 9.0 `not` operator for [pattern matching](https://daveabrock.com/2020/07/06/c-sharp-9-pattern-matching#logical-patterns) here. The C# 8.0 alternative is `Model.Image is object`.

```html
@model HomeViewModel

<h1>@Model.Title</h1>

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

This appears to express the intent of the functionality and the content being modeled 💪🏽.

## 🦊 What Does the Docs Say?

The Kentico Xperience documentation discusses [using nullable properties for non-required fields](https://docs.xperience.io/developing-websites/generating-classes-for-xperience-objects#GeneratingclassesforXperienceobjects-Usingnullablepropertiesfornon-requiredfields).

This is a neat idea, but has a few too many caveats at the moment:

- Generated Page Type classes don't include nullable properties
- Re-generating the Page Type class will remove customized nullable properties
- Putting nullable types on a separate `partial` class doesn't enforce their use (the non-nullable ones are still accessible)
- Only nullable value types are supported if the Page Type class is shared between ASP.NET Core and the Web Forms Content Management application (.NET 4.8 doesn't support nullable reference types)

At the same time, I think nullable types in generated code is definitely something to keep an eye on 😏 as Xperience moves to run fully on ASP.NET Core [in the next version of the platform, Odyssey](https://xperience.io/discover/blog/2021-03/kentico-xperience-13-refreshes-small-steps-for-the).

## 🛣 A Fork in the Road: Missing Data

We could say we've solved this problem and head home for dinner 🥙!

But I think we should consider another common variation of this issue...

We decided to use nullable reference types to represent an optional value (or set of values), but what about data that _should_ exist but is missing (unintentionally)?

If we look back at our `HomeController` example, we can see that we found our `CallToAction` content by querying for the node with a `NodeGUID` that matched the value of our Home Page's `CTAPage` field:

```csharp
var home = contextRetriever.Retrieve().Page;

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

This means there's a field in the `Home` Page Type that holds a reference to the `CallToAction` Page in the content tree. But what happens if that Page is deleted 🤷‍♂️

Currently, our application would throw an exception because we try to access the `HasImage` property on a `cta` variable that has the value of `null` 🤦🏽.

The C# LINQ method `.FirstOrDefault()` knows it's returning a reference type and the "default" value of a reference type is `null`, so it's marked as possibly returning a `null` value (`CallToAction?`).

### What Is Null? Baby, Don't Hurt Me!

{% youtube HEXWRTEbj1I %}

Jokes aside, we are now in a situation where `null` now represents two different ideas.

Let's look at our `HomeViewModel` again:

```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; }
}
```

The nullable `public ImageViewModel? Image { get; set; }` represents an optional value - it's ok if it's not there, and we should account for that.

However, with the .NET LINQ `.FirstOrDefault()` method, a `null` value doesn't have any meaning except what **we** decide it should have... and in that case, it's definitely _not_ an optional value.

When we can't find the `CallToAction` Page with our query, we might want to treat that as an error and [log it to the Xperience event log](https://docs.xperience.io/13api/configuration/event-log). We might also want to display a message to the Content Manager when the View is rendered in the Page tab 🤔.

---

Here are some **important** questions that should be answerable if we want to design robust and maintainable applications:

- 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)?

Unfortunately, when `null` represents two different things at the same time, we will always have a hard time answering these questions or we'll arrive at inconsistent answers over time and across the development team 😫.

## 🤨 Conclusion

We've seen how the process of content modeling impacts the way we write our code, and this is a good thing!

Accounting for optional or missing data is the responsibility of software developers, especially when working with content in a platform like [Kentico Xperience](https://xperience.io/).

The C# nullable reference type language feature is a great way to expose the hidden use of `null` in our code, and I recommend we all enable in all of our ASP.NET Core projects. However, since it is a low level language feature, it doesn't have much meaning on its own, besides 'there is no value here'.

In my next post we'll look at how combining nullable reference types with the [null object pattern](https://en.wikipedia.org/wiki/Null_object_pattern) can get us closer to modeling content accurately in our code.

As always, thanks for reading 🙏!

## References

- [What is Content Modeling](https://docs.kontent.ai/tutorials/set-up-kontent/content-modeling/what-is-content-modeling) (Kentico Kontent docs)
- [Defining Website Content Structure](https://docs.xperience.io/developing-websites/defining-website-content-structure) (Kentico Xperience docs)
- [Planning Content Management](https://advantage.kentico.com/planning-planning-content-management) (Kentico Xperience Advantage guide)
- [Ubiquitous Language](https://www.martinfowler.com/bliki/UbiquitousLanguage.html) as part of Domain Driven Development
- [Creating Page Types](https://docs.xperience.io/developing-websites/defining-website-content-structure/managing-page-types/creating-page-types) (Kentico Xperience docs)
- [C# Nullable Reference Types](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references) (Microsoft Docs)
- [Guide to enabling nullable reference types](https://agirlamonggeeks.com/2019/10/20/how-to-turn-c-8-nullable-reference-types/)
- [Treat nullable warnings as errors](https://stackoverflow.com/a/62116924/939634)

---

<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)