How did I do?*

Add custom placeholders to FluentValidation messages

In situations where your validation message should display a non-standard value, or a value which isn't already available as a FluentValidation placeholder, you can add your own name/value pair to the validation context for use in messaging.

In the following example, the object being validated is a MemoryStream, and we're making sure it is not an empty stream by validating that it is greater than 0 bytes in length.

The default message placeholders available in this situation, {PropertyName} and {PropertyValue}, would output the values "FileStream" and "System.IO.MemoryStream" respectively, neither of which are suitable for this particular validation message as the message should reference the name of the file which the stream was created from, and this is stored in the FileName property.

public record DataSetFileDto
{
    public required string FileName { get; set; }
    public required MemoryStream FileStream { get; set; }

    public class Validator : AbstractValidator<DataSetFileDto>
    {
        public Validator()
        {
            RuleFor(dto => dto.FileStream)
                .Must((dto, fileStream, context) =>
                {
                    context.MessageFormatter.AppendArgument("FileName", dto.FileName);
                    return fileStream.Length > 0;
                })
                    .WithMessage(ValidationMessages.FileSizeMustNotBeZero, "{FileName}");
        }
    }
}

This overload of Must has three parameters:

  1. the root object being validated (dto), in this case the DataSetFileDto
  2. the property being validated (fileStream), in this case the MemoryStream
  3. the validation context (context)

The first part of the Must statement block creates a placeholder name and assigns the value it should reference, and the second part performs the boolean validation, in this case ensuring the file is not empty.