Skip to main content

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:

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

Which is used by a DTO like:

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

That allows me in the backend to do this:

csharp
/// <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:

csharp
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:

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

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

Now if we try:

csharp
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:

csharp
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