我想用一些 ID 验证请求模型。我尝试使用批量请求预加载所有必需的数据。
问题是我的 RuleForEach
中的 WhereAsync
在 LoadUserGroupsAsync
完成或开始之前被调用。我用 TestValidateAsync(request)
开始验证。
是否有更好的解决方案?很遗憾,我没有找到任何解决方案。此外,我无法从 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");
}
}
更新 2021-04-28 - 向示例添加更多信息
答案 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)