653
京東網上商城
.NET Core采用的全新配置係統[7]: 將配置保存在數據庫中
我們在《聊聊默認支持的各種配置源》和《深入了解三種針對文件(JSON、XML與INI)的配置源》對配置模型中默認提供的各種ConfigurationSource進行了深入詳盡的介紹,如果它們依然不能滿足項目中的配置需求,我們可以還可以通過自定義ConfigurationProvider來支持我們希望的配置來源。就配置數據的持久化方式來說,將培植存儲在數據庫中應該是一種非常常見的方式,接下來我們就是創建一個針對數據庫的ConfigurationSource,它采用最新的Entity Framework Core來完成數據庫的存取操作。篇幅所限,我們不可能對Entity Framework Core相關的編程作單獨介紹,如果讀者朋友們對此不太熟悉,可以查閱Entity Framework Core在線文檔。 [ 本文已經同步到《ASP.NET Core框架揭秘》之中]
目錄
一、在應用中使用自定義的DbConfigurationSource
二、ApplicationSetting & ApplicationSettingsContext
三、DbConfigurationSource
四、DbConfigurationProvider
五、擴展方法AddDatabase
我們將這個自定義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: }
我們將鏈接字符串作為配置定義在一個名為“”的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:
如上麵的代碼片斷所示,針對DbConfigurationSource的應用僅僅體現在我們為ConfigurationBuilder定義的擴展方法上,所以使用起來是非常方便的,那麼這個擴展方法背後有著怎樣的邏輯實現呢?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的定義,它的構造函數接受兩個參數,第一個參數類型為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派生於抽象類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: }
實例演示中用來注冊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: }
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-25 11:06:01