FluentValidation 预加载数据

时间:2021-04-28 11:55:47

标签: c# validation asp.net-core fluentvalidation

我想用一些 ID 验证请求模型。我尝试使用批量请求预加载所有必需的数据。 问题是我的 RuleForEach 中的 WhereAsyncLoadUserGroupsAsync 完成或开始之前被调用。我用 TestValidateAsync(request) 开始验证。

是否有更好的解决方案?很遗憾,我没有找到任何解决方案。此外,我无法从 RuleForRuleForEachWhere、...

外部访问模型
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");
    }
}

更新 2021-04-28 - 向示例添加更多信息

1 个答案:

答案 0 :(得分:0)

您可以为用户使用延迟加载和包装对象。然而,这需要调用同步方法而不是异步方法来加载用户。

您可以使用 Lazy<IEnumerable<User>> 对象,但这可能需要重构您的子验证器。我喜欢为 Lazy<IEnumerable<User>> 创建一个包装类,只是为了使代码向后兼容接受 IEnumerable<T> 对象的任何其他代码:

/// <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();
    }
}

这实际上是一个通用类,可以用于任何类型。老实说,我希望 .NET 在它的基础库中有这个类。我倾向于将其复制并粘贴到每个项目中,因为它非常易于使用。无论如何...

然后修改您的验证器类。由于您没有为验证器发布足够的代码,我对事物的名称和代码结构进行了一些猜测:

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;
    }
}

这将延迟加载用户,并且由于您将相同的对象传递给所有子验证器,因此无论集合迭代多少次,它只会加载用户一次。

最后,修改您的子验证器类以接受 IEnumerable<UserGroup> 对象而不是数组:

public class UserUpdateValidator : AbstractValidator<UserUpdateDto>
{
    public UserUpdateValidator(IEnumerable<UserGroup> groups)
相关问题