博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用MEF实现通用参数设置
阅读量:6325 次
发布时间:2019-06-22

本文共 10167 字,大约阅读时间需要 33 分钟。

  通用后台管理系统必备功能模块包含日志管理,权限管理,数据字典,参数配置等功能。参数设置主要用于设置系统运行所需的一些基础性配置项,比如redis缓存,mq消息队列,系统版本等信息。好的参数设置需要达到以下几点1.使用简单  2.功能强大,方便拓展 3.界面美观。本篇将带你实现通用参数设置,在阅读之前你需要了解的知识,ASP.NET MVC,Entity Framework,MEF。在线预览地址:

阅读目录

添加配置项及使用

 为了验证系统实现了这几个目标1.使用简单  2.功能强大,方便拓展 3.界面美观,这里先通过实例来演示如何添加配置项以及怎么使用该配置项。

  1.添加配置项组

  只需添加一个继承于ConfigOption抽象类的类,这里我们称继承于ConfigOption的类为配置项组

///     /// 测试配置信息    ///     [Export(typeof(ConfigOption))]    [ConfigType(Group = "TestConfig", GroupCn = "测试配置项", ImmediateUpdate = true)]    public class TestConfig : ConfigOption    {        ///         /// 是否记录执行SQL        ///         [Config(Name = "记录执行SQL", DefaultValue = false)]        public static bool IfLogSQL { get; set; }    }
  注意点:继承ConfigOption抽象类,添加ConfigTypeAttribute属性描述(Group:分组  GroupCn:分组名称,用于显示在界面)
 
  2.添加配置项
  配置项组里面的每个静态属性称为
配置项
  ///   /// 是否记录执行SQL  ///   [Config(Name = "记录执行SQL", DefaultValue = false)]  public static bool IfLogSQL { get; set; }
  注意点:ConfigAttribute属性描述中 Name:对应配置项中文说明  DefaultValue:默认值  ConfigValueType:bool类型会显示成单选radio,更多设置可参考ConfigAttribute类
  
     3.可选步骤  实现个性化业务
   
  ///   /// 保存前校验  ///   ///   /// 
bool
  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
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.Init();
 MefConfig.Init()               方法初始化组合容器
 EFInitializer.UnSafeInit()   Entity Framework数据库连接和类型初始化
 configManager.Init()         读取所有配置项 从数据库读取所有配置项值进行赋值
 
 2.关键类ConfigManager
  ///   /// 系统所有配置信息  ///   [ImportMany(typeof(ConfigOption))]  public IEnumerable
AllConfig  {    get    {      return _allConfig;    }    set    {      if (_allConfig == null)      {         _allConfig = value;      }    }  }
 通过MEF导入器读取所有配置项组,存储在静态变量 _allConfig 中
      ///         /// 初始化系统参数配置信息        ///         public void Init()        {            //所有选项值            List
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
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())反射读取所有静态属性的相关值

属性类型和前台控件映射关系

///         /// 设置默认项数值类型-根据属性类型进行转换        ///         ///         /// 属性类型        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;            }        }

 密码类型的可以自行进行指定

///         /// MQ连接密码        ///         [Config(Name = "密码", ValueType = ConfigValueType.Password)]        public static string Password { get; set; }

 提供的获取配置项的接口

///         /// 获取所有配置信息        ///         /// 
所有配置信息
public List
GetAllOption(string GroupType = "")
///         /// 获取指定项配置信息        ///         /// 分组项        /// 
所有配置信息
public OptionViewModel GetOptionByGroup(string GroupType)
///         /// 获取指定项配置信息        ///         /// 分组项        /// 
所有配置信息
public Options GetOptionByGroupAndKey(string GroupType, string key){}

保存方法实现

///         /// 保存配置信息        ///         /// 配置信息        public ApiResult
Save(OptionViewModel value) { ApiResult
result = new ApiResult
(); 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
(); 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; } ///
/// 保存时 对当前配置项进行赋值 /// ///
当前配置项 ///
配置项值 public void SetValue(ConfigOption item, List
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);  //绘制界面            }        });    }

 

总结

      该参数配置可以很简单的移植到自己系统里面,在这边博客中使用的参数配置功能就是直接移植的该系统的代码。另外使用的时候记得修改Web.config中的数据库连接字符串,本篇写到这里就完结了,在介绍一下与文章无关的内容。我在博客上添加的打赏功能,分别位于公告和文章结尾处,欢迎资助我持续写作!下篇将结合参数配置介绍消息队列RabbitMQ的使用,敬请期待!

  源代码下载地址:,svn源码地址。

 

转载地址:http://zcmaa.baihongyu.com/

你可能感兴趣的文章
paip.hibernate save 失败的解决
查看>>
使用批处理给IIS添加MIME类型
查看>>
VLC 源代码结构
查看>>
接下来将介绍C#如何设置子窗体在主窗体中居中显示,本文提供详细的操作步骤,需要的朋友可以参考下...
查看>>
[游戏学习22] MFC 井字棋 双人对战
查看>>
Qt中的qreal
查看>>
Codeforces Beta Round #95 (Div. 2) D.Subway
查看>>
ASP.NET MVC3 系列教程 – 新的Layout布局系统
查看>>
iOS开发之指定UIView的某几个角为圆角
查看>>
企业搜索引擎开发之连接器connector(二十)
查看>>
HeadFirst Jsp 09 (JSTL)
查看>>
jquery版小型婚礼(可动态添加祝福语)
查看>>
Centos5.8 安装 PHP5.5 和 memcached
查看>>
第25周六
查看>>
[转]CENTOS LINUX安装并使用NFS共享文件
查看>>
Android AES加密算法及其实现
查看>>
spring mvc --自己定义converse
查看>>
Entity Framework公共的增删改方法
查看>>
hdu1698 Just a Hook 线段树:成段替换,总区间求和
查看>>
dorado spring知识补充
查看>>