C# FluentValidation预加载数据

C# FluentValidation预加载数据,c#,validation,asp.net-core,fluentvalidation,C#,Validation,Asp.net Core,Fluentvalidation,我想用一些ID验证一个请求模型。我尝试通过批量请求预加载所有必需的数据。 问题在于my中的RuleForEach,在执行或启动LoadUserGroupsAsync之前,会调用同步。我使用testvalidateSync(请求)开始验证 有没有更好的解决办法?很遗憾,我还没有找到任何解决办法。此外,我无法从RuleFor,RuleForEach,Where,…外部访问模型 private readonly List<UserGroup> _userGroups; private a

我想用一些ID验证一个请求模型。我尝试通过批量请求预加载所有必需的数据。 问题在于my
中的
RuleForEach
,在执行或启动
LoadUserGroupsAsync
之前,会调用同步。我使用
testvalidateSync(请求)
开始验证

有没有更好的解决办法?很遗憾,我还没有找到任何解决办法。此外,我无法从
RuleFor
RuleForEach
Where
,…外部访问模型

private readonly List<UserGroup> _userGroups;
private async Task LoadUserGroupsAsync(UserUpdateDto[]userUpdates,CancellationToken CancellationToken)
{
var id=userUpdates.Select(o=>o.userGroupId);
this.\u userGroups=wait this.\u userGroupService.GetByIdsAsync(ids,cancellationToken);
返回true;
}
公共类UserUpdateValidator:AbstractValidator
{
公共用户UpdateValidator(
用户组[]组)
{
RuleFor(item=>item.UserGroupId).Must(UserGroupId=>
{
var group=groups.SingleOrDefault(o=>o.Id==userGroupId);
如果(组==null)
{
返回false;
}
返回true;
}).WithMessage(“组无效”);
RuleFor(item=>item.UserGroupId).Must(UserGroupId=>
{
var group=groups.SingleOrDefault(o=>o.Id==userGroupId);
返回组。活动;
}).WithMessage(“组处于非活动状态”);
RuleFor(item=>item.Password)。必须((上下文,密码)=>
{
var group=groups.SingleOrDefault(o=>o.Id==context.UserGroupId);
if(group.Permissions.Contains(“AllowPasswordChange”))
{
返回true;
}
返回false;
}).WithMessage(“现在允许更改用户的密码”);
}
}

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

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

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

/// <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();
    }
}
//
///表示类型为T的延迟加载枚举
/// 
公共类LazyEnumerable:IEnumerable
{
私有只读惰性项;
/// 
///初始化新的延迟加载的枚举。
/// 
///返回要迭代的项的lambda表达式。
公共休闲可枚举(Func(项目工厂);
}
/// 
///初始化新的延迟加载的枚举。
/// 
///用于延迟检索要迭代的项的延迟对象。
公共懒散可枚举项(懒散项)
{
这个项目=项目;
}
公共IEnumerator GetEnumerator()
{
返回items.Value.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
返回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;
    }
}
公共类YourValidator:AbstractValidator
{
私人用户服务(UserService);;
私有IEnumerable用户组;
公共YourValidator(用户服务用户服务)
{
_userService=userService;
当((request,cancellationToken)=>preload用户组(request.Items,cancellationToken),()=>
{
RuleForEach(o=>o.Items).SetValidator(新的UserUpdateValidator(_userGroups));
});
}
私有bool预加载用户组(UserUpdateDto[]userUpdates,CancellationToken CancellationToken)
{
var id=userUpdates.Select(o=>o.userGroupId);
_userGroups=新的LazyEnumerable(()=>_userService.GetByIds(ids,cancellationToken));
返回true;
}
}
这将延迟加载用户,因为您将同一对象传递给所有子验证器,所以无论集合迭代多少次,它只加载用户一次

最后,修改子验证程序类以接受
IEnumerable
对象而不是数组:

public class UserUpdateValidator : AbstractValidator<UserUpdateDto>
{
    public UserUpdateValidator(IEnumerable<UserGroup> groups)
公共类UserUpdateValidator:AbstractValidator
{
公共用户UpdateValidator(IEnumerable组)

与其在验证器中解决这一问题,不如在用户服务中实现缓存,这可能是一个更好的解决方案。不幸的是,我无法发布推荐这一解决方案的答案,因为我需要看到更多的代码。基本上,验证器中的所有代码,以及用户服务中足够的代码可以帮助制定解决方案。但这将是我的职责建议。将缓存添加到用户服务。@GregBurghardt例如,我想检查用户组是否存在。但现在我必须为每个组id分别加载它们。此外,如果我实现缓存,我必须实现一个逻辑,在更新时使缓存失效。因此,我更喜欢一个逻辑,即预先加载组一次对于请求中的所有用户。您能添加更多的验证程序类吗?我想我有一个解决方案,但我没有足够的信息。我需要查看整个验证程序类。@GregBurghardt我有改进问题,请添加上层验证程序的代码-初始化子验证程序的验证程序。
/// <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();
    }
}
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;
    }
}
public class UserUpdateValidator : AbstractValidator<UserUpdateDto>
{
    public UserUpdateValidator(IEnumerable<UserGroup> groups)