如何从c#Core中的DI获取可用的IOptions列表? [英] How to get the list of IOptions available from DI in c# Core?

查看:189
本文介绍了如何从c#Core中的DI获取可用的IOptions列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

I m now available to validate the Settings when a service need it. What I would like to do is to validate directly when the server starts in program.cs.

我不确定该怎么做?有没有一种方法可以获取注入到DI中的服务列表,然后验证是否可以从IOption分配类型并进行注册?

I m not sure how to do it? Is there a way to get the list of services injected in DI then verify if the type is assignable from IOption and register it?

这是我如何向DI添加设置:

Here is how I add to DI the Settings:

    //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;
            }
        }
    }

这是我当前的启动器:

Here is my current launcher:

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();
    }
}

如果您有任何建议,请告诉我。

Let me know if you have any suggestions in comment.

感谢您的帮助。

编辑1:
感谢史蒂文(Steven)的帮助,并为您提供了答案,它帮助我继续找到了答案,但是事情仍然不见了。

EDIT 1: Thanks Steven for your help, with your answer, it helped me to continue to find an answer, but things are still missing.

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

now, all my settings inherit from ISettings, like:

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就像:

I update Program.cs like:

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();

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

so now I need to instantiate each option in options and get the Value. I m still working on it. let me know if you have any suggestion or the solution :)

推荐答案

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

Following suggestions from Steven, here is my solution: My settings validator service

    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

in 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.");
            }
        }

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

Let me know if it works for you too :)

这篇关于如何从c#Core中的DI获取可用的IOptions列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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