asp.net 配置从基选项类或接口派生的所有选项.NET Core

6yjfywim  于 2023-02-26  发布在  .NET
关注(0)|答案(2)|浏览(172)

我在appsettings.json文件中有包含变量的代码,因此我通过configure方法在IServiceCollection中注册了所有选项:

public static void Configure(IServiceCollection services, IConfiguration configuration, bool useHangfire = true)
        {
            services
                .Configure<AuthSettings>(configuration.GetSection(AuthSettings.SectionName))
                .Configure<CacheSettings>(configuration.GetSection(CacheSettings.SectionName))
..... and so on

例如,我想创建一个基类(抽象)或接口

public interface ISettings
    {
        public const string SectionName = "DefaultSettings";
    }
public class AuthSettings: ISettings
    {
        public const string SectionName = "AuthSettings";

        public int ConfirmCodeLength { get; set; }
        public string AllowedChars { get; set; }
        public TimeSpan ConfirmCodeExpiry { get; set; }
}

并像这样配置所有设置

foreach (var type in
                Assembly.GetAssembly(typeof(ISettings)).GetTypes()
                    .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(ISettings))))
            {
                var currentSettings = (ISettings)Activator.CreateInstance(type);
                services.ConfigureOptions(currentSettings);
            }

我已经做了同样的注册hangfire作业,但这个案例看起来有点不同。不幸的是,这个版本不工作,因为currentSetting应该implenetn IConfigureOptions,但它没有。而且我不确定这个代码从JSON中获取值。有人做了这样的事情吗?

mwngjboj

mwngjboj1#

因此,如果您有许多设置,您可以为所有设置创建基类,该基类具有定义节名称的方法

public class BaseSettings
{
    public virtual string SectionName => this.GetType().Name;
}

和许多这样的设置类

public class AuthSettings:BaseSettings
{
    public int ConfirmCodeLength { get; set; }
    public string AllowedChars { get; set; }
    public TimeSpan ConfirmCodeExpiry { get; set; }
}

然后在ServiceCollection扩展类中设置方法以查找所有继承的类并将其添加到IServiceCollection

public static IServiceCollection AddAllOptions(this IServiceCollection services, IConfiguration configuration)
    {
        foreach (var type in
            Assembly.GetAssembly(typeof(BaseSettings)).GetTypes()
                .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(BaseSettings))))
        {
            var configurationInstance = (BaseSettings)Activator.CreateInstance(type);
            if (configurationInstance != null)
            {
                var configurationSection = configuration.GetSection(configurationInstance.SectionName);
                var configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions).GetMethods()
                    .Where(x => x.Name == "Configure")
                    .Single(m => m.GetParameters().Length == 2)
                    .MakeGenericMethod(type);
                configureMethod.Invoke(null, new object[] { services, configurationSection });
            }
        }

        return services;
    }

最后,您可以在StartUp类中使用此方法

service.AddAllOptions(configuration);
wkyowqbh

wkyowqbh2#

这里的答案帮助我使我的解决方案工作起来,但是我发现了另一种方法,在.NET中使用动态类型创建类的示例,并将动态示例传递给扩展方法,以便在运行时解析参数的类型。这使用实际的Configure<T>方法而不是反射来调用该方法。

foreach (var type in Assembly.GetAssembly(typeof(ISettings)).GetTypes()
  .Where(myType => myType.IsClass && !myType.IsAbstract 
    && myType.GetInterfaces().Any(i => i == typeof(ISettings)))
{
  var currentSettings = (ISettings)Activator.CreateInstance(type);
  var currentSettingsConfigurationSection = 
                    configuration.GetSection(currentSettings.Section);
  Configure(services, type, currentSettingsConfigurationSection );
}

private static IServiceCollection Configure(IServiceCollection services, Type type, IConfigurationSection section)
{
    dynamic instance = Activator.CreateInstance(type);
    Configure(services, instance, section);
    return services;
}

private static IServiceCollection Configure<T>(IServiceCollection services, T _, IConfigurationSection section)
    where T : ISettings
{
    services.Configure<T>(section);
    return services;
}

相关问题