Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

365
Visualizações
FluentValidation preload data

I want validate a request model with some ids. I try to preload all required data with a bulk request. The problem is the RuleForEach inside my WhereAsync is called before the LoadUserGroupsAsync is done or started. I start the validation with TestValidateAsync(request).

Is there a better solution for this I have unfortunately not found any solutions for it. Also I have no access to the model from outside a RuleFor, RuleForEach, Where, ...

private readonly List<UserGroup> _userGroups;
WhenAsync(async (request, cancellationToken) => await this.LoadUserGroupsAsync(request.Items, cancellationToken), () =>
{
    RuleForEach(o => o.Items).SetValidator(new UserUpdateValidator(this._userGroups));
});
private async Task<bool> LoadUserGroupsAsync(UserUpdateDto[] userUpdates, CancellationToken cancellationToken)
{
    var ids = userUpdates.Select(o => o.userGroupId);
    this._userGroups = await this._userGroupService.GetByIdsAsync(ids, cancellationToken);

    return true;
}
public class UserUpdateValidator : AbstractValidator<UserUpdateDto>
{
    public UserUpdateValidator(
       UserGroup[] groups)
    {
        RuleFor(item => item.UserGroupId).Must(userGroupId =>
        {
            var group = groups.SingleOrDefault(o => o.Id == userGroupId);
            if (group == null)
            {
                return false;
            }

            return true;
        }).WithMessage("Group is invalid");

        RuleFor(item => item.UserGroupId).Must(userGroupId =>
        {
            var group = groups.SingleOrDefault(o => o.Id == userGroupId);
            return group.Active;
        }).WithMessage("Group is inactive");

        RuleFor(item => item.Password).Must((context, password) =>
        {
            var group = groups.SingleOrDefault(o => o.Id == context.UserGroupId);
            if (group.Permissions.Contains("AllowPasswordChange"))
            {
                return true;
            }

            return false;
        }).WithMessage("It is now allowed to change the password for your user");
    }
}

Update 2021-04-28 - Add more Informations to example

over 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

You can use lazy loading and a wrapper object for the users. This would require calling sync methods instead of async methods to load the users, however.

You could use a Lazy<IEnumerable<User>> object, but that would probably require refactoring your child validator. I like creating a wrapper class for Lazy<IEnumerable<User>> just to make the code backwards-compatible for any other code accepting IEnumerable<T> objects:

/// <summary>
/// Represents a lazy loaded enumerable of type T
/// </summary>
public class LazyEnumerable<T> : IEnumerable<T>
{
    private readonly Lazy<IEnumerable<T>> items;

    /// <summary>
    /// Initializes a new lazy loaded enumerable.
    /// </summary>
    /// <param name="itemFactory">A lambda expression that returns the items to be iterated over.</param>
    public LazyEnumerable(Func<IEnumerable<T> itemFactory)
    {
        items = new Lazy<T>>(itemFactory);
    }

    /// <summary>
    /// Initializes a new lazy loaded enumerable.
    /// </summary>
    /// <param name="itemFactory">The Lazy<T> object used to lazily retrieve the items to be iterated over.</param>
    public LazyEnumerable(Lazy<IEnumerable<T>> items)
    {
        this.items = items;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return items.Value.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return items.Value.GetEnumerator();
    }
}

This is actually a general purpose class that can be used for any type. I wish .NET had this class in its base library, to be honest. I tend to copy and paste this in to every project, because it is so easy to use. In any event...

Then modify your validator class. Since you did not post enough code for your validator, I took a few guesses about the names of things and structure of your code:

public class YourValidator : AbstractValidator<X>
{
    private UserService _userService;
    private IEnumerable<UserGroup> _userGroups;

    public YourValidator(UserService userService)
    {
        _userService = userService;

        When((request, cancellationToken) => PreloadUserGroups(request.Items, cancellationToken), () =>
        {
            RuleForEach(o => o.Items).SetValidator(new UserUpdateValidator(_userGroups));
        });
    }

    private bool PreloadUserGroups(UserUpdateDto[] userUpdates, CancellationToken cancellationToken)
    {
        var ids = userUpdates.Select(o => o.userGroupId);

        _userGroups = new LazyEnumerable<UserGroup>(() => _userService.GetByIds(ids, cancellationToken));

        return true;
    }
}

This will lazy load the users, and since you pass the same object to all child validators it will load the users only once, regardless of how many times the collection is iterated.

Lastly, modify your child validator class to accept an IEnumerable<UserGroup> object instead of an array:

public class UserUpdateValidator : AbstractValidator<UserUpdateDto>
{
    public UserUpdateValidator(IEnumerable<UserGroup> groups)
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda