.NET Core采用的全新配置系统[7]: 将配置保存在数据库中
我們在《聊聊默認(rèn)支持的各種配置源》和《深入了解三種針對文件(JSON、XML與INI)的配置源》對配置模型中默認(rèn)提供的各種ConfigurationSource進(jìn)行了深入詳盡的介紹,如果它們依然不能滿足項(xiàng)目中的配置需求,我們可以還可以通過自定義ConfigurationProvider來支持我們希望的配置來源。就配置數(shù)據(jù)的持久化方式來說,將培植存儲在數(shù)據(jù)庫中應(yīng)該是一種非常常見的方式,接下來我們就是創(chuàng)建一個針對數(shù)據(jù)庫的ConfigurationSource,它采用最新的Entity Framework Core來完成數(shù)據(jù)庫的存取操作。篇幅所限,我們不可能對Entity Framework Core相關(guān)的編程作單獨(dú)介紹,如果讀者朋友們對此不太熟悉,可以查閱Entity Framework Core在線文檔。 [ 本文已經(jīng)同步到《ASP.NET Core框架揭秘》之中]
目錄
一、在應(yīng)用中使用自定義的DbConfigurationSource
二、ApplicationSetting & ApplicationSettingsContext
三、DbConfigurationSource
四、DbConfigurationProvider
五、擴(kuò)展方法AddDatabase
一、在應(yīng)用中使用自定義的DbConfigurationSource
我們將這個自定義ConfigurationSource命名為DbConfigurationSource。在正式對它的實(shí)現(xiàn)展開介紹之前,我們先來看看它在項(xiàng)目中的應(yīng)用。我們創(chuàng)建一個控制臺程序來演示對這個DbConfigurationSource應(yīng)用。我們將配置保存在SQL Server數(shù)據(jù)庫中的某個數(shù)據(jù)表中,并采用Entity Framework Core來讀取配置,所以我們需要添加針對“ Microsoft.EntityFrameworkCore”和“Microsoft.EntityFrameworkCore.SqlServer”這兩個NuGet包的依賴。除此之外,我們的實(shí)例程序會采用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”配置項(xiàng)使這個文件可以在編譯的時候可以自動拷貝到輸出目錄下。
1: { 2: "connectionStrings": { 3: "defaultDb": "Server = ... ; Database=...; Uid = ...; Pwd = ..." 4: } 5: }我們編寫了如下的程序來演示針對自定義ConfigurationSource(DbConfigurationSource)的應(yīng)用。我們首先創(chuàng)建了一個ConfigurationBuilder對象,并注冊了一個指向“connectionString.json”文件的JsonConfigurationSource。針對DbConfigurationSource的注冊體現(xiàn)在擴(kuò)展方法AddDatabase上,這個方法接收兩個參數(shù),它們分別代表鏈接字符串的名稱和初始的配置數(shù)據(jù)。前者正式“connectionString.json”設(shè)置的連接字符串名稱“defaultDb”,后者是一個字典對象,它提供的原始配置正好可以構(gòu)成一個Profile對象。在利用ConfigurationBuilder創(chuàng)建出相應(yīng)的Configuration對象之后,我們采用標(biāo)準(zhǔn)的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的應(yīng)用僅僅體現(xiàn)在我們?yōu)镃onfigurationBuilder定義的擴(kuò)展方法AddDatabase上,所以使用起來是非常方便的,那么這個擴(kuò)展方法背后有著怎樣的邏輯實(shí)現(xiàn)呢?DbConfigurationSource采用Entity Framework Core以Code First的方式進(jìn)行數(shù)據(jù)操作,如下所示的ApplicationSetting是表示基本配置項(xiàng)的POCO類型,我們將配置項(xiàng)的Key以小寫的方式存儲。另一個ApplicationSettingsContext是對應(yīng)的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的定義,它的構(gòu)造函數(shù)接受兩個參數(shù),第一個參數(shù)類型為Action<DbContextOptionsBuilder>的委托對象,我們用它來對創(chuàng)建DbContext采用的DbContextOptions進(jìn)行設(shè)置,另一個可選的參數(shù)用來指定一些需要自動初始化的配置項(xiàng)。DbConfigurationSource在重寫的Build方法中利用這兩個對象創(chuàng)建一個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方法中,它會根據(jù)提供的Action<DbContextOptionsBuilder>創(chuàng)建ApplicationSettingsContext對象,并利用后者從數(shù)據(jù)庫中讀取配置數(shù)據(jù)并轉(zhuǎn)換成字典對象并賦值給代表配置字典的Data屬性。如果數(shù)據(jù)表中沒有數(shù)據(jù),該方法還會利用這個DbContext對象將提供的初始化配置添加到數(shù)據(jù)庫中。
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: }五、擴(kuò)展方法AddDatabase
實(shí)例演示中用來注冊DbConfigurationSource的擴(kuò)展方法AddDatabase具有如下的定義。該方法首先調(diào)用ConfigurationBuilder的Build方法創(chuàng)建出一個Configuration對象,并調(diào)用后者的擴(kuò)展方法GetConnectionString根據(jù)指定的連接字符串名稱得到完整的連接字符串。接下來我們調(diào)用構(gòu)造函數(shù)創(chuàng)建一個DbConfigurationSource對象并注冊到ConfigurationBuilder上。創(chuàng)建DbConfigurationSource對象指定的Action<DbContextOptionsBuilder>會完成針對連接字符串的設(shè)置。
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: }作者:蔣金楠
微信公眾賬號:大內(nèi)老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術(shù)資料,可以掃描左邊二維碼(或者長按識別二維碼)關(guān)注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 原文鏈接
總結(jié)
以上是生活随笔為你收集整理的.NET Core采用的全新配置系统[7]: 将配置保存在数据库中的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基友是什么意思
- 下一篇: 与朱元思书翻译和原文一句一翻译