·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> [Asp.net 5] Options-配置文件之后的配置

[Asp.net 5] Options-配置文件之后的配置

作者:佚名      ASP.NET网站开发编辑:admin      更新时间:2022-07-23

[asp.net 5] Options-配置文件之后的配置

今天要讲的是OptionsModel解决方案,整个解决方案中也只有Microsoft.Framework.OptionsModel一个工程。按照表面文字OptionsModel应该翻译成选项模型,但是这个词没表现它实际的含义,我觉得称呼它为配置选项好些,不过为了原滋原味,我们还是用英文的:Configuration和OptionsModel表示它们。

什么是OptionsModel

在之前的配置文件一节([Asp.net 5] Configuration-新一代的配置文件)我们介绍过配置文件最后生成的是IConfiguration对象,但是IConfiguration可能包含很多信息混杂在一起。比如设置日志的等级Level、连接数据库的字符串connectionstring等。我们有时候不希望直接使用IConfiguration对象(比如对象中的配置项会变动),而是抽取一部分有用的信息以及一些其他值构成的具体的实体对象,那么这个从配置文件之后抽取的配置,就可以叫做OptionsModel。

简而言之:IConfiguration是配置文件的抽象;OptionsModel配置文件之后的配置,是直接对于系统的配置项。

OptionsModel的特点与实现

这类对象的特点很明显一般都是只包含一些属性,并无具体的内部逻辑;但是这种配置可能不是仅有一个:比如有数据库的connectionModel,而日志可能有LogOption。而且让这些OptionsModel实现统一的接口也不是现实的,也没有实际意义。那么如何实现Configuration到OptionsModel的转换呢?

答案很简单:Binder(神奇的Binder)。

        [Fact]        public void CanReadComplexPRoperties()        {            var dic = new Dictionary<string, string>            {                {"Integer", "-2"},                {"Boolean", "TRUe"},                {"Nested:Integer", "11"}            };            var builder = new ConfigurationBuilder(new MemoryConfigurationSource(dic));            var config = builder.Build();            var options = ConfigurationBinder.Bind<ComplexOptions>(config);            Assert.True(options.Boolean);            Assert.Equal(-2, options.Integer);            Assert.Equal(11, options.Nested.Integer);        }

如何DI?

很多工程使用了DependencyInjection(依赖注入),而OptionsModel是比较基础的配置,几乎可以肯定内部用到OptionsModel的类会被注入,并且使用类型注册的方式注入。那么DependencyInjection内部肯定会递归到OptionsModel类,所以OptionsModel类也必须要进行注入,那么如何实现?

答曰:使用实例(Instance)直接注入到该类类型。[services.AddInstance(serviceType, type.Assembly.CreateInstance(type));]

如果我对于多个OptionsModel注册,但是我可以通过命名方式注入,还可以进行排序,那又该如何实现?

答曰:Microsoft.Framework.OptionsModel。

Microsoft.Framework.OptionsModel

类文件分类

在Microsoft.Framework.OptionsModel中,使用泛型的方式进行注入,之后以泛型的方式获取注入;但是注入的类和获取的泛型类确是不完全一致的。

  • 注入的类和接口:IConfigureOptions<in TOptions>、ConfigureOptions<TOptions>、ConfigureFromConfigurationOptions<TOptions>
  • 获取注入的类和接口:IOptions<out TOptions>、OptionsManager<TOptions>
  • 注册的扩展类:OptionsServiceCollectionExtensions
  • 其他:OptionsConstants

注入类和接口

IConfigureOptions<in TOptions>、ConfigureOptions<TOptions>、ConfigureFromConfigurationOptions<TOptions>这几个类和接口的关系为:

    public interface IConfigureOptions<in TOptions>    {        int Order { get; }        void Configure(TOptions options, string name = "");    }
IConfigureOptions
    public class ConfigureOptions<TOptions> : IConfigureOptions<TOptions>    {        public ConfigureOptions([NotNull]Action<TOptions> action)        {            Action = action;        }        public Action<TOptions> Action { get; private set; }        public string Name { get; set; } = "";        public virtual int Order { get; set; } = OptionsConstants.DefaultOrder;        public virtual void Configure([NotNull]TOptions options, string name = "")        {            // Always invoke the action if no Name was specified, otherwise only if it was the requested name            if (string.IsNullOrEmpty(Name) || string.Equals(name, Name, StringComparison.OrdinalIgnoreCase))            {                Action.Invoke(options);            }        }    }
ConfigureOptions
    public class ConfigureFromConfigurationOptions<TOptions> : ConfigureOptions<TOptions>    {        public ConfigureFromConfigurationOptions([NotNull] IConfiguration config)            : base(options => ConfigurationBinder.Bind(options, config))        {        }    }
ConfigureFromConfigurationOptions

注入类和接口的注入

在OptionsServiceCollectionExtensions中提供了三种注册方式(实际是七个方法和重载):

  • 直接注册实现IConfigureOptions<in TOptions>类型的数据。(假设实现类的类型为OptionsDev,则实际注册[services.AddTransient(IConfigureOptions<T>,OptionsDev)])
  • 提供Action<TOptions>进行注册。(创建实例instanceA=new ConfigureOptions<TOptions>(setupAction),之后注册[services.AddInstance(IConfigureOptions<T>,configureInstance)])
  • 提供IConfiguration进行注册。(创建实例instanceA=new ConfigureFromConfigurationOptions<TOptions>(setupAction),之后注册[services.AddInstance(IConfigureOptions<T>, configureInstance)])
    public static class OptionsServiceCollectionExtensions    {        public static IServiceCollection AddOptions([NotNull]this IServiceCollection services)        {            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));            return services;        }        private static bool IsAction(Type type)        {            return (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Action<>));        }        private static IEnumerable<Type> FindIConfigureOptions(Type type)        {            var serviceTypes = type.GetTypeInfo().ImplementedInterfaces                .Where(t => t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(IConfigureOptions<>));            if (!serviceTypes.Any())            {                string error = "TODO: No IConfigureOptions<> found.";                if (IsAction(type))                {                    error += " did you mean Configure(Action<T>)";                }                throw new InvalidOperationException(error);            }            return serviceTypes;        }        public static IServiceCollection ConfigureOptions([NotNull]this IServiceCollection services, Type configureType)        {            var serviceTypes = FindIConfigureOptions(configureType);            foreach (var serviceType in serviceTypes)            {                services.AddTransient(serviceType, configureType);            }            return services;        }        public static IServiceCollection ConfigureOptions<TSetup>([NotNull]this IServiceCollection services)        {            return services.ConfigureOptions(typeof(TSetup));        }        public static IServiceCollection ConfigureOptions([NotNull]this IServiceCollection services, [NotNull]object configureInstance)        {            var serviceTypes = FindIC