Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

361
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda