gpt4 book ai didi

c# - 如何使用 FluentValidation 在 IValidateOptions 中编写流畅的验证规则?

转载 作者:行者123 更新时间:2023-12-05 03:32:46 26 4
gpt4 key购买 nike

对于我的 .Net 5 workerservice 应用程序,我想通过实现 IValidateOptions 来验证选项界面,但不想编写我自己的错误消息。这就是为什么我想使用包 FluentValidation.AspNetCore

给定模型

namespace App.Models
{
public class MyOptions
{
public string Text { get; set; }
}
}

我添加了验证规则

namespace App.ModelValidators
{
public class MyOptionsValidator : AbstractValidator<MyOptions>
{
public MyOptionsValidator()
{
RuleFor(model => model.Text).NotEmpty();
}
}
}

接下来我在验证中使用这个验证器

namespace App.OptionsValidators
{
public class MyOptionsValidator : IValidateOptions<MyOptions>
{
private readonly IValidator<MyOptions> _validator;

public MyOptionsValidator(IValidator<MyOptions> validator)
{
_validator = validator;
}

public ValidateOptionsResult Validate(string name, MyOptions options)
{
var validationResult = _validator.Validate(options);

if (validationResult.IsValid)
{
return ValidateOptionsResult.Success;
}

return ValidateOptionsResult.Fail(validationResult.Errors.Select(validationFailure => validationFailure.ErrorMessage));
}
}
}

最后我设置了 DI 容器

services.AddTransient<IValidator<MyOptions>, ModelValidators.MyOptionsValidator>();
services.AddSingleton<IValidateOptions<MyOptions>, OptionsValidators.MyOptionsValidator>();
services.Configure<MyOptions>(configuration.GetSection("My"));

我想知道这是否可以简化?

也许我可以实现 IValidateOptions接口(interface),避免 AbstractValidator并在 .Validate() 中编写流畅的规则方法?

我想要实现的示例代码

namespace App.OptionsValidators
{
public class MyOptionsValidator : IValidateOptions<MyOptions>
{
public ValidateOptionsResult Validate(string name, MyOptions options)
{
var validationResult = options.Text.Should.Not.Be.Empty();

if (validationResult.IsValid)
{
return ValidateOptionsResult.Success;
}

return ValidateOptionsResult.Fail(validationResult.ErrorMessage);
}
}
}

所以我不需要 AbstractValidator<MyOptions>不再。


我不确定的第一种方法

我没有使用 FluentValidation,而是使用了 DataAnnotations。

  • 我添加了 [Required]属性为 Text属性(property)
  • 我完全删除了类 MyOptionsValidator : AbstractValidator<MyOptions>
  • 我只在 DI 设置中注册了它

.

services.AddSingleton<IValidateOptions<MyOptions>, OptionsValidators.MyOptionsValidator>();
services.Configure<MyOptions>(configuration.GetSection("My"));

内部MyOptionsValidator我像这样验证选项

    public ValidateOptionsResult Validate(string name, MyOptions options)
{
var validationResults = new List<ValidationResult>();

if (!Validator.TryValidateObject(options, new ValidationContext(options), validationResults, true))
{
return ValidateOptionsResult.Fail(validationResults.Select(validationResult => validationResult.ErrorMessage));
}

return ValidateOptionsResult.Success;
}

但也许还有更好的方法:)

最佳答案

我非常喜欢在整个堆栈中使用相同的方法进行验证,在我的例子中,这是通过 FluentValidation。以下是我的方法。

为您的选项/设置验证器创建一个新的基本验证器:

public abstract class AbstractOptionsValidator<T> : AbstractValidator<T>, IValidateOptions<T>
where T : class
{
public virtual ValidateOptionsResult Validate(string name, T options)
{
var validateResult = this.Validate(options);
return validateResult.IsValid ? ValidateOptionsResult.Success : ValidateOptionsResult.Fail(validateResult.Errors.Select(x => x.ErrorMessage));
}
}

这扩展了 FluentValidation AbstractValidator<T>支持IValidateOptions<T> .您现在已经有了一个可用于所有选项/设置验证器的基础。对于以下设置:

public class FooSettings
{
public string Bar { get; set; }
}

你最终得到一个典型的验证器:

public class FooSettingsValidator : AbstractOptionsValidator<FooSettings>, IFooSettingsValidator
{
public FooSettingsValidator()
{
RuleFor(x => x.Bar).NotEmpty();
}
}

让 DI 容器知道它:

serviceCollection.AddSingleton<IValidateOptions<FooSettings>, FooSettingsValidator>();

如果没有内置的东西来执行上述操作,我希望 Scrutor 能将它变成一个自动过程。

以上所有内容都为我提供了使用 FluentValidation 的所有好处,同时利用 Microsoft 为我们提供的一流选项验证支持。

LINQPad 工作示例:

enter image description here

enter image description here

using static FluentAssertions.FluentActions;

void Main()
{
var fixture = new Fixture();
var validator = new FooSettingsValidator();
validator.Validate(fixture.Build<FooSettings>().Without(x => x.Bar).Create()).Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo(new string[] { "'Bar' must not be empty." });
validator.Validate(fixture.Build<FooSettings>().Create()).Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo(new string[] { });

using (var scope = ServiceProvider.Create(bar: null).CreateScope())
{
Invoking(() => scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<FooSettings>>().Value).Should().Throw<OptionsValidationException>();
}

using (var scope = ServiceProvider.Create(bar: "asdf").CreateScope())
{
scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<FooSettings>>().Value.Bar.Should().Be("asdf");
}
}

// You can define other methods, fields, classes and namespaces here
public class FooSettings
{
public string Bar { get; set; }
}

public interface IFooSettingsValidator : IValidator { }

public class FooSettingsValidator : AbstractOptionsValidator<FooSettings>, IFooSettingsValidator
{
public FooSettingsValidator()
{
RuleFor(x => x.Bar).NotEmpty();
}
}

public abstract class AbstractOptionsValidator<T> : AbstractValidator<T>, IValidateOptions<T>
where T : class
{
public virtual ValidateOptionsResult Validate(string name, T options)
{
var validateResult = this.Validate(options);
return validateResult.IsValid ? ValidateOptionsResult.Success : ValidateOptionsResult.Fail(validateResult.Errors.Select(x => x.ErrorMessage));
}
}

public class ServiceProvider
{
public static IServiceProvider Create(string bar)
{
var serviceCollection = new ServiceCollection();

var config = new ConfigurationBuilder()
.AddInMemoryCollection(
new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("Foo:Bar", bar) })
.Build();
serviceCollection.AddSingleton<IConfiguration>(config);
serviceCollection.AddOptions();
//serviceCollection.Configure<FooSettings>(config.GetSection("Foo"));
serviceCollection.AddOptions<FooSettings>()
.Bind(config.GetSection("Foo"));

serviceCollection.AddSingleton<IValidateOptions<FooSettings>, FooSettingsValidator>();
serviceCollection.AddSingleton<IFooSettingsValidator, FooSettingsValidator>();

return serviceCollection.BuildServiceProvider();
}
}

关于c# - 如何使用 FluentValidation 在 IValidateOptions 中编写流畅的验证规则?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70407637/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com