如何在 IValidateOptions<T> 实现中触发模型验证?
How to trigger model validation inside IValidateOptions<T> implementation?
我有一个 .Net 5 应用程序,想为我的配置添加验证器。鉴于此样本选项
public sealed class DatabaseOptions
{
public string ConnectionString { get; set; }
}
我目前正在使用此实现对其进行验证
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string name, DatabaseOptions databaseOptions)
{
List<string> validationFailures = new List<string>();
if (string.IsNullOrEmpty(databaseOptions.ConnectionString))
validationFailures.Add($"{nameof(databaseOptions.ConnectionString)} is required.");
// ...
if (validationFailures.Any())
{
return ValidateOptionsResult.Fail(validationFailures);
}
return ValidateOptionsResult.Success;
}
}
我想避免实施自己的验证检查和错误消息,因为我知道数据注释已经完成了工作。
我将选项模型修改成了这个
public sealed class DatabaseOptions
{
[Required]
[MinLength(9999999)] // for testing purposes
public string ConnectionString { get; set; }
}
并希望找到一种方法来触发模型验证
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string name, DatabaseOptions databaseOptions)
{
List<string> validationFailures = new List<string>();
// trigger the model validation and add every error to the validationFailures list
if (validationFailures.Any())
{
return ValidateOptionsResult.Fail(validationFailures);
}
return ValidateOptionsResult.Success;
}
}
但不幸的是我没能做到。调试器命中验证器,但如何在 Validate 方法中触发验证?
我使用一种技术在我的 netcore 应用程序中验证数据注释,不是使用 IValidateOptions
,而是实现自定义验证器,并将其注册为 PostConfigure
。
您可以在命名空间 System.ComponentModel.DataAnnotations
中找到有价值的资产。
像这样:
// Custom validator for data annotations
public static class Validation {
public static void ValidateDataAnotations<TOptions>(TOptions options) {
var context = new ValidationContext(options);
var results = new List<ValidationResult>();
Validator.TryValidateObject(options, context, results, validateAllProperties: true);
if (results.Any()) {
var aggrErrors = string.Join(' ', results.Select(x => x.ErrorMessage));
var count = results.Count;
var configType = typeof(TOptions).Name;
throw new ApplicationException($"Found {count} configuration error(s) in {configType}: {aggrErrors}");
}
}
}
然后,你在组合根目录中注册这个静态方法(可能Startup.cs
):
public void ConfigureServices(IConfiguration configuration, IServiceCollection serviceCollection) {
// (...)
serviceCollection.Configure<DatabaseOptions>(configuration.GetSection(nameof(DatabaseOptions)));
// invalid configuration values will break at this point
serviceCollection.PostConfigure<DatabaseOptions>(Validation.ValidateDataAnotations);
}
请查看评论,因为我的解决方案已经可用!
基于我创建了自己的基于数据注释的选项验证器
public sealed class OptionsValidator<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
public ValidateOptionsResult Validate(string name, TOptions options)
{
ValidationContext validationContext = new ValidationContext(options);
List<ValidationResult> validationResults = new List<ValidationResult>();
bool noValidationErrorsOccured = Validator.TryValidateObject(options, validationContext, validationResults, true);
if (noValidationErrorsOccured) {
return ValidateOptionsResult.Success;
}
IEnumerable<string> validationFailures = validationResults.Select(validationResult => validationResult.ErrorMessage);
return ValidateOptionsResult.Fail(validationFailures);
}
}
所以每当我想向我的 DI 容器添加验证器时,我都可以使用这个扩展方法
public static IServiceCollection AddOptionsValidator<TOptions>(this IServiceCollection serviceCollection) where TOptions : class
=> serviceCollection.AddSingleton<IValidateOptions<TOptions>, OptionsValidator<TOptions>>();
我有一个 .Net 5 应用程序,想为我的配置添加验证器。鉴于此样本选项
public sealed class DatabaseOptions
{
public string ConnectionString { get; set; }
}
我目前正在使用此实现对其进行验证
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string name, DatabaseOptions databaseOptions)
{
List<string> validationFailures = new List<string>();
if (string.IsNullOrEmpty(databaseOptions.ConnectionString))
validationFailures.Add($"{nameof(databaseOptions.ConnectionString)} is required.");
// ...
if (validationFailures.Any())
{
return ValidateOptionsResult.Fail(validationFailures);
}
return ValidateOptionsResult.Success;
}
}
我想避免实施自己的验证检查和错误消息,因为我知道数据注释已经完成了工作。
我将选项模型修改成了这个
public sealed class DatabaseOptions
{
[Required]
[MinLength(9999999)] // for testing purposes
public string ConnectionString { get; set; }
}
并希望找到一种方法来触发模型验证
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string name, DatabaseOptions databaseOptions)
{
List<string> validationFailures = new List<string>();
// trigger the model validation and add every error to the validationFailures list
if (validationFailures.Any())
{
return ValidateOptionsResult.Fail(validationFailures);
}
return ValidateOptionsResult.Success;
}
}
但不幸的是我没能做到。调试器命中验证器,但如何在 Validate 方法中触发验证?
我使用一种技术在我的 netcore 应用程序中验证数据注释,不是使用 IValidateOptions
,而是实现自定义验证器,并将其注册为 PostConfigure
。
您可以在命名空间 System.ComponentModel.DataAnnotations
中找到有价值的资产。
像这样:
// Custom validator for data annotations
public static class Validation {
public static void ValidateDataAnotations<TOptions>(TOptions options) {
var context = new ValidationContext(options);
var results = new List<ValidationResult>();
Validator.TryValidateObject(options, context, results, validateAllProperties: true);
if (results.Any()) {
var aggrErrors = string.Join(' ', results.Select(x => x.ErrorMessage));
var count = results.Count;
var configType = typeof(TOptions).Name;
throw new ApplicationException($"Found {count} configuration error(s) in {configType}: {aggrErrors}");
}
}
}
然后,你在组合根目录中注册这个静态方法(可能Startup.cs
):
public void ConfigureServices(IConfiguration configuration, IServiceCollection serviceCollection) {
// (...)
serviceCollection.Configure<DatabaseOptions>(configuration.GetSection(nameof(DatabaseOptions)));
// invalid configuration values will break at this point
serviceCollection.PostConfigure<DatabaseOptions>(Validation.ValidateDataAnotations);
}
请查看评论,因为我的解决方案已经可用!
基于
public sealed class OptionsValidator<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
public ValidateOptionsResult Validate(string name, TOptions options)
{
ValidationContext validationContext = new ValidationContext(options);
List<ValidationResult> validationResults = new List<ValidationResult>();
bool noValidationErrorsOccured = Validator.TryValidateObject(options, validationContext, validationResults, true);
if (noValidationErrorsOccured) {
return ValidateOptionsResult.Success;
}
IEnumerable<string> validationFailures = validationResults.Select(validationResult => validationResult.ErrorMessage);
return ValidateOptionsResult.Fail(validationFailures);
}
}
所以每当我想向我的 DI 容器添加验证器时,我都可以使用这个扩展方法
public static IServiceCollection AddOptionsValidator<TOptions>(this IServiceCollection serviceCollection) where TOptions : class
=> serviceCollection.AddSingleton<IValidateOptions<TOptions>, OptionsValidator<TOptions>>();