Persilsoft.ValidationService.FluentValidation 1.0.4

dotnet add package Persilsoft.ValidationService.FluentValidation --version 1.0.4
NuGet\Install-Package Persilsoft.ValidationService.FluentValidation -Version 1.0.4
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Persilsoft.ValidationService.FluentValidation" Version="1.0.4" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Persilsoft.ValidationService.FluentValidation --version 1.0.4
#r "nuget: Persilsoft.ValidationService.FluentValidation, 1.0.4"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Persilsoft.ValidationService.FluentValidation as a Cake Addin
#addin nuget:?package=Persilsoft.ValidationService.FluentValidation&version=1.0.4

// Install Persilsoft.ValidationService.FluentValidation as a Cake Tool
#tool nuget:?package=Persilsoft.ValidationService.FluentValidation&version=1.0.4

An implementation of Persilsoft.Validation using FluentValidation.


Example

Let's start by creating a class to register an order with its details.

public class CreateOrderDto(string customerId, string shipAddress, string shipPostalCode, IEnumerable<CreateOrderDetailDto> orderDetails)
{
    public string CustomerId => customerId;
    public string ShipAddress => shipAddress;
    public string ShipPostalCode => shipPostalCode;
    public IEnumerable<CreateOrderDetailDto> OrderDetails => orderDetails;
}
public class CreateOrderDetailDto(int productId, decimal unitPrice, short quantity)
{
    public int ProductId => productId;
    public decimal UnitPrice => unitPrice;
    public short Quantity => quantity;
}

Now, let's implement validators for each model.

using Persilsoft.Validation.Abstracts;
using Persilsoft.Validation.Interfaces;

public class CreateOrderDtoValidator : AbstractModelValidator<CreateOrderDto>
{
    public CreateOrderDtoValidator(IValidationService<CreateOrderDto> validationService,
        IModelValidator<CreateOrderDetailDto> detailValidator)
        : base(validationService)
    {
        AddRuleFor(dto => dto.CustomerId)
            .NotEmpty("Customer Id is required.")
            .Length(7, "Customer Id must have 7 characters.");

        AddRuleFor(dto => dto.ShipAddress)
            .NotEmpty("Ship address is required.")
            .MinimumLength(20, "Ship address must have a minimum lenght of 20 characters.")
            .MaximumLength(50, "Ship address must have a maximum lenght of 50 characters.");

        AddRuleFor(dto => dto.ShipPostalCode)
            .NotEmpty("Ship postal code is required")
            .Length(5, "Ship postal code must have 5 characters.");

        AddRuleFor(dto => dto.OrderDetails)
            .NotNull("Order details is required.")
            .NotNull("Order details must not be empty");

        AddRuleForEach(dto => dto.OrderDetails)
            .SetValidator(detailValidator);
    }
}
using Persilsoft.Validation.Abstracts;
using Persilsoft.Validation.Interfaces;

internal class CreateOrderDetailDtoValidator :
    AbstractModelValidator<CreateOrderDetailDto>
{
    public CreateOrderDetailDtoValidator(IValidationService<CreateOrderDetailDto> validationService)
        : base(validationService)
    {
        AddRuleFor(d => d.ProductId)
            .GreaterThan(0, "Product Id must be greater than 0.");

        AddRuleFor<int>(d => d.Quantity)
            .GreaterThan(0, "Quantity must be greater than 0.");

        AddRuleFor(d => d.UnitPrice)
            .GreaterThan<decimal>(0, "Unit price must be greater than 0.");
    }
}

Next, let's register the validators and the validation service.

using ServiceCollectionExtensions;

builder.Services.AddModelValidator<CreateOrderDto, CreateOrderDtoValidator>();
builder.Services.AddModelValidator<CreateOrderDetailDto, CreateOrderDetailDtoValidator>();
builder.Services.AddValidationService();

Now, let's implement an Endpoint that receives the model, and we'll inject the validation hub for the model.

using Persilsoft.Exceptions.CustomExceptions;
using Persilsoft.Validation.Interfaces;

app.MapPost("/order/register", async (CreateOrderDto order, IModelValidatorHub<CreateOrderDto> modelValidatorHub) =>
{
    if (!await modelValidatorHub.Validate(order))
        throw new ValidationException(modelValidatorHub.Errors);

    Console.WriteLine("Sending the new order to the Database");
    await Task.Delay(750);

    return Results.Ok("Order registered!");
})
.WithName("Order");

Note:
You can use constructor injection.

You can implement a custom exception handler to handle the ValidationException and format the output appropriately, or you can include the Persilsoft.Exceptions package, which already includes a ready-to-use handler.

using ServiceCollectionExtensions;

builder.Services.AddValidationExceptionHandler();

If you will use the Persilsoft.Exceptions handler, it will be necessary to configure the Localization middleware in your AspNet Core application.

builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var SupportedCultures = new[] { "en-US", "es-PE" };
    var NeutralCulture = SupportedCultures[1];

    options.SetDefaultCulture(NeutralCulture)
    .AddSupportedCultures(SupportedCultures)6
    .AddSupportedUICultures(SupportedCultures);
});

Finally, we add the localization and exception handling middlewares to the AspNet Core pipeline

app.UseRequestLocalization();
app.UseExceptionHandler(e => { });

Note:
It's important that the localization middleware is added before the exception handling middleware; otherwise, you won't see the messages in the appropriate language.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Persilsoft.ValidationService.FluentValidation:

Package Downloads
Persilsoft.Membership.Blazor

Contains razor clases for use in frontend membership projects

Persilsoft.Membership.Backend.IoC

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.4 368 5/17/2024
1.0.3 157 5/4/2024
1.0.2 114 4/13/2024
1.0.1 90 4/13/2024
1.0.0 90 4/13/2024