我们在《聊聊默认支持的各种配置源》和《深入了解三种针对文件(JSON、XML与INI)的配置源》对配置模型中默认提供的各种ConfigurationSource进行了深入详尽的介绍,如果它们依然不能满足项目中的配置需求,我们可以还可以通过自定义ConfigurationProvider来支持我们希望的配置来源。就配置数据的持久化方式来说,将培植存储在数据库中应该是一种非常常见的方式,接下来我们就是创建一个针对数据库的ConfigurationSource,它采用最新的Entity Framework Core来完成数据库的存取操作。篇幅所限,我们不可能对Entity Framework Core相关的编程作单独介绍,如果读者朋友们对此不太熟悉,可以查阅Entity Framework Core在线文档。 [ 本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、在应用中使用自定义的DbConfigurationSource
二、ApplicationSetting & ApplicationSettingsContext
三、DbConfigurationSource
四、DbConfigurationProvider
五、扩展方法AddDatabase

一、在应用中使用自定义的DbConfigurationSource

我们将这个自定义ConfigurationSource命名为DbConfigurationSource。在正式对它的实现展开介绍之前,我们先来看看它在项目中的应用。我们创建一个控制台程序来演示对这个DbConfigurationSource应用。我们将配置保存在SQL Server数据库中的某个数据表中,并采用Entity Framework Core来读取配置,所以我们需要添加针对“ Microsoft.EntityFrameworkCore”和“Microsoft.EntityFrameworkCore.SqlServer”这两个NuGet包的依赖。除此之外,我们的实例程序会采用Options模式将读取的配置绑定为了一个Options对象,所以我们添加了针对NuGet包“Microsoft.Extensions.DependencyInjection”和“Microsoft.Extensions.Options.ConfigurationExtensions”的依赖。

   1: {

   2:   ...

   3:   "buildOptions": {

   4:     ...

   5:     "copyToOutput": "connectionString.json"

   6:   },

   7:  

   8:   "dependencies": {

   9:     ...

  10:     "Microsoft.Extensions.Options.ConfigurationExtensions"    : "1.0.0",

  11:     "Microsoft.Extensions.DependencyInjection"                : "1.0.0",

  12:     "Microsoft.Extensions.Configuration.Json"                 : "1.0.0",

  13:     "Microsoft.EntityFrameworkCore.SqlServer"                 : "1.0.0",

  14:     "Microsoft.EntityFrameworkCore"                           : "1.0.0"

  15:   } 

  16: }

我们将链接字符串作为配置定义在一个名为“connectionString.json”的JSON文件中,所以我们添加了针对NuGet包“Microsoft.Extensions.Configuration.Json”的依赖。链接字符串采用如下的形式定义在这个JSON文件中的定义,我们修改了“buildOptions/copyToOutput”配置项使这个文件可以在编译的时候可以自动拷贝到输出目录下。

   1: {

   2:   "connectionStrings": {

   3:     "defaultDb":  "Server = ... ; Database=...; Uid = ...; Pwd = ..."

   4:   }

   5: }

我们编写了如下的程序来演示针对自定义ConfigurationSource(DbConfigurationSource)的应用。我们首先创建了一个ConfigurationBuilder对象,并注册了一个指向“connectionString.json”文件的JsonConfigurationSource。针对DbConfigurationSource的注册体现在扩展方法AddDatabase上,这个方法接收两个参数,它们分别代表链接字符串的名称和初始的配置数据。前者正式“connectionString.json”设置的连接字符串名称“defaultDb”,后者是一个字典对象,它提供的原始配置正好可以构成一个Profile对象。在利用ConfigurationBuilder创建出相应的Configuration对象之后,我们采用标准的Options编程模式读取配置将将其绑定为一个Profile对象。

   1: var initialSettings = new Dictionary<string, string>

   2: {

   3:     ["Gender"]             = "Male",

   4:     ["Age"]                 = "18",

   5:     ["ContactInfo:EmailAddress"]     = "foobar@outlook.com",

   6:     ["ContactInfo:PhoneNo"]         = "123456789"

   7: };

   8:  

   9: IConfiguration config = new ConfigurationBuilder()

  10:     .AddJsonFile("connectionString.json")

  11:     .AddDatabase("DefaultDb", initialSettings)

  12:     .Build();

  13:  

  14: Profile profile = new ServiceCollection()

  15:     .AddOptions()

  16:     .Configure<Profile>(config)

  17:     .BuildServiceProvider()

  18:     .GetService<IOptions<Profile>>()

  19:     .Value;

  20:  

  21: Debug.Assert(profile.Gender == Gender.Male);

  22: Debug.Assert(profile.Age == 18);

  23: Debug.Assert(profile.ContactInfo.EmailAddress == "foobar@outlook.com");

  24: Debug.Assert(profile.ContactInfo.PhoneNo == "123456789");

  25:  

  26:  

  27: public class Profile

  28: {

  29:     public Gender         Gender { get; set; }

  30:     public int            Age { get; set; }

  31:     public ContactInfo    ContactInfo { get; set; }

  32: }

  33:  

  34: public class ContactInfo

  35: {

  36:     public string EmailAddress { get; set; }

  37:     public string PhoneNo { get; set; }

  38: }

  39:  

  40: public enum Gender

  41: {

  42:     Male,

  43:     Female

  44: }

  45:  

二、ApplicationSetting & ApplicationSettingsContext

如上面的代码片断所示,针对DbConfigurationSource的应用仅仅体现在我们为ConfigurationBuilder定义的扩展方法AddDatabase上,所以使用起来是非常方便的,那么这个扩展方法背后有着怎样的逻辑实现呢?DbConfigurationSource采用Entity Framework Core以Code First的方式进行数据操作,如下所示的ApplicationSetting是表示基本配置项的POCO类型,我们将配置项的Key以小写的方式存储。另一个ApplicationSettingsContext是对应的DbContext类型。

   1: [Table("ApplicationSettings")]

   2: public class ApplicationSetting

   3: {

   4:     private string key;

   5:  

   6:     [Key]

   7:     public string Key

   8:     {

   9:         get { return key; }

  10:         set { key = value.ToLowerInvariant(); }

  11:     }

  12:  

  13:     [Required]

  14:     [MaxLength(512)]

  15:     public string Value { get; set; }

  16:  

  17:     public ApplicationSetting()

  18:     {}

  19:  

  20:     public ApplicationSetting(string key, string value)

  21:     {

  22:         this.Key     = key;

  23:         this.Value   = value;

  24:     }

  25: }

  26:  

  27: public class ApplicationSettingsContext : DbContext

  28: {

  29:     public ApplicationSettingsContext(DbContextOptions options) : base(options)

  30:     {}

  31:  

  32:     public DbSet<ApplicationSetting> Settings { get; set; }

  33: }

三、DbConfigurationSource

如下所示的是DbConfigurationSource的定义,它的构造函数接受两个参数,第一个参数类型为Action<DbContextOptionsBuilder>的委托对象,我们用它来对创建DbContext采用的DbContextOptions进行设置,另一个可选的参数用来指定一些需要自动初始化的配置项。DbConfigurationSource在重写的Build方法中利用这两个对象创建一个DbConfigurationProvider对象。

   1: public class DbConfigurationSource : IConfigurationSource

   2: {

   3:     private Action<DbContextOptionsBuilder> _setup;

   4:     private IDictionary<string, string> _initialSettings;

   5:  

   6:     public DbConfigurationSource(Action<DbContextOptionsBuilder> setup, IDictionary<string, string> initialSettings = null)

   7:     {

   8:         _setup               = setup;

   9:         _initialSettings     = initialSettings;

  10:     }

  11:     public IConfigurationProvider Build(IConfigurationBuilder builder)

  12:     {

  13:         return new DbConfigurationProvider(_setup, _initialSettings);

  14:     }

  15: }

四、DbConfigurationProvider

DbConfigurationProvider派生于抽象类ConfigurationProvider。在重写的Load方法中,它会根据提供的Action<DbContextOptionsBuilder>创建ApplicationSettingsContext对象,并利用后者从数据库中读取配置数据并转换成字典对象并赋值给代表配置字典的Data属性。如果数据表中没有数据,该方法还会利用这个DbContext对象将提供的初始化配置添加到数据库中。

   1: public class DbConfigurationProvider: ConfigurationProvider

   2: {

   3:     private IDictionary<string, string>         _initialSettings;

   4:     private Action<DbContextOptionsBuilder>     _setup;

   5:  

   6:     public DbConfigurationProvider(Action<DbContextOptionsBuilder> setup, IDictionary<string, string> initialSettings)

   7:     {

   8:         _setup               = setup;

   9:         _initialSettings     = initialSettings?? new Dictionary<string, string>() ;

  10:     }

  11:  

  12:     public override void Load()

  13:     {

  14:         DbContextOptionsBuilder<ApplicationSettingsContext> builder = new DbContextOptionsBuilder<ApplicationSettingsContext>();

  15:         _setup(builder);

  16:         using (ApplicationSettingsContext dbContext = new ApplicationSettingsContext(builder.Options))

  17:         {

  18:             dbContext.Database.EnsureCreated();

  19:             this.Data = dbContext.Settings.Any()? dbContext.Settings.ToDictionary(it => it.Key, it => it.Value, StringComparer.OrdinalIgnoreCase): this.Initialize(dbContext);

  20:         }

  21:     }

  22:  

  23:     private IDictionary<string, string> Initialize(ApplicationSettingsContext dbContext)

  24:     {

  25:         foreach (var item in _initialSettings)

  26:         {

  27:             dbContext.Settings.Add(new ApplicationSetting(item.Key, item.Value));

  28:         }

  29:         return _initialSettings.ToDictionary(it => it.Key, it => it.Value, StringComparer.OrdinalIgnoreCase);

  30:     }

  31: }

五、扩展方法AddDatabase

实例演示中用来注册DbConfigurationSource的扩展方法AddDatabase具有如下的定义。该方法首先调用ConfigurationBuilder的Build方法创建出一个Configuration对象,并调用后者的扩展方法GetConnectionString根据指定的连接字符串名称得到完整的连接字符串。接下来我们调用构造函数创建一个DbConfigurationSource对象并注册到ConfigurationBuilder上。创建DbConfigurationSource对象指定的Action<DbContextOptionsBuilder>会完成针对连接字符串的设置。

   1: public static class DbConfigurationExtensions

   2: {

   3:     public static IConfigurationBuilder AddDatabase(this IConfigurationBuilder builder, string connectionStringName, IDictionary<string, string> initialSettings = null)

   4:     {

   5:         string connectionString = builder.Build().GetConnectionString(connectionStringName);

   6:         DbConfigurationSource source = new DbConfigurationSource(optionsBuilder => optionsBuilder.UseSqlServer(connectionString), initialSettings);

   7:         builder.Add(source);

   8:         return builder;

   9:     }

  10: }

.NET Core采用的全新配置系统[7]: 将配置保存在数据库中的更多相关文章

  1. .NET Core采用的全新配置系统[10]: 配置的同步机制是如何实现的?

    配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.要了解配置同步机制的实现原理,先得从认识一个 ...

  2. .NET Core采用的全新配置系统[2]: 配置模型设计详解

    在<.NET Core采用的全新配置系统[1]: 读取配置数据>中,我们通过实例的方式演示了几种典型的配置读取方式,其主要目的在于使读者朋友们从编程的角度对.NET Core的这个全新的配 ...

  3. .NET Core采用的全新配置系统[3]: “Options模式”下的配置是如何绑定为Options对象

    配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置.值得推荐的做法就是采用<.NET Core采用的全新配置系统[1]: 读取 ...

  4. .NET Core采用的全新配置系统[1]: 读取配置数据

    提到“配置”二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义在这两个文 ...

  5. .NET Core采用的全新配置系统[5]: 聊聊默认支持的各种配置源[内存变量,环境变量和命令行参数]

    较之传统通过App.config和Web.config这两个XML文件承载的配置系统,.NET Core采用的这个全新的配置模型的最大一个优势就是针对多种不同配置源的支持.我们可以将内存变量.命令行参 ...

  6. .NET Core采用的全新配置系统[9]: 为什么针对XML的支持不够好?如何改进?

    物理文件是我们最常用到的原始配置的载体,最佳的配置文件格式主要由三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

  7. .NET Core采用的全新配置系统[8]: 如何实现配置与源文件的同步

    配置的同步涉及到两个方面:第一,对原始的配置文件实施监控并在其发生变化之后从新加载配置:第二,配置重新加载之后及时通知应用程序进而使后者能够使用最新的配置.接下来我们利用一个简单的.NET Core控 ...

  8. 重新整理 .net core 实践篇—————配置系统之强类型配置[十]

    前言 前文中我们去获取value值的时候,都是通过configurationRoot 来获取的,如configurationRoot["key"],这种形式. 这种形式有一个不好的 ...

  9. 虚拟机console基础环境配置——系统镜像站点配置

    1. 概述2. 部署HTTP服务器2.1 YUM安装httpd2.2 配置httpd2.3 启动httpdf2.4 测试httpd3. 部署FTP服务器3.1 YUM安装vsftpd3.2 配置vsf ...

随机推荐

  1. Java数据库连接技术——JDBC

    大家好,今天我们学习了Java如何连接数据库.之前学过.net语言的数据库操作,感觉就是一通百通,大同小异. JDBC是Java数据库连接技术的简称,提供连接各种常用数据库的能力. JDBC API ...

  2. PHP 5.6 编译安装选项说明

    `configure' configures this package to adapt to many kinds of systems. Usage: ./configure [OPTION].. ...

  3. 【开源】.Net 动态脚本引擎NScript

    开源地址: https://git.oschina.net/chejiangyi/NScript 开源QQ群: .net 开源基础服务  238543768 .Net 动态脚本引擎 NScript   ...

  4. scp报错 -bash: scp: command not found

    环境:RHEL6.5 使用scp命令报错: [root@oradb23 media]# scp /etc/hosts oradb24:/etc/ -bash: scp: command not fou ...

  5. 【绝对干货】仿微信QQ设置图形头像裁剪,让你的App从此炫起来~

    最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue ...

  6. 在.NET Core之前,实现.Net跨平台之Mono+CentOS+Jexus初体验

    准备工作 本篇文章采用Mono+CentOS+Jexus的方式实现部署.Net的Web应用程序(实战,上线项目). 不懂Mono的请移步张善友大神的:国内 Mono 相关文章汇总 不懂Jexus为何物 ...

  7. trigger事件模拟

    事件模拟trigger 在操作DOM元素中,大多数事件都是用户必须操作才会触发事件,但有时,需要模拟用户的操作,来达到效果. 需求:页面初始化时触发搜索事件并获取input控件值,并打印输出(效果图如 ...

  8. Angular (SPA) WebPack模块化打包、按需加载解决方案完整实现

    文艺小说-?2F,言情小说-?3F,武侠小说-?9F long long ago time-1-1:A 使用工具,long long A ago time-1-2:A 使用分类工具,long long ...

  9. 为什么很多SaaS企业级产品都熬不过第一年

    因工作缘由,笔者与周边数位SaaS企业级应用的创始人.运营负责人有过深入接触,发现一个有趣的现象:刚起步时,蓝图远志.规划清晰,但是一路下来,却异常艰难,有些甚至熬不过第一年,就关门歇业. 2015年 ...

  10. 使用Nginx反向代理 让IIS和Tomcat等多个站点一起飞

    使用Nginx 让IIS和Tomcat等多个站点一起飞 前言: 养成一个好习惯,解决一个什么问题之后就记下来,毕竟“好记性不如烂笔头”. 这样也能帮助更多的人 不是吗? 最近闲着没事儿瞎搞,自己在写一 ...