• 4.2 多级可换源的配置(下)


    前面已经实现了Json配置源的方式,以及在Startup中注册使用我们的配置源。下面我们进入重点,就是如何实现数据库方式的配置。数据表对应的实体类和DbContext代码如下,就不写数据表的结构了:)

     1     public class ConfigurationSectionInfo
     2     {
     3         public string AppCode { get; set; }
     4         public string SectionCode { get; set; }
     5         public string SectionName { get; set; }
     6         public string SectionString { get; set; }
     7     }
     8 
     9     public class ConfigurationContext : DbContext
    10     {
    11         public ConfigurationContext(DbContextOptions options) : base(options)
    12         {
    13         }
    14 
    15         public DbSet<ConfigurationSectionInfo> ConfigurationSections { get; set; }
    16 
    17         protected override void OnModelCreating(ModelBuilder modelBuilder)
    18         {
    19             EntityTypeBuilder<ConfigurationSectionInfo> builder = modelBuilder.Entity<ConfigurationSectionInfo>();
    20 
    21             builder.ToTable("CONFIGURATION_SECTION_INFO");
    22             builder.Property(c => c.AppCode).HasColumnName("APP_CODE").IsRequired();
    23             builder.Property(c => c.SectionCode).HasColumnName("SECTION_CODE").IsRequired();
    24             builder.Property(c => c.SectionName).HasColumnName("SECTION_NAME").IsRequired();
    25             builder.Property(c => c.SectionString).HasColumnName("SECTION_STRING").IsRequired();
    26 
    27             builder.HasKey(c => new { c.AppCode, c.SectionCode });
    28         }
    29     }

    在.net core的文档中,也写了一个数据库方式。但是该方式,是每一个键值对都存放到数据库中,例如一个缓存配置就包含MicroStrutLibrary:Caching:DefaultSlidingTime、MicroStrutLibrary:Caching:Type等多条记录,配置分散不说,写起来非常麻烦,也不如像上图中看起来那么有条理。

     

    接下来就是数据库的配置源类DatabaseConfigSource,继承我们自己的基类ConfigSource,并实现GetConfigurationRoot方法。

     1     [TypeName("Database", "数据库配置")]
     2     public class DatabaseConfigSource : ConfigSource
     3     {
     4         public DatabaseConfigSource(string parameter) : base(parameter)
     5         {
     6         }
     7 
     8         public override IConfigurationRoot GetConfigurationRoot()
     9         {
    10             AppConfigInfo config = AppConfigInfo.GetConfig();
    11 
    12             ConfigurationBuilder builder = new ConfigurationBuilder();
    13             builder.Add(new DbConfigurationSource(options => options.UseSqlServer(_Parameter), config.AppCode));
    14 
    15             return builder.Build();
    16         }
    17     }

    需要注意的是AppConfigInfo类,这个类我们用到的是AppCode属性,AppCode是指应用程序代码。因为我们的公共配置可以给多个应用使用,因此数据库方式获取配置时必须传入AppCode。在这里的意思是获取与应用程序(AppCode)相关的配置项。因为配置数据表中可能存在许多个应用的配置信息,我们这里只获取当前应用的配置信息。Parameter参数就是数据库链接串,可以在前面一节ConfigSource类的介绍中明显的看到。

    创建ConfigurationBuilder,添加IConfigurationSource的数据库实现--DbConfigurationSource,其核心是DbConfigurationProvider。DbConfigurationSource和DbConfigurationProvider的实现如下:

     1     public class DbConfigurationSource : IConfigurationSource
     2     {
     3         private readonly Action<DbContextOptionsBuilder> _optionsAction;
     4         private readonly string appCode;
     5 
     6         public DbConfigurationSource(Action<DbContextOptionsBuilder> optionsAction, string appCode)
     7         {
     8             _optionsAction = optionsAction;
     9             this.appCode = appCode;
    10         }
    11 
    12         public IConfigurationProvider Build(IConfigurationBuilder builder)
    13         {
    14             return new DbConfigurationProvider(_optionsAction, appCode);
    15         }
    16     }
    17 
    18     public class DbConfigurationProvider : ConfigurationProvider
    19     {
    20         private Action<DbContextOptionsBuilder> optionsAction;
    21         private string appCode;
    22 
    23         public DbConfigurationProvider(Action<DbContextOptionsBuilder> optionsAction, string appCode)
    24         {
    25             this.optionsAction = optionsAction;
    26             this.appCode = appCode;
    27         }
    28 
    29         public override void Load()
    30         {
    31             var builder = new DbContextOptionsBuilder<ConfigurationContext>();
    32             optionsAction(builder);
    33 
    34             using (var dbContext = new ConfigurationContext(builder.Options))
    35             {
    36                 dbContext.Database.EnsureCreated();
    37                 Data = GetConfigData(dbContext);
    38             }
    39         }
    40 
    41         private IDictionary<string, string> GetConfigData(ConfigurationContext dbContext)
    42         {
    43             List<string> configSections = new List<string>();
    44 
    45             var appConfigs = dbContext.ConfigurationSections.Where(a => a.AppCode == this.appCode);
    46             foreach (ConfigurationSectionInfo info in appConfigs)
    47             {
    48                 configSections.Add(""" + info.SectionCode + "":{" + info.SectionString + "}");
    49             }
    50 
    51             var defConfigs = dbContext.ConfigurationSections.Where(d => string.IsNullOrEmpty(d.AppCode) && appConfigs.Any(a => a.SectionCode == d.SectionCode));
    52             foreach (ConfigurationSectionInfo info in defConfigs)
    53             {
    54                 configSections.Add(""" + info.SectionCode + "":{" + info.SectionString + "}");
    55             }
    56 
    57             string configs = "{"MicroStrutLibrary":{" + string.Join(",", configSections) + "}}";
    58 
    59             return JsonConfigurationParser.Parse(configs);
    60         }
    61     }

    DbConfigurationProvider程序的大体流程是:从数据库中读取与本应用(AppCode)相关的配置节,再读取所有应用为空的配置节(缺省配置节),然后所有配置节合并成为一个总的配置字符串,最后调用解析方法生成配置的Key/Value。解析的代码:

     1     public static class JsonConfigurationParser
     2     {
     3         private static IDictionary<string, string> _data;
     4         private static Stack<string> _context;
     5         private static string _currentPath;
     6 
     7         static JsonConfigurationParser()
     8         {
     9             _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    10             _context = new Stack<string>();
    11         }
    12 
    13         public static IDictionary<string, string> Parse(string configs)
    14         {
    15             _data.Clear();
    16 
    17             var jsonConfig = JObject.Parse(configs);
    18 
    19             VisitJObject(jsonConfig);
    20 
    21             return _data;
    22         }
    23 
    24         private static void VisitJObject(JObject jObject)
    25         {
    26             foreach (var property in jObject.Properties())
    27             {
    28                 EnterContext(property.Name);
    29                 VisitProperty(property);
    30                 ExitContext();
    31             }
    32         }
    33 
    34         private static void VisitProperty(JProperty property)
    35         {
    36             VisitToken(property.Value);
    37         }
    38 
    39         private static void VisitToken(JToken token)
    40         {
    41             switch (token.Type)
    42             {
    43                 case JTokenType.Object:
    44                     VisitJObject(token.Value<JObject>());
    45                     break;
    46 
    47                 case JTokenType.Array:
    48                     VisitArray(token.Value<JArray>());
    49                     break;
    50 
    51                 case JTokenType.Integer:
    52                 case JTokenType.Float:
    53                 case JTokenType.String:
    54                 case JTokenType.Boolean:
    55                 case JTokenType.Bytes:
    56                 case JTokenType.Raw:
    57                 case JTokenType.Null:
    58                     VisitPrimitive(token);
    59                     break;
    60                 default:
    61                     MicroStrutLibraryExceptionHelper.Throw(typeof(JsonConfigurationParser).FullName, LogLevel.Error, "类型不正确!");
    62                     break;
    63             }
    64         }
    65 
    66         private static void VisitArray(JArray array)
    67         {
    68             for (int index = 0; index < array.Count; index++)
    69             {
    70                 EnterContext(index.ToString());
    71                 VisitToken(array[index]);
    72                 ExitContext();
    73             }
    74         }
    75 
    76         private static void VisitPrimitive(JToken data)
    77         {
    78             var key = _currentPath;
    79 
    80             MicroStrutLibraryExceptionHelper.TrueThrow(_data.ContainsKey(key), typeof(JsonConfigurationParser).FullName, LogLevel.Error, $"键值{key}重复");
    81 
    82             _data[key] = data.ToString();
    83         }
    84 
    85         private static void EnterContext(string context)
    86         {
    87             _context.Push(context);
    88             _currentPath = ConfigurationPath.Combine(_context.Reverse());
    89         }
    90 
    91         private static void ExitContext()
    92         {
    93             _context.Pop();
    94             _currentPath = ConfigurationPath.Combine(_context.Reverse());
    95         }
    96     }

    这个解析类的代码基本照.net core的源代码复制而来。

    面向云的.net core开发框架目录

  • 相关阅读:
    初赛Part2
    数据结构
    ES 匹配条件后分组聚合
    SpringBoot starter 懒人包介绍
    HTTP1.1中容易忽视的知识点
    ​netcore 中间件浅析
    spring boot请求参数验证
    spring boot中统一对响应做处理
    并发请求工具
    抓apk中的https包(​含破解https的SSL Pinning)
  • 原文地址:https://www.cnblogs.com/BenDan2002/p/5996994.html
Copyright © 2020-2023  润新知