如何使用带有ValidateDataAnnotations的配置 [英] How to Use Configuration with ValidateDataAnnotations

查看:192
本文介绍了如何使用带有ValidateDataAnnotations的配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读选项

I've read the Microsoft documentation of fundamentals for Options and Configuration, but still can't find the right way to extract configuration into an object while validating data annotations.

我在Startup.ConfigureServices

services.AddOptions<EmailConfig>().Bind(Configuration.GetSection("Email")).ValidateDataAnnotations();

此应该"允许通过将其添加到类构造函数中来访问配置:(IOptions<EmailConfig> emailConfig)

This "should" allow accessing the configuration by adding this in the class constructor: (IOptions<EmailConfig> emailConfig)

但是它不起作用.

另一种方法是将(IConfiguration configuration)添加到构造函数中,但这不允许我调用ValidateDataAnnotations.

Another approach is to add (IConfiguration configuration) to the constructor, but this doesn't allow me to call ValidateDataAnnotations.

configuration.GetSection("Email").Get<EmailConfig>();

第一个问题:绑定和验证配置的责任是属于Startup类还是属于使用它的类?如果它被多个类使用,我会说它属于Startup;并且该类可以在具有不同配置布局的另一个项目中使用.

First question: does the responsibility to bind and validate the configuration belong to the Startup class or to the class using it? If it's used by several classes I'd say it belongs to Startup; and the class could be used in another project with different configuration layout.

第二个问题:绑定和验证配置以便可以从类中访问它的正确语法是什么?

Second question: what is the correct syntax to bind and validate the configuration so it can be accessed from the class?

第三个问题:如果我要通过Startup中的数据批注进行验证,则使用配置的类仅假设该配置有效,并且我不进行任何任何重新验证?

Third question: if I'm validating through data annotations in Startup, then the class using the configuration simply assumes the configuration is valid and I don't put any re-validation whatsoever?

更新:在获得更多经验并查看了所有代码的结构之后,我更改了方法,以遵循标准模式.

以下代码可以工作...但是仅在使用时对其进行验证.可以在类库中注册它,并且在使用特定服务之前不会抛出任何错误.

The following code DOES work... but only validates it when used. This can be registered in a class library and won't throw any errors until the particular service is used.

services.AddOptions<EmailConfig>()
    .Bind(configuration.GetSection("Email"))
    .ValidateDataAnnotations();

然后,在配置"中,我将其添加为在启动时强制验证所需的配置值(CheckNotNull是自定义扩展方法,重要的是您只需调用IOptions.Value

Then, in Configure, I add this to force validation of needed configuration values at startup (CheckNotNull is a custom extension method, what matters is simply that you call IOptions.Value

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app?.ApplicationServices.GetService<IOptions<EmailConfig>>().Value.CheckNotNull("Config: Email");
    app?.ApplicationServices.GetService<IOptions<OntraportConfig>>().Value.CheckNotNull("Config: Ontraport");
    ...

然后在课堂上使用它

public class EmailService(IOptions<EmailConfig> config)

推荐答案

在将其添加到服务集合之前,您可以尝试在启动时亲自验证该类.

You can try validating the class yourself in start up before adding it to service collection.

启动

var settings = Configuration.GetSection("Email").Get<EmailConfig>();

//validate
var validationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(settings, serviceProvider: null, items: null);
if (!Validator.TryValidateObject(settings, validationContext, validationResults, 
        validateAllProperties: true)) {
    //...Fail early
    //will have the validation results in the list
}

services.AddSingleton(settings);

这样,您就不会与IOptions耦合,并且还允许代码尽早失败,并且可以在需要时显式注入依赖项.

That way you are not coupled to IOptions and you also allow your code to fail early and you can explicitly inject the dependency where needed.

您可以将验证打包到您自己的扩展方法中,例如

You could package the validation up into your own extension method like

public static T GetValid<T>(this IConfiguration configuration) {
    var obj = configuration.Get<T>();    
    //validate
     Validator.ValidateObject(obj, new ValidationContext(obj), true);    
    return obj;
}

用于类似

EmailConfig emailSection = Configuration.GetSection("Email").GetValid<EmailConfig>();
services.AddSingleton(emailSection);

内部, 基本上是在做同样的事情.

Internally, ValidateDataAnnotations is basically doing the same thing.

/// <summary>
/// Validates a specific named options instance (or all when name is null).
/// </summary>
/// <param name="name">The name of the options instance being validated.</param>
/// <param name="options">The options instance.</param>
/// <returns>The <see cref="ValidateOptionsResult"/> result.</returns>
public ValidateOptionsResult Validate(string name, TOptions options)
{
    // Null name is used to configure all named options.
    if (Name == null || name == Name)
    {
        var validationResults = new List<ValidationResult>();
        if (Validator.TryValidateObject(options,
            new ValidationContext(options, serviceProvider: null, items: null), 
            validationResults, 
            validateAllProperties: true))
        {
            return ValidateOptionsResult.Success;
        }

        return ValidateOptionsResult.Fail(String.Join(Environment.NewLine,
            validationResults.Select(r => "DataAnnotation validation failed for members " +
                String.Join(", ", r.MemberNames) +
                " with the error '" + r.ErrorMessage + "'.")));
    }

    // Ignored if not validating this instance.
    return ValidateOptionsResult.Skip;
}

源代码

这篇关于如何使用带有ValidateDataAnnotations的配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆