如何从c#Core中的DI获取可用的IOptions列表? [英] How to get the list of IOptions available from DI in c# Core?
问题描述
关注此帖子: 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屋!