Skip to content

DamianEdwards/MiniValidation

main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Files

Permalink
Failed to load latest commit information.

MiniValidation

A minimalistic validation library built atop the existing features in .NET's System.ComponentModel.DataAnnotations namespace. Adds support for single-line validation calls and recursion with cycle detection.

Supports .NET Standard 2.0 compliant runtimes.

Installation

Nuget

Install the library from NuGet:

dotnet add package MiniValidation

ASP.NET Core 6+ Projects

If installing into an ASP.NET Core 6+ project, consider using the MinimalApis.Extensions package instead, which adds extensions specific to ASP.NET Core, including a validation endpoint filter for .NET 7 apps:

dotnet add package MinimalApis.Extensions

Example usage

Validate an object

var widget = new Widget { Name = "" };

var isValid = MiniValidator.TryValidate(widget, out var errors);

class Widget
{
    [Required, MinLength(3)]
    public string Name { get; set; }

    public override string ToString() => Name;
}

Use services from validators

var widget = new Widget { Name = "" };

// Get your serviceProvider from wherever makes sense
var serviceProvider = ...
var isValid = MiniValidator.TryValidate(widget, serviceProvider, out var errors);

class Widget : IValidatableObject
{
    [Required, MinLength(3)]
    public string Name { get; set; }

    public override string ToString() => Name;

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var disallowedNamesService = validationContext.GetService(typeof(IDisallowedNamesService)) as IDisallowedNamesService;

        if (disallowedNamesService is null)
        {
            throw new InvalidOperationException($"Validation of {nameof(Widget)} requires an {nameof(IDisallowedNamesService)} instance.");
        }

        if (disallowedNamesService.IsDisallowedName(Name))
        {
            yield return new($"Cannot name a widget '{Name}'.", new[] { nameof(Name) });
        }
    }
}

Console app

using System.ComponentModel.DataAnnotations;
using MiniValidation;

var title = args.Length > 0 ? args[0] : "";

var widgets = new List<Widget>
{
    new Widget { Name = title },
    new WidgetWithCustomValidation { Name = title }
};

foreach (var widget in widgets)
{
    if (!MiniValidator.TryValidate(widget, out var errors))
    {
        Console.WriteLine($"{nameof(Widget)} has errors!");
        foreach (var entry in errors)
        {
            Console.WriteLine($"  {entry.Key}:");
            foreach (var error in entry.Value)
            {
                Console.WriteLine($"  - {error}");
            }
        }
    }
    else
    {
        Console.WriteLine($"{nameof(Widget)} '{widget}' is valid!");
    }
}

class Widget
{
    [Required, MinLength(3)]
    public string Name { get; set; }

    public override string ToString() => Name;
}

class WidgetWithCustomValidation : Widget, IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.Equals(Name, "Widget", StringComparison.OrdinalIgnoreCase))
        {
            yield return new($"Cannot name a widget '{Name}'.", new[] { nameof(Name) });
        }
    }
}
widget.exe
Widget 'widget' is valid!
Widget has errors!
  Name:
  - Cannot name a widget 'widget'.widget.exe Ok
Widget has errors!
  Name:
  - The field Name must be a string or array type with a minimum length of '3'.
Widget has errors!
  Name:
  - The field Name must be a string or array type with a minimum length of '3'.widget.exe Widget
Widget 'Widget' is valid!
Widget has errors!
  Name:
  - Cannot name a widget 'Widget'.widget.exe MiniValidation
Widget 'MiniValidation' is valid!
Widget 'MiniValidation' is valid!

Web app (.NET 6)

using System.ComponentModel.DataAnnotations;
using MiniValidation;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World");

app.MapGet("/widgets", () =>
    new[] {
        new Widget { Name = "Shinerizer" },
        new Widget { Name = "Sparklizer" }
    });

app.MapGet("/widgets/{name}", (string name) =>
    new Widget { Name = name });

app.MapPost("/widgets", (Widget widget) =>
    !MiniValidator.TryValidate(widget, out var errors)
        ? Results.ValidationProblem(errors)
        : Results.Created($"/widgets/{widget.Name}", widget));

app.MapPost("/widgets/custom-validation", (WidgetWithCustomValidation widget) =>
    !MiniValidator.TryValidate(widget, out var errors)
        ? Results.ValidationProblem(errors)
        : Results.Created($"/widgets/{widget.Name}", widget));

app.Run();

class Widget
{
    [Required, MinLength(3)]
    public string? Name { get; set; }

    public override string? ToString() => Name;
}

class WidgetWithCustomValidation : Widget, IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.Equals(Name, "Widget", StringComparison.OrdinalIgnoreCase))
        {
            yield return new($"Cannot name a widget '{Name}'.", new[] { nameof(Name) });
        }
    }
}

About

A minimalist validation library for .NET built atop the existing features in `System.ComponentModel.DataAnnotations` namespace

Resources

License

Stars

Watchers

Forks

Languages