c# - 如何从 C# Core 中的 DI 获取可用的 IOptions 列表?

标签 c# dependency-injection .net-core

关注这篇文章:https://blog.bredvid.no/validating-configuration-in-asp-net-core-e9825bd15f10

我现在可以在服务需要时验证设置。我想做的是在 program.cs 中服务器启动时直接验证。

我不知道该怎么做?有没有办法获取注入(inject)到 DI 中的服务列表,然后验证类型是否可从 IOption 分配并注册?

以下是我如何将设置添加到 DI:

    //App settings
    services.ConfigureAndValidate<AuthenticationSettings>(Configuration);
    services.ConfigureAndValidate<SmtpSettings>(Configuration);

扩展代码:

public static class IServiceCollectionExtensions
    {
        public static IServiceCollection ConfigureAndValidate<T>(
            this IServiceCollection serviceCollection,
            IConfiguration config,
            string section = null
        ) where T : class
        {
            var configType = typeof(T).Name;
            if (string.IsNullOrEmpty(section)) { 
                section = configType;
            }

            return serviceCollection
                .Configure<T>(config.GetSection(section))
                .PostConfigure<T>(settings =>
                {
                    var configErrors = settings.ValidationErrors().ToArray();
                    if (configErrors.Any())
                    {
                        var aggrErrors = string.Join(",", configErrors);
                        var count = configErrors.Length;
                        throw new ApplicationException($"Found {count} configuration error(s) in {configType}: {aggrErrors}");
                    }
                });
        }

        private static IEnumerable<string> ValidationErrors(this object obj)
        {
            var context = new ValidationContext(obj, serviceProvider: null, items: null);
            var results = new List<ValidationResult>();
            Validator.TryValidateObject(obj, context, results, true);
            foreach (var validationResult in results)
            {
                yield return validationResult.ErrorMessage;
            }
        }
    }

这是我当前的启动器:

public class Program
{
    public static async Task Main(string[] args)
    {
        var webHost = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddEnvironmentVariables();

                var env = hostingContext.HostingEnvironment;

                config.SetBasePath(env.ContentRootPath)
                      .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
            })
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                logging.AddDebug();
            })
            .UseStartup<Startup>()
            .Build();

        using (var scope = webHost.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            /// <---- BEGIN / AN IDEA OF WHAT I WOULD LIKE TO DO ---->
            /// <---- THIS CODE IS NOT WORKING ---->
            var allServices = services.GetAllServices();
            if (allServices != null)
            {
                foreach (var service in allServices )
                {
                    if (service.ServiceType.IsAssignableFrom(IOptions))
                    {
                       services.GetRequiredService<service.ServiceType>()
                    }
                }
            }
            /// <---- END ---->
        }

        await webHost.RunAsync();
    }
}

如果您有任何建议,请在评论中告诉我。

感谢您的帮助。


编辑 1: 感谢 Steven 的帮助,有了你的回答,它帮助我继续寻找答案,但仍然缺少一些东西。

现在,我所有的设置都继承自 ISettings,例如:

public class AuthenticationSettings : ISettings
{
    [Required]
    public string Issuer { get; set; }
    [Required]
    public string Audience { get; set; }
    [Required]
    public string SecretKey { get; set; }
    [Required]
    public int ExpirationDurationInDays { get; set; }
}

我像这样更新 Program.cs:

using Autofac;
using Autofac.Core;



var options = services.GetService<ILifetimeScope>()
   .ComponentRegistry
   .Registrations.SelectMany(e => e.Services)
   .Select(s => s as TypedService)
   .Where(s => s.ServiceType.IsGenericType && s.ServiceType.GetGenericTypeDefinition() == typeof(IConfigureOptions<>))
   .Select(s => s.ServiceType.GetGenericArguments()[0])
   .Where(s => typeof(ISettings).IsAssignableFrom(s))
   .ToList();

所以现在我需要实例化选项中的每个选项并获取值。我还在努力。如果您有任何建议或解决方案,请告诉我:)

最佳答案

您可以通过迭代 IServiceCollection 实例来获取已配置选项类型的列表:

var configuredOptionTypes =
    from descriptor in services
    let serviceType = descriptor.ServiceType
    where serviceType.IsGenericType
    where serviceType.GetGenericTypeDefinition() == typeof(IConfigureNamedOptions<>)
    let optionType = serviceType.GetGenericArguments()[0]
    select optionType;

关于c# - 如何从 C# Core 中的 DI 获取可用的 IOptions 列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54127217/

相关文章:

c# - 通过避免像点击事件这样的回发,使用 javascript 显示消息框。确定,取消按钮应该只关闭消息框

c# - 与 IIS 相比,Kestrel 的性能较慢

c# - 如何在 ASP.NET Core 中注册单例工厂解析作用域对象?

c# - 从 url 中提取网站名称

c# - 如何按类型过滤集合?

c# - 如何将相同的依赖关系绑定(bind)到 Ninject 中的多个依赖项?

php - PHP 5.4 对象取消引用是否成功缓解了此 DI 容器中静态存储参数的缺点?

java - Spring中使用依赖注入(inject)替代工厂模式

asp.net-mvc - Aspnet Core Azure 失败并出现 HTTP 错误 502.5 - 进程失败

entity-framework - EF Core 工具版本更新 2.1.1