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

时间:2019-01-08 16:09:22

标签: c# dependency-injection .net-core

此帖子之后:https://blog.bredvid.no/validating-configuration-in-asp-net-core-e9825bd15f10

我现在可以在服务需要时用来验证设置。我想做的就是直接在server.cs中启动服务器时进行验证。

我不确定该怎么做?有没有办法获取注入到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();

所以现在我需要实例化options中的每个选项并获取Value。我还在努力。让我知道您是否有任何建议或解决方法:)

2 个答案:

答案 0 :(得分:1)

您可以通过迭代chrome://flags/#enable-service-worker-servicification实例来获得配置的选项类型的列表:

IServiceCollection

答案 1 :(得分:0)

以下是史蒂文的建议,这是我的解决方案: 我的设置验证器服务

    public SettingsValidator(
        IServiceProvider services,
        ILifetimeScope scope
    )
    {
        var types = scope.ComponentRegistry.Registrations
            .SelectMany(e => e.Services)
            .Select(s => s as TypedService)
            .Where(s => s.ServiceType.IsAssignableToGenericType(typeof(IConfigureOptions<>)))
            .Select(s => s.ServiceType.GetGenericArguments()[0])
            .Where(s => typeof(ISettings).IsAssignableFrom(s))
            .ToList();

        foreach (var t in types)
        {
            var option = services.GetService(typeof(IOptions<>).MakeGenericType(new Type[] { t }));
            option.GetPropertyValue("Value");
        }
    }

启动时:

        builder.RegisterType<SettingsValidator>();

设置示例

public class AzureStorageSettings : ISettings
{
    [Required]
    public string ConnectionString { get; set; }
    [Required]
    public string Container { get; set; }
    [Required]
    public string Path { get; set; }
}

扩展名

public static class TypeExtensions
{
    public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
    {
        foreach (var it in givenType.GetInterfaces())
        {
            if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
                return true;
        }

        if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
            return true;

        Type baseType = givenType.BaseType;
        if (baseType == null) return false;

        return IsAssignableToGenericType(baseType, genericType);
    }
}

在program.cs

using (var scope = webHost.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            var logger = services.GetRequiredService<ILogger<Program>>();
            try
            {
                logger.LogInformation("Starting settings validation.");
                services.GetRequiredService<SettingsValidator>();
                logger.LogInformation("The settings have been validated.");
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "An error occurred while validating the settings.");
            }
        }

让我知道它是否也对您有用:)

相关问题