Skip to content

Model validation

When building Application Services, it is a common practice to want to validate incoming DTOs using .NET's Data Annotations.

The Suite automatically execute .NET's Data Annotation validations when an incoming parameter of an IAppService method has them. App Service's methods won't be executed if the input DTO doesn't fulfill the validation attached to it.

The IValidatableObject interface is also supported, besides using attributes, which can be implemented if the validation logic is too complex to be expressed with the built-in validation attributes.

The Suite Framework automatically localizes validation errors thrown by .NET standard validation attributes. If you implement custom validation logic, such as IValidatableObject or [CustomValidation], you will need to localize them. Keep reading to see how.

Localizing IValidatableObject

When you implement IValidatableObject interface in your DTOs, you are in charge of apply the whole set of validation rules, therefore you need to provide an instance of IEnumerable<ValidationResult> as the validation method result. Here you have to perform all the validation you want, creating one or more instances of ValidationResult carrying on the validation info for the fields whose validation rules are broken.

You'll need to inject an ILocalizerFactory<TResourceBundle>, where TResourceBundle is the bundle resource type containing your localized error message values for the validation being done. For example:

C#
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    // Check for correctness.
    if (value.Equals(CorrectValue))
    {
        return ValidationResult.Success;
    }

    var stringLocalizer = validationContext.GetRequiredService<IStringLocalizer<CustomDataAnnotationResources>>();
    return new ValidationResult(
        stringLocalizer[CustomDataAnnotationResources.MSG_VALIDATABLE_OBJECT_ERROR, nameof(ValidatableDTO.Description)],
        new string[] { nameof(ValidatableDTO.Description) }).Yield();
}

Localizing CustomValidation attributes

When using [CustomValidationAttribute], the approach is quite similar. For example:

C#
public static class CustomValidator
{
    public static ValidationResult Validate(string value, ValidationContext context)
    {
        // Check for correctness.
        if (value.Equals(CorrectValue))
        {
            return ValidationResult.Success;
        }

        // Build the ValidationResult object using the proper IStringLocalizer instance.
        var strLocalizer = context.GetRequiredService<IStringLocalizer<CustomLocalizationResources>>();
        var msg = strLocalizer[CustomLocalizationResources.MSG_CUSTOM_VALIDATION_ERROR, context.MemberName];

        return new ValidationResult(errorMessage: msg, memberNames: new[] { context.MemberName });
    }
}

Then you must use the [CustomValidationAttribute] pointing to the CustomValidator, as follows:

C#
1
2
3
4
5
6
public class MyDTO {
    [CustomValidation(
        typeof(CustomValidator),
        nameof(CustomValidator.Validate))]
    public string CustomValidatedField { get; set; };
}

As the previous example shows, CustomLocalizationResources provides the localized message values to fill out the ValidationResult instance. Be sure to provide localizable messages for your application to be localizable for different cultures and languages. The IStringLocalizer<T> instance can be obtained out of the ValidationContext instance provided as method argument.

Customizing built-in attributes localization

Warning

This is an advanced/custom scenario. Please contact the Suite Team if you feel the need to use it.

By default a resource bundle and its bundle resource type are provided through ITsynch.Suite.Mvc.DataAnnotationLocalization.Resources.DataAnnotationLocalizationResources, with english ('en') and spanish ('es') locales. If you have to change some of the provided localization values or add new ones, you can proceed as follows:

  1. Create a new localization bundle to hold your locale values. Ensure to use DataAnnotationLocalizationResources base type (see here) to override base resources values with the new ones.
  2. Register your resource bundle to the LocalizationOptions (see here)
  3. Be sure to keep the localization key values as is, or toggle the EndpointsModuleOptions to provide a new localization resource key mapping to the AttributeValidation type.
  4. Tell the framework to use your localization bundle instead of the default one, by setting your localization bundle type to the DataAnnotationsLocalizationResourceBundle property of EndpointsModuleOptions.