rbasniak
rbasniak

Reputation: 4984

FluentValidator extension method to check if entity exists in database

I have a pretty common situation in which I need to validate when a reference property is being set in a given entity. When this happens I validate like this:

public class Validator : AbstractValidator<Command>
{
    public Validator()
    {
        ...

        RuleFor(user => user.EmployeeId)
            .MustNotBeEmpty() 
            .MustAsync(ExistInDatabase).WithMessage("Employee not found"); 
    }

    public async Task<bool> ExistInDatabase(Command command, string id, CancellationToken cancelation)
    {
        return await _context.Employees.AnyAsync(x => x.Id == id.ToGuid());
    }
}

This and other check in database are so much common and I'm writing methods like this in almost every validator.

I would like to turn this into an extension method, in which I would pass the entity type, the context and the id.

A FluentValidation extension method looks like this:

public static IRuleBuilderOptions<T, string> MustNotBeEmpty<T>(this IRuleBuilder<T, string> rule)
{
    return rule
        .NotEmpty().WithMessage("O campo '{PropertyName}' não pode ser vazio");
}

But these extension methods already receive generic types and I'm not figuring out how to pass another generic type to use with context.Set<T>().AnyAsync(...)

How could it be done?

----- UPDATE -----

I tried adding another generic type T2 to the extension method but it does not work:

    public static IRuleBuilderOptions<T, string> MustExistInDatabase<T, T2>(this IRuleBuilder<T, string> rule, DatabaseContext context) where T2: BaseEntity
    {
        return rule.MustAsync(async (command, id, cancelation) => await context.Set<T2>().AnyAsync(x => x.Id == id.ToGuid())).WithMessage("'{PropertyName}' not found in database");
    } 

When I try to call it, the compiler complains that it could not found such extension method:

public class Validator : AbstractValidator<Command>
{
    public Validator()
    {
        ...

        RuleFor(user => user.EmployeeId)
            .MustNotBeEmpty() 
            .MustExistInDatabase<Employee>(); 
    }
}

Upvotes: 3

Views: 2063

Answers (1)

tickwave
tickwave

Reputation: 3455

I have the same issue and ends up doing this

    public static IRuleBuilderOptions<T, Guid> MustExistInDatabase<T, TEntity>(this IRuleBuilder<T, Guid> ruleBuilder, DbSet<TEntity> dbSet) where TEntity : class
    {
        return ruleBuilder.Must(id => dbSet.Find(id) != null).WithMessage("'{PropertyName}' {PropertyValue} not found.");
    }

Then

RuleFor(r => r.EmployeeId).NotEmpty().MustExistInDatabase(context.Employees);

Upvotes: 2

Related Questions