我们在上篇和中篇对配置模型中默认提供的各种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: } |