---
title: "RequiredIf - Extend the validation in Blazor"
description: "Just imagine you have your beautiful model in a form. And Blazor is very kind and does validation for you. It is really straight forward because you can annotate your model with some attributes like `RequiredAttribute` to tell Blazor: \"Hey this property has to be set, otherwise the form will not be submitted.\" \n\nBut what if you want to have that easy setup with attributes but also want to say to Blazor: \"Hey this property has to be set, **if the other property is true**.* Well, that does not work out of the box. So let's make it together!"
updated: 2022-02-15
tags: ["Forms", "C#", "Blazor", "Validaton"]
canonical: "https://steven-giesel.com/blogPost/f6be6a72-1497-4ad5-8197-343966761b77/requiredif-extend-the-validation-in-blazor"
---

# RequiredIf - Extend the validation in Blazor

As often I am writing about topics I encounter in my daily life. So just imagine you have a blog... like this one you are reading right now. This has some properties. Simplified your model could look like this:
```csharp
public class BlogPostModel
{
    [Required]
    public string Title { get; set; }

    [Required]
    public string ShortDescription { get; set; }

    [Required]
    public string Content { get; set; }

    [Required]
    public string PreviewImageUrl { get; set; }

    [Required]
    public bool IsPublished { get; set; } = true;
}
```

That looks very nice. Basically the user (in this case: me) has to fill up all the properties otherwise the form will not get submitted. But what if the blog post gets not published `IsPublished == false`. Do we still have to provide all the information? Unfortunately yes. Blazor does not offer us any possibility to give the `Required` attribute any context. If Blazor doesn't, then we do!

## `RequiredIf`
The goal is that we want to express: 
> `Content` is only mandatory, if we publish the blog post!

So let us start with the notation. I want something like this:
```csharp
public class BlogPostModel
{
    [Required]
    public string Title { get; set; }
    
    [RequiredIf(nameof(IsPublished), true)]
    public string Content { get; set; }
    
    [Required]
    public bool IsPublished { get; set; }
}
```
Now here a few things:
 * `Title` is still mandatory. I don't want to change that
 * `Content` should be only required if `IsPublished` is `true` 

Why the `nameof` notation. Well attributes are nice and cool but they lag some key features. For example generics. An attribute is not allowed to have generics. So we have to workaround that. To be able to be used for validation we have to at least extend `ValidationAttribute`. Have a look at the documentation [here](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationattribute?view=net-6.0). You can see that there are a lot of derived attributes.

So let's go, the constructor is pretty much done:
```csharp
public class RequiredIfAttribute : ValidationAttribute
{
    private readonly string _propertyName;
    private readonly object? _isValue;

    public RequiredIfAttribute(string propertyName, object? isValue)
    {
        _propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
        _isValue = isValue;
    }
```

Well that was easy.. the heavy lifting will follow. But first let us override the error message. We want to indicate in the error message as well, that there is some dependencies. We can do so by overriding `FormatErrorMessage`
```csharp
public override string FormatErrorMessage(string name)
{
    var errorMessage = $"Property {name} is required when {_propertyName} is {_isValue}";
    return ErrorMessage ?? errorMessage;
}
```
In our case with the blog post, we want to say: `Property content is required when IsPublished is not True.`

Now the central function: `ValidationResult? IsValid(object? value, ValidationContext validationContext)`. ([Here](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationattribute.getvalidationresult?view=net-6.0#system-componentmodel-dataannotations-validationattribute-getvalidationresult(system-object-system-componentmodel-dataannotations-validationcontext)) for more information)
This one gets called for every property with a `RequiredAttribute`. So this is our point to jump in and do the heavy work.

The first thing we want to do is: Check if we have every information. That also includes, does our `propertyName` which we got via `nameof(IsPublished)` really exist?
```csharp
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
    ArgumentNullException.ThrowIfNull(validationContext);
    var property = validationContext.ObjectType.GetProperty(_propertyName);

    if (property == null)
    {
        throw new NotSupportedException($"Can't find {_propertyName} on searched type: {validationContext.ObjectType.Name}");
    }
```

I guess the only line which needs a bit more explanation is the following: `var property = validationContext.ObjectType.GetProperty(_propertyName);`

The `ValidationContext.ObjectType` holds our model (`BlogPostModel`). That is important because we need to resolve the dependency to our property. Therefore we need the type and also the concrete instance to get the actual value of `IsPublished`. So this snippet gives us the type information.

```csharp
var requiredIfTypeActualValue = property.GetValue(validationContext.ObjectInstance);

if (requiredIfTypeActualValue == null && _isValue != null)
{
    return ValidationResult.Success;
}
```
`var requiredIfNotTypeActualValue = property.GetValue(validationContext.ObjectInstance);` does exactly what I wrote before. We want to get the real value of our `RequiredIf` "pointer". Again in our case we want to have `IsPublished`.

Afterwards we make a quick check the actual value is null and the expected is not null. The both would be null, we would consider the property as required. If not we can go out here and can say: "Everything fine!".
And now exactly that is left: What if the property is required? Well then we just have to check if it is set or not:
```csharp
if (requiredIfTypeActualValue == null || requiredIfTypeActualValue.Equals(_isValue))
{
    return value == null
        ? new ValidationResult(FormatErrorMessage(validationContext.DisplayName))
        : ValidationResult.Success;
}

return ValidationResult.Success;
```
The whole if does nothing more than checking, if our `IsPublished` is either `null` or the required value. If so, we check if `value` is null. If so that would be a violation and leads to an error result.
Why did I use `requiredIfTypeActualValue.Equals(_isValue)` instead of `requiredIfTypeActualValue == _isValue`. If you check the types, they are all `object`s. That means `==` would check for `ReferenceEquals`. Not so great if we use `bool`.

## Links and resources
Now if you just want to use this and more: I am currently building a library which houses such helper functionality.
I am also planning to add dependencies outside of the model. 

If you want to see the code plus tests: Here you go on [github](https://github.com/linkdotnet/ValidationExtensions).
And here directly the [nuget package](https://www.nuget.org/packages/LinkDotNet.ValidationExtensions/1.0.0).

## The whole Thing
```csharp
using System.ComponentModel.DataAnnotations;

namespace LinkDotNet.ValidationExtensions;

public class RequiredIfAttribute : ValidationAttribute
{
    private readonly string _propertyName;
    private readonly object? _isValue;

    public RequiredIfAttribute(string propertyName, object? isValue)
    {
        _propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
        _isValue = isValue;
    }

    public override string FormatErrorMessage(string name)
    {
        var errorMessage = $"Property {name} is required when {_propertyName} is {_isValue}";
        return ErrorMessage ?? errorMessage;
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        ArgumentNullException.ThrowIfNull(validationContext);
        var property = validationContext.ObjectType.GetProperty(_propertyName);

        if (property == null)
        {
            throw new NotSupportedException($"Can't find {_propertyName} on searched type: {validationContext.ObjectType.Name}");
        }

        var requiredIfTypeActualValue = property.GetValue(validationContext.ObjectInstance);

        if (requiredIfTypeActualValue == null && _isValue != null)
        {
            return ValidationResult.Success;
        }

        if (requiredIfTypeActualValue == null || requiredIfTypeActualValue.Equals(_isValue))
        {
            return value == null
                ? new ValidationResult(FormatErrorMessage(validationContext.DisplayName))
                : ValidationResult.Success;
        }

        return ValidationResult.Success;
    }
}
```