通用後台管理系統必備功能模塊包含日志管理,權限管理,數據字典,參數配置等功能。參數設置主要用於設置系統運行所需的一些基礎性配置項,比如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;
}
//調用配置項的保存前校驗事件
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);
}
}
}
前台渲染邏輯(config.js)
//初始化數據
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。
如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】按鈕。
如果,您希望更容易地發現我的新博客,不妨點擊一下綠色通道的【關注我】。
因為,我的寫作熱情也離不開您的肯定支持。
感謝您的閱讀,如果您對我的博客所講述的內容有興趣,請繼續關注我的後續博客,我是焰尾迭 。