ASP.NET Core的配置(4):多样性的配置来源[下篇]

我们在上篇和中篇对配置模型中默认提供的各种ConfigurationProvider进行了深入详尽的介绍,如果它们依然不能满足项目中的配置需求,我们可以还可以通过自定义ConfigurationProvider来支持我们希望的配置来源。就配置数据的持久化方式来说,将配置存储在数据库中应该是一种非常常见的方式,接下来我们就是创建一个针对数据库的ConfigurationProvider,它采用最新的Entity Framework 7来完成数据库的存取操作。

目录 MemoryConfigurationProvider EnvironmentVariablesConfigurationProvider CommandLineConfigurationProvider JsonConfigurationProvider XmlConfiguationProvider IniConfigurationProvider 自定义ConfigurationProvider

我们将这个自定义ConfigurationProvider命名为DbConfigurationProvider。在正式对它的实现展开介绍之前,我们先来看看它在项目中的应用。我们创建一个ASP.NET Core控制台程序来演示对这个DbConfigurationProvider应用,由于我们需要使用到Entity Framework 7,并且采用SQL Server数据库,所以我们需要在project.json文件中按照如下的方式添加对“EntityFramework.MicrosoftSqlServer”这个NuGet包的依赖。

   1: {
   2:   ...
   3:   "dependencies": {
   4:     "Microsoft.Extensions.Configuration": "1.0.0-rc1-final",
   5:     "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final"
   6:   },
   7: }

我们按照如下的方式读取相关配置并将绑定为一个Profile对象。我们调用自定义的扩展方法AddDatabase创建一个DbConfigurationProvider对象并将其注册到创建的ConfigurationBuilder对象上。我们在调用扩展方法AddDatabase的时候指定了连接的目标数据库,同时设置了一些初始的配置项(如果确保配置项存在于目标数据库中,这个参数是不需要指定的),它们提供了组成一个完整的Profile对象的基础数据。

   1: string connectionString = "...";
   2: Profile profile = new ConfigurationBuilder().AddDatabase(optionsBuilder => optionsBuilder.UseSqlServer(connectionString),
   3:         new Dictionary<string, string>
   4:         {
   5:             ["Profile:Gender"]                    = "Male",
   6:             ["Profile:Age"]                       = "18",
   7:             ["Profile:ContactInfo:Email"]         = "foobar@outlook.com",
   8:             ["Profile:ContactInfo:PhoneNo"]       = "123456789"
   9:         })
  10:     .Build().Get<Profile>("Profile");

如上面的代码片断所示,针对自定义的DbConfigurationProvider的应用仅仅体现在我们为ConfigurationBuilder定义的扩展方法AddDatabase上,所以使用起来是非常方便的,那么这个扩展方法背后有着怎样的逻辑实现呢?DbConfigurationProvider采用Entity Framework 7以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: }

如下所示的是DbConfigurationProvider和扩展方法AddDatabase的定义。DbConfigurationProvider它的构造函数具有两个参数,一个参数类型为Action<DbContextOptionsBuilder>,用来对创建DbContext采用的DbContextOptions进行设置,另一个可选的参数用来指定一些需要自动初始化的配置项。在重写的Load方法中,我们利用创建的DbContexts从数据库中读取所有的配置项并作为自身的配置字典。

   1: public class DbConfigurationProvider: ConfigurationProvider
   2: {
   3:     public Func<DbContextOptions> DbContextOptionsAccessor { get; private set; }
   4:  
   5:     public DbConfigurationProvider(Action<DbContextOptionsBuilder> setup, IEnumerable<KeyValuePair<string, string>> settings = null)
   6:     {
   7:         DbContextOptionsBuilder<ApplicationSettingsContext> optionsBuilder = new DbContextOptionsBuilder<ApplicationSettingsContext>();
   8:         setup(optionsBuilder);
   9:         this.DbContextOptionsAccessor = () => optionsBuilder.Options;
  10:  
  11:         if (settings!=null && settings.Any())
  12:         {
  13:             using (ApplicationSettingsContext dbContext = new ApplicationSettingsContext(this.DbContextOptionsAccessor()))
  14:             {
  15:                 dbContext.Database.EnsureCreated();
  16:                 foreach (var item in settings)
  17:                 {
  18:                     ApplicationSetting setting = dbContext.Settings.FirstOrDefault(it => it.Key == item.Key.ToLowerInvariant());
  19:                     if (null == setting)
  20:                     {
  21:                         dbContext.Settings.Add(new ApplicationSetting(item.Key, item.Value));
  22:                     }
  23:                     else
  24:                     {
  25:                         setting.Value = item.Value;
  26:                     }
  27:                 }
  28:                 dbContext.SaveChanges();
  29:             }
  30:         }
  31:     }
  32:  
  33:     public override void Load()
  34:     {
  35:         using (ApplicationSettingsContext dbContext = new ApplicationSettingsContext(this.DbContextOptionsAccessor()))
  36:         {
  37:             var dictionary = dbContext.Settings.ToDictionary(it => it.Key, it => it.Value);
  38:             this.Data = new Dictionary<string, string>(dictionary, StringComparer.OrdinalIgnoreCase);
  39:         }
  40:     }
  41: }
  42:  
  43: public static class DbConfigurationProviderExtensions
  44: {
  45:     public static IConfigurationBuilder AddDatabase(this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> setup,
  46:         IEnumerable<KeyValuePair<string, string>> settings = null)
  47:     {
  48:         builder.Add(new DbConfigurationProvider(setup, settings));
  49:         return builder;
  50:     }
  51: }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

[WCF]缺少一行代码引发的血案

这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性。具体来...

1946
来自专栏ASP.NETCore

通过修改CoreCLR中的ClrHost实现自托管程序

上一篇我们讲了如何在windows和Linux上编译CoreClr的问题 虽然文章使用的是windows 10 (Bash)环境,但是也可以做为ubuntu环境...

1543
来自专栏博客园

浅谈Await

   我们都知道Await关键字是.Net FrameWork4.5引入的特性。await使得我们使用异步更加时特别便捷,并且还不会导致线程堵塞。我们在使用时也...

2272
来自专栏大内老A

依赖注入[6]: .NET Core DI框架[编程体验]

毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动时构建请求处理管道过程中,以及利用该管道处理每个请求过程中使用到的服务...

811
来自专栏君赏技术博客

一键替换Key Mac软件的制作教程第二篇

我们写的导入方法,需要提示用户错误信息,我们用NSAlert提示用户。新建一个类OAlertMessage。

1172
来自专栏林德熙的博客

Roslyn 使用 Target 替换占位符方式生成 nuget 打包

在项目文件的相同文件夹可以放一个 nuspec 用来告诉 VisualStudio 如何打包

1312
来自专栏从零开始学自动化测试

python接口自动化8-参数化

前言 前面一篇实现了参数的关联,那种只是记流水账的完成功能,不便于维护,也没什么可读性,接下来这篇可以把每一个动作写成一个函数,这样更方便了。 参数化的思维只...

3436
来自专栏Java编程技术

高并发编程必备基础(上)

借用Java并发编程实践中的话"编写正确的程序并不容易,而编写正常的并发程序就更难了",相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没...

1032
来自专栏恰童鞋骚年

.NET基础拾遗(5)多线程开发基础

  下面的一些基本概念可能和.NET的联系并不大,但对于掌握.NET中的多线程开发来说却十分重要。我们在开始尝试多线程开发前,应该对这些基础知识有所掌握,并且能...

1252
来自专栏一个会写诗的程序员的博客

《Kotin 极简教程》第9章 轻量级线程:协程(2)《Kotlin极简教程》正式上架:

如果需要依次调用它们, 我们只需要使用正常的顺序调用, 因为协同中的代码 (就像在常规代码中一样) 是默认的顺序执行。下面的示例通过测量执行两个挂起函数所需的总...

1282

扫码关注云+社区

领取腾讯云代金券