通用後台管理系統必備功能模塊包含日志管理,權限管理,數據字典,參數配置等功能。參數設置主要用於設置系統運行所需的一些基礎性配置項,比如redis緩存,mq消息隊列,系統版本等信息。好的參數設置需要達到以下幾點1.使用簡單 2.功能強大,方便拓展 3.界面美觀。本篇將帶你實現通用參數設置,在閱讀之前你需要了解的知識,ASP.NET MVC,Entity Framework,MEF。在線預覽地址:http://config.myscloud.cn
閱讀目錄
為了驗證系統實現了這幾個目標1.使用簡單 2.功能強大,方便拓展 3.界面美觀,這裡先通過實例來演示如何添加配置項以及怎麼使用該配置項。
1.添加配置項組
只需添加一個繼承於ConfigOption抽象類的類,這裡我們稱繼承於ConfigOption的類為配置項組。
/// <summary> /// 測試配置信息 /// </summary> [Export(typeof(ConfigOption))] [ConfigType(Group = "TestConfig", GroupCn = "測試配置項", ImmediateUpdate = true)] public class TestConfig : ConfigOption { /// <summary> /// 是否記錄執行SQL /// </summary> [Config(Name = "記錄執行SQL", DefaultValue = false)] public static bool IfLogSQL { get; set; } }注意點:繼承ConfigOption抽象類,添加ConfigTypeAttribute屬性描述(Group:分組 GroupCn:分組名稱,用於顯示在界面) 2.添加配置項 配置項組裡面的每個靜態屬性稱為配置項
/// <summary> /// 是否記錄執行SQL /// </summary> [Config(Name = "記錄執行SQL", DefaultValue = false)] public static bool IfLogSQL { get; set; }注意點:ConfigAttribute屬性描述中 Name:對應配置項中文說明 DefaultValue:默認值 ConfigValueType:bool類型會顯示成單選radio,更多設置可參考ConfigAttribute類 3.可選步驟 實現個性化業務
/// <summary> /// 保存前校驗 /// </summary> /// <param name="value"></param> /// <returns>bool</returns> public override bool BeforeSave(OptionViewModel value) { foreach(var item in value.ListOptions) { switch (item.Key) { case "IfLogSQL": if (string.IsNullOrEmpty(item.Value)) { return false; } break; default: break; } } return base.BeforeSave(value); }
public override void AfterSave(List<Options> ListOptions) { foreach (Options item in ListOptions) { switch (item.Key) { case "IfLogSQL": //開啟或者關閉EF日志記錄 bool curValue = Convert.ToBoolean(item.Value); if (curValue != TestConfig.IfLogSQL) { //EfLogConfig.ManagerEFLog(curValue); } break; default: break; } } }
使用BeforeSave和AfterSave方法可以實現個性化業務
4.參數使用
public ActionResult Index() { ViewBag.Title = SystemConfig.SystemTitle; return View(); }
由於定義的是靜態屬性,所以可以直接使用
小結:只需通過添加配置項類,無需修改其它東西,所需的保存邏輯和界面都已經完成。這裡留個疑問,我是如何知道前台界面渲染的時候該用radio,text,password中哪種控件的呢? 回到頂部通用配置管理達到以下目標
1.使用簡單
通過添加配置項類,無需額外操作即可完成工作
2.功能強大,方便拓展
界面等其它工作都已經由框架完成,對於個性化的配置比如需要實現校驗,或者額外工作,可以通過beforesave,aftersave進行拓展
3.界面美觀
基於bootstrap實現,整體效果還是挺不錯的
系統的類圖
系統主流程
回到頂部1.初始化(Global.asax.cs)
//1.MEF初始化 MefConfig.Init(); //2.EF初始化 EFInitializer.UnSafeInit(); //3.初始化系統參數配置 ConfigManager configManager =MefConfig.TryResolve<ConfigManager>(); configManager.Init();MefConfig.Init() 方法初始化組合容器 EFInitializer.UnSafeInit() Entity Framework數據庫連接和類型初始化 configManager.Init() 讀取所有配置項 從數據庫讀取所有配置項值進行賦值 2.關鍵類ConfigManager
/// <summary> /// 系統所有配置信息 /// </summary> [ImportMany(typeof(ConfigOption))] public IEnumerable<ConfigOption> AllConfig { get { return _allConfig; } set { if (_allConfig == null) { _allConfig = value; } } }通過MEF導入器讀取所有配置項組,存儲在靜態變量 _allConfig 中
/// <summary> /// 初始化系統參數配置信息 /// </summary> public void Init() { //所有選項值 List<Options> listOption = ConfigService.GetAllOptions(); ConfigDescription desc = null; //代碼現有配置項 foreach (ConfigOption item in AllConfig) { //反射讀取配置項ConfigTypeAttribute ConfigAttribute 信息 desc = ConfigDescriptionCache.GetTypeDiscription(item.GetType()); //設置當前配置項的GroupType desc.GroupTypePropertyInfo.SetValue(item, Convert.ChangeType(desc.Group, desc.GroupTypePropertyInfo.PropertyType), null); //每項值信息 List<Options> itemOptions = listOption.Where(e => e.OptionType.Equals(desc.Group, StringComparison.OrdinalIgnoreCase)).ToList(); Options op = null; ConfigAttribute ca = null; foreach (PropertyInfo prop in desc.StaticPropertyInfo) { op = itemOptions.FirstOrDefault(e1 => e1.Key.Equals(prop.Name, StringComparison.OrdinalIgnoreCase)); ca = desc.MemberDict[prop.Name]; if (op == null) { //設置默認值 prop.SetValue(null, Convert.ChangeType(ca.DefaultValue, prop.PropertyType), null); } else { prop.SetValue(null, Convert.ChangeType(op.Value, prop.PropertyType), null); } } } }
ConfigService.GetAllOptions()從數據庫中讀取所有選項值,通過ConfigDescriptionCache.GetTypeDiscription(item.GetType())反射讀取所有靜態屬性的相關值
屬性類型和前台控件映射關系
/// <summary> /// 設置默認項數值類型-根據屬性類型進行轉換 /// </summary> /// <param name="configAttr"></param> /// <param name="propertyType">屬性類型</param> private static void SetConfigValueType(ConfigAttribute configAttr,Type propertyType) { switch (propertyType.ToString()) { case "System.String": configAttr.ValueType = ConfigValueType.String; //文本框 break; case "System.Boolean": configAttr.ValueType = ConfigValueType.Bool; //對應前台 radio break; case "System.Int32": case "System.Double": configAttr.ValueType = ConfigValueType.Number; //對應數值輸入框 break; default: configAttr.ValueType = ConfigValueType.String; break; } }
密碼類型的可以自行進行指定
/// <summary> /// MQ連接密碼 /// </summary> [Config(Name = "密碼", ValueType = ConfigValueType.Password)] public static string Password { get; set; }
提供的獲取配置項的接口
/// <summary> /// 獲取所有配置信息 /// </summary> /// <returns>所有配置信息</returns> public List<OptionViewModel> GetAllOption(string GroupType = "")
/// <summary> /// 獲取指定項配置信息 /// </summary> /// <param name="GroupType">分組項</param> /// <returns>所有配置信息</returns> public OptionViewModel GetOptionByGroup(string GroupType)
/// <summary> /// 獲取指定項配置信息 /// </summary> /// <param name="GroupType">分組項</param> /// <returns>所有配置信息</returns> public Options GetOptionByGroupAndKey(string GroupType, string key){}
保存方法實現
/// <summary> /// 保存配置信息 /// </summary> /// <param name="value">配置信息</param> public ApiResult<string> Save(OptionViewModel value) { ApiResult<string> result = new ApiResult<string>(); result.HasError = true; string GroupType = value.Group.GroupType; if (value.Group == null || string.IsNullOrEmpty(GroupType) || value.ListOptions == null) { result.Message = "保存參數配置時發生參數空異常"; return result; } //調用保存前處理事件 ConfigOption curConfigOption = AllConfig.FirstOrDefault(e => e.GroupType.Equals(GroupType, StringComparison.OrdinalIgnoreCase)); if (curConfigOption == null) { //如果沒有找到匹配項 result.Message = string.Format("當前保存配置信息{0}不對應後台的任務配置類", GroupType); return result; }前台渲染邏輯(config.js)
//調用配置項的保存前校驗事件 if (!curConfigOption.BeforeSave(value)) { result.Message = "當前配置項不允許保存"; return result; } //保存數據 try { using (CommonDbContext cdb = new CommonDbContext()) { var dbSet = cdb.Set<Options>(); var delObjs=dbSet.Where(e => e.OptionType == GroupType).ToList(); //刪除原有數據 foreach (var item in delObjs) { cdb.Entry(item).State = EntityState.Deleted; } //保存數據 foreach (var item in value.ListOptions) { item.OptionId = Guid.NewGuid().ToString("N"); } dbSet.AddRange(value.ListOptions); cdb.SaveChanges(); } } catch (Exception ex) { result.Message = ex.Message; return result; } //對當前配置項進行賦值 SetValue(curConfigOption, value.ListOptions); result.HasError = false; return result; } /// <summary> /// 保存時 對當前配置項進行賦值 /// </summary> /// <param name="item">當前配置項</param> /// <param name="ListOptions">配置項值</param> public void SetValue(ConfigOption item, List<Options> ListOptions) { //調用保存後處理事件 item.AfterSave(ListOptions); var desc = ConfigDescriptionCache.GetTypeDiscription(item.GetType()); Options option = null; foreach (PropertyInfo prop in desc.StaticPropertyInfo) { option = ListOptions.First(e => e.Key.Equals(prop.Name, StringComparison.OrdinalIgnoreCase)); if (option == null) { //不存在該配置項,則清空當前值 prop.SetValue(null, Convert.ChangeType(null, prop.PropertyType), null); } else { prop.SetValue(null, Convert.ChangeType(option.Value, prop.PropertyType), null); } } }
//初始化數據 initData = function () { $.ajax({ type: "get", url: "/Config/GetAllOption", //調用後台獲取所有配置項接口 dataType: "json", beforeSend: function () { //加載等待層 index = layer.load(0); }, complete: function () { layer.close(index); }, success: function (data) { BaseData = data; drawConfig(BaseData); //繪制界面 } }); }
回到頂部
該參數配置可以很簡單的移植到自己系統裡面,在TaskManagerV2.0這邊博客中使用的參數配置功能就是直接移植的該系統的代碼。另外使用的時候記得修改Web.config中的數據庫連接字符串,本篇寫到這裡就完結了,在介紹一下與文章無關的內容。我在博客上添加的打賞功能,分別位於公告和文章結尾處,歡迎資助我持續寫作!下篇將結合參數配置介紹消息隊列RabbitMQ的使用,敬請期待!
源代碼下載地址:http://files.cnblogs.com/files/yanweidie/CommonParamConfig.rar,svn源碼地址http://code.taobao.org/svn/commonparamconfig。
如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】按鈕。
如果,您希望更容易地發現我的新博客,不妨點擊一下綠色通道的【關注我】。
因為,我的寫作熱情也離不開您的肯定支持。
感謝您的閱讀,如果您對我的博客所講述的內容有興趣,請繼續關注我的後續博客,我是焰尾迭 。