Sometimes you just need extensions methods to model your stuff

1/25/2026
2 minute read

I came across a recent thing, where I hit the limit with the "normal" approach of modeling my entity and had to resort to extension methods.

The problem

I have a frontend part that allows partial updates of an entity. Imagine your favourite tool: JIRA 😄 - you can update title, reporter, and so on in a single UI. For that I didn't want a DTO for each use case, but a single DTO that allows partial updates of an entity.

In the frontend (TypeScript / Angular) I have:

export type Optional<T> = {
  hasValue: boolean;
  value: T;
};

Which is used by a DTO like:

export type UpdateModel = {
  propOne: Optional<number | null>;
  propTwo: Optional<string | null>;
  propThree: Optional<string>;
};

That allows me in the backend to do this:

/// <summary>
/// A struct representing an optional value.
/// </summary>
public readonly record struct Optional<T>(bool HasValue, T Value)
{
    /// <summary>
    /// Returns the value if present; otherwise, returns the provided default value.
    /// </summary>
    public T GetValueOrDefault(T current) => HasValue ? Value : current;
}

And then in my update method:

public void UpdateEntity(UpdateModel model)
{
    PropOne = model.PropOne.GetValueOrDefault(entity.PropOne);
    PropTwo = model.PropTwo.GetValueOrDefault(entity.PropTwo);
    PropThree = model.PropThree.GetValueOrDefault(entity.PropThree);
}

That works fine, but we hit a wall if we add a new propFour that has different nullability:

public int? PropFour { get; set; }

But in the UpdateModel we want to have (because in an update case that is mandatory, whereas initially it isn't):

public Optional<int> PropFour { get; set; }

Now if we try:

PropFour = model.PropFour.GetValueOrDefault(entity.PropFour);

We get:

Argument type 'int?' is not assignable to parameter type 'int'

The solution

The problem is that we can't easily override nullability in the GetValueOrDefault method. Here extension methods come into play:

public static class OptionalExtensions
{
    /// <summary>
    /// Returns the value if present; otherwise, returns the provided default value.
    /// </summary>
    public static T? GetValueOrDefault<T>(this Optional<T> optional, T? value)
        where T : struct
        => optional.HasValue ? optional.Value : value;
}

Now: PropFour = model.PropFour.GetValueOrDefault(entity.PropFour); works as expected.

An error has occurred. This application may no longer respond until reloaded.Reload x