Skip to content

Instantly share code, notes, and snippets.

@glen-84
Created February 2, 2024 13:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glen-84/a9fb44836fff5be1caf9d1a1189c182d to your computer and use it in GitHub Desktop.
Save glen-84/a9fb44836fff5be1caf9d1a1189c182d to your computer and use it in GitHub Desktop.
HC validation example
public sealed class AddTemporaryFileInputValidator : AbstractValidator<AddTemporaryFileInput>
{
public AddTemporaryFileInputValidator()
{
this.RuleFor(f => f.TemporaryFileName)
.NotEmptyWithLength(FileName.MinimumLength, FileName.MaximumLength) // using VO metadata
.Must(FileName.ValidateCharacters) // using VO validation logic
.WithErrorCode(nameof(FileName.Errors.FileNameDisallowedCharacters)); // using VO validation message/code
}
}
internal sealed class FileConfiguration : IEntityTypeConfiguration<File>
{
public void Configure(EntityTypeBuilder<File> builder)
{
// Properties.
builder
.Property(f => f.Name)
.HasMinLength(FileName.MinimumLength)
.HasMaxLength(FileName.MaximumLength); // using VO metadata
// ...
}
}
public sealed class StringValueAttribute(Conversions conversions = Conversions.None)
: ValueObjectAttribute<string>(conversions);
[StringValue]
public sealed partial class FileName
{
public const int MinimumLength = 1;
public const int MaximumLength = 200;
public string Extension => this.extension ??= Path.GetExtension(this._value);
private string? extension;
// Based on System.IO.Path.GetInvalidFileNameChars (in a Windows environment).
// On Unix, there are only 2 invalid characters ('\0' and '/'), which is too permissive.
private static readonly SearchValues<char> DisallowedFileNameChars = SearchValues.Create(
[
#pragma warning disable IDE0055 // TODO: https://github.com/dotnet/roslyn/issues/70951.
'\"', '<', '>', '|', '\0',
(char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
(char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19,
(char)20, (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28,
(char)29, (char)30, (char)31, ':', '*', '?', '\\', '/'
#pragma warning restore IDE0055
]);
/// <summary>
/// Returns <c>true</c> if the file's extension is contained within the given list.
/// </summary>
public bool HasExtension(IList<string> extensions)
{
return extensions.Contains(this.Extension);
}
public static bool ValidateCharacters(string value)
{
return value.AsSpan().IndexOfAny(DisallowedFileNameChars) == -1;
}
private static Validation Validate(string value)
{
var result = StringValidator.Validate(value, MinimumLength, MaximumLength);
if (result != Validation.Ok)
{
return result;
}
// Validate characters.
if (!ValidateCharacters(value))
{
return Validation.Invalid(Errors.FileNameDisallowedCharacters);
}
return Validation.Ok;
}
public static class Errors
{
public const string FileNameDisallowedCharacters
= "File name contains disallowed characters.";
public static IReadOnlyDictionary<string, string> ToDictionary()
{
return new Dictionary<string, string>()
{
{ nameof(FileNameDisallowedCharacters), FileNameDisallowedCharacters }
}.AsReadOnly();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment