Validation plays a crucial role in the.NET development environment in guaranteeing data integrity and robustness of applications. A popular package called FluentValidation offers a clear and expressive method for defining validation rules in.NET applications. We will look at how to make the process of dynamically registering FluentValidation classes in.NET Minimal APIs more efficient in this blog post. This method improves the maintainability of your apps' validation mechanism while lowering manual overhead.

Understanding Dynamic Registration
Dynamic registration refers to the process of automatically discovering and registering components at runtime, based on predefined conventions or criteria. In the context of FluentValidation and .NET Minimal APIs, dynamic registration allows us to scan assemblies, identify validator classes, and register them with the dependency injection container without explicit manual intervention.

Benefits of Dynamic Registration with FluentValidation

  • Reduced Boilerplate Code: With dynamic registration, developers no longer need to manually register each validator class. This reduces boilerplate code and makes the registration process more concise and maintainable.
  • Improved Scalability: As your application grows and evolves, dynamic registration accommodates new validator classes seamlessly. Adding or modifying validation logic becomes simpler and requires minimal changes to the registration code.
  • Enhanced Flexibility: Dynamic registration enables developers to adhere to conventions or patterns for validator class naming and structure. This promotes consistency and simplifies maintenance across the codebase.

Implementing Dynamic Registration
To implement dynamic registration of FluentValidation classes in .NET Minimal APIs, follow these steps.

  • The tools that we are leveraging here are.
  • FluentValidation
  • Microsoft.Extensions.DependencyInjection.Abstractions
  • .NET 8.0
  • Visual Studio 2022 Community Edition
  • Web API

Scanning Assemblies: Utilize reflection to scan assemblies at runtime and identify classes that implement the IValidator<T> interface. This involves iterating through types, filtering out validator classes, and extracting generic type arguments.Registering Validators: Once validator classes are identified, register them with the dependency injection container using the appropriate service lifetime scope (e.g., scoped, transient). Map each validator interface (IValidator<T>) to its corresponding concrete implementation.Integration with Startup Logic: Integrate the dynamic registration logic into the startup/configuration code of your .NET 6 Minimal API project. This typically involves invoking the scanning mechanism during application startup and configuring the dependency injection container accordingly.Code Snippet//Person Domain Model

public class Person
 {
     public string Name { get; set; }
     public int Age { get; set; }
     public string Email { get; set; }

 }

//PersonValidation.cs
public class PersonValidation:AbstractValidator<Person>
{
    public PersonValidation()
    {
        RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required");
        RuleFor(x => x.Age).GreaterThan(0).WithMessage("Age must be greater than 18");
        RuleFor(x => x.Email).EmailAddress().WithMessage("Email is not valid");
    }
}

//Create Person Action
app.MapPost("Person",([FromBody] Person person, IValidator<Person> validator) =>
{
    var personValidation = validator.Validate(person);
    if (!personValidation.IsValid)
    {
        return Results.BadRequest(personValidation.ToDictionary());
    }
    return Results.Ok("Person created successfully");
})
    .WithName("CreatePerson")
    .WithOpenApi();

//TypeExtension.cs

/*
This extension method is designed to check whether a given type or any of its base types implement a generic type or generic interface specified by genericType. It handles both interface and inheritance hierarchies, recursively checking base types until a match is found or until there are no more base types to check.

*/

public static class TypeExtensions
{
    public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
    {
        var interfaceTypes = givenType.GetInterfaces();

        foreach (var it in interfaceTypes)
        {
            if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
                return true;
        }

        if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
            return true;

        var baseType = givenType.BaseType;
        if (baseType == null) return false;

        return IsAssignableToGenericType(baseType, genericType);
    }
}

//FluentValidationServiceCollections.cs
public static class FluentValidationServiceCollections
{
    public static void AddFluentValidationValidators(this IServiceCollection services)
    {
        var validatorTypes = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(t => !t.IsAbstract && t.IsAssignableToGenericType(typeof(AbstractValidator<>)))
            .ToList();

        foreach (var validatorType in validatorTypes)
        {
            var genericArg = validatorType.BaseType!.GetGenericArguments().FirstOrDefault();
            if (genericArg != null)
            {
                var serviceType = typeof(IValidator<>).MakeGenericType(genericArg);
                services.AddScoped(serviceType, validatorType);
            }
        }
    }
}

//register the fluent validation validators in Program.cs
builder.Services.AddFluentValidationValidators();

HostForLIFE ASP.NET Core 8.0.1 Hosting

European best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.