總所周知,配置信息是應用程序可變化的設置信息,存放於配置文件中,開發人員可以使用配置文件來更改應用的設置。.net core提供了多種配置方式,例如json文件配置,注冊表配置、環境配置,xml文件配置等。其中大家常用的是一個json配置文件方式,即每個應用都有一個appsettings.json配置文件,存放所有的配置信息。
這裡,我還有個需要說明的地方,就是當前系統的配置過於龐大了,我建議盡量采用約定方式,就是約定優於配置。例如所有的MVC Controller都以Controller為結尾就是一種約定優於配置的例子。
但這種配置方式存在重復配置問題。例如有三個應用項目訪問某數據庫,該數據庫的數據庫連接串將在這三個應用項目的配置文件中分別存放,當數據庫的服務器地址、用戶、口令等變化時,均需要在這三個項目中修改,隨著系統的增多,極易造成漏改的情況。同樣的,在雲環境下,應用項目可以部署動態分配、擴展的計算資源中(多台虛擬服務器),修改一個配置項就需要同時修改各個服務器中的配置文件,也一樣的容易造成漏改的情況。
為了解決這個問題,我們采用了多級配置文件方式,將公用部分配置信息提取出來,存放到一個獨立的公共配置文件中,這三個項目均訪問公共配置文件以獲取數據庫連接串。公共配置文件方式可以解決目前存在的問題。然而,在實際運行過程中又發現了新的問題,就是運行過程中發現了程序異常,技術人員經常需要查看配置信息,或者需要修改配置,而客戶安全要求比較高(對於客戶技術人員的能力就不吐槽了),根本不允許直接訪問服務器,排查問題非常困難。
因此,我們最終采用數據庫方式存放配置信息。在數據庫中增加一個配置信息表,統一存放公共的配置信息,並增加相應的管理界面進行維護。各系統均從同一數據庫中獲取公共配置信息,完美的解決了重復性配置問題。
首先是公用的配置基類:
1 public abstract class ConfigInfo 2 { 3 public abstract string SectionName { get; } 4 5 public abstract void RegisterOptions(IServiceCollection services, IConfigurationRoot root); 6 }
SectionName是配置的名稱,RegisterOptions的做法是把配置信息(請參考Options模式)注冊到DI中,.net core的缺省注冊容器就是IServiceCollection。所有的配置類,例如緩存配置、數據庫配置、日志配置等都必須繼承該類。例如緩存配置實現類如下:
1 public sealed class CachingConfigInfo : ConfigInfo 2 { 3 /// <summary> 4 /// 緩存滑動窗口時間(分鐘) 5 /// </summary> 6 public int DefaultSlidingTime { get; set; } 7 8 /// <summary> 9 /// 類型 10 /// </summary> 11 public string Type { get; set; } 12 13 /// <summary> 14 /// 參數 15 /// </summary> 16 public List<CacheServer> Servers { get; set; } 17 18 public override string SectionName 19 { 20 get 21 { 22 return "MicroStrutLibrary:Caching"; 23 } 24 } 25 26 public override void RegisterOptions(IServiceCollection services, IConfigurationRoot root) 27 { 28 services.Configure<CachingConfigInfo>(root.GetSection(SectionName)); 29 } 30 }
其次,配置源基類。配置源是指公共配置(例如緩存、數據庫、日志等)存放的位置,例如數據庫或者Json文件等。在每個應用項目的appsettings.json中建立一個配置節,存放當前公共配置存放的位置。例如數據庫方式和Json文件方式的配置源在appsettings.json文件中的寫法如下:
1 "ConfigurationSource": { 2 "StorageMode": "JsonFile", 3 "Parameter": "D:\\Programs\\OSChina\\MicroStrutLibrary\\Client\\MicroStrutLibrary.Client.SampleWebApp\\applibrary.json" 4 } 5 6 ---------- 7 "ConfigurationSource": { 8 "StorageMode": "Database", 9 "Parameter": "Data Source=XXXXXX;Initial Catalog=MicroStrutLibrary;User Id=OperUser;Password=XXXX;MultipleActiveResultSets=true;Persist Security Info=true" 10 }
配置源基類的代碼如下:
1 public abstract class ConfigSource 2 { 3 private const string SourceSectionName = "MicroStrutLibrary:ConfigurationSource"; 4 private const string DefaultStorageMode = "JsonFile"; 5 private const string DefaultParameter = "appsettings.json"; 6 7 protected string _Parameter; 8 9 public static ConfigSource GetConfigSource() 10 { 11 IConfiguration configuration = new ConfigurationBuilder() 12 .SetBasePath(Directory.GetCurrentDirectory()) 13 .AddJsonFile("appsettings.json") 14 .Build(); 15 16 IConfigurationSection section = configuration.GetSection(SourceSectionName); 17 18 string storageMode = section["StorageMode"] ?? DefaultStorageMode; 19 string parameter = section["Parameter"] ?? DefaultParameter; 20 21 TypeNameHelperInfo info = TypeNameHelper.GetTypeNameHelperInfo<ConfigSource>(storageMode); 22 23 return ReflectionHelper.CreateInstance(info.Type, new object[] { parameter }) as ConfigSource; 24 } 25 26 public ConfigSource(string parameter) 27 { 28 this._Parameter = parameter; 29 } 30 31 public abstract IConfigurationRoot GetConfigurationRoot(); 32 }
靜態方法GetConfigSource,從應用的本地配置文件appsettings.json中獲取配置源設置,然後創建數據庫或者Json文件等數據源。TypeNameHelper的內容,請參照“反射工具”。
抽象方法GetConfigurationRoot需要每個具體子類實現,主要作用是從配置源中獲取所有配置信息,生成配置信息的IConfiguraionRoot對象。
下面是Json配置源的具體實現:
1 [TypeName("JsonFile", "Json配置文件")] 2 public class JsonConfigSource : ConfigSource 3 { 4 public JsonConfigSource(string parameter) : base(parameter) 5 { 6 } 7 8 public override IConfigurationRoot GetConfigurationRoot() 9 { 10 //JsonConfigurationSource source = new JsonConfigurationSource(); 11 //source.Path = _Parameter; 12 13 //ConfigurationBuilder builder = new ConfigurationBuilder(); 14 //builder.Add(source); 15 16 ConfigurationBuilder builder = new ConfigurationBuilder(); 17 builder.AddJsonFile(_Parameter); 18 19 return builder.Build(); 20 } 21 }
Json方式配置源的大體寫法:
{ "MicroStrutLibrary": { "Caching": { "DefaultSlidingTime": 20, "Type": "MicroStrutLibrary.Infrastructure.Core.Caching.DefaultCacheHandler, MicroStrutLibrary.Infrastructure.Core" }, "Database": { "ConnectionStrings": [ { "Name": "MicroStrutLibrary", "ConnectionString": "Data Source=XXXX;Initial Catalog=MicroStrutLibrary;User Id=OperUser;Password=XXXX;MultipleActiveResultSets=true;Persist Security Info=true", "ProviderName": "System.Data.SqlClient" } ], "Providers": [ { "Name": "System.Data.SqlClient", "Type": "MicroStrutLibrary.Infrastructure.Core.Data.Entity.SqlServerDbContextOptionsBuilderProvider, MicroStrutLibrary.Infrastructure.Core.Data.Entity" } ] } } }
最後,在Startup.cs的ConfigureServices中增加類似services.AddConfiguration()的語句,注冊配置源的方式。AddConfiguration是一個擴展的方法,具體實現如下:
1 public static IServiceCollection AddConfiguration(this IServiceCollection services) 2 { 3 if (services == null) 4 { 5 throw new ArgumentNullException(nameof(services)); 6 } 7 8 ConfigSource configSource = ConfigSource.GetConfigSource(); 9 10 IConfigurationRoot root = configSource.GetConfigurationRoot(); 11 12 IEnumerable<Type> list = ReflectionHelper.GetSubTypes<ConfigInfo>(); 13 foreach (Type type in list) 14 { 15 ConfigInfo configInfo = Activator.CreateInstance(type) as ConfigInfo; 16 17 configInfo.RegisterOptions(services, root); 18 } 19 20 return services; 21 }
大體流程是,獲取配置源信息,然後從配置源中獲取ConfigurationRoot,獲取所有的ConfigInfo的子類,創建並注冊到DI容器中。GetSubTypes內容,請參照“反射工具”
面向雲的.net core開發框架目錄