通用后台管理系统必备功能模块包含日志管理,权限管理,数据字典,参数配置等功能。参数设置主要用于设置系统运行所需的一些基础性配置项,比如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

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

如果,想给予我更多的鼓励,求打

因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是焰尾迭 。

使用MEF实现通用参数设置的更多相关文章

  1. u-boot 环境变量参数设置

    今天本来是烧写内核,结果一不小心把uboot也整不能用了,无奈之下只好重新烧个uboot,等都弄好以后,发现系统还是启动不了,原来是启动参数设置不对,于是找到了这篇文章,//是我添加的内容. 原文地址 ...

  2. Netty:option和childOption参数设置说明

    Channel配置参数 (1).通用参数 CONNECT_TIMEOUT_MILLIS :   Netty参数,连接超时毫秒数,默认值30000毫秒即30秒. MAX_MESSAGES_PER_REA ...

  3. MySQL——my.cnf参数设置说明

    以下为个人总结的MySQL配置文件参数说明,如有错误,烦请大佬们留言指正,本人将第一时间修改.2019-12-10 12:32:08 [mysqld] server- # Mysql唯一标识,一个集群 ...

  4. MySQL服务优化参数设置参考

    l 通用类: key_buffer_size 含义:用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写). 影响:对于MyISAM表的影响不是很大,MyISAM会使用系统的缓存来存储 ...

  5. Fusioncharts图表常用参数设置

    1.1 <chart>参数设置: 图表和轴的标题* caption=”String” : 图表上方的标题* subCaption=”String” : 图表上方的副标题* xAxisNam ...

  6. spring boot:用swagger3生成接口文档,支持全局通用参数(swagger 3.0.0 / spring boot 2.3.2)

    一,什么是swagger? 1,  Swagger 是一个规范和完整的文档框架, 用于生成.描述.调用和可视化 RESTful 风格的 Web 服务文档 官方网站: https://swagger.i ...

  7. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置

    前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...

  8. jqGrid的autoencode参数设置为true在客户端可能引发的编码问题

    不久前使用jqGrid+MVC做过一段时间开发. 一开始,分页参数几乎都是默认值,jqGrid的分页功能很好用. 考虑到each input is evil,我们的系统对安全性又有较高要求,所以,为了 ...

  9. Hibernate 参数设置一览表

    Hibernate 参数设置一览表 属性名 用途 hibernate.dialect 一个Hibernate Dialect类名允许Hibernate针对特定的关系数据库生成优化的SQL. 取值 fu ...

随机推荐

  1. jQueryMobile示例页面代码

    这是一个jQueryMobile示例页面 示例效果:http://hovertree.com/texiao/jquerymobile/ 可以在手机或者触屏浏览器查看效果. 以下是HTML代码: < ...

  2. arcgis server之路网服务发布

    路网服务发布首先需要建立好道路的网络集,为了保证道路网络分析的准确性,建立网络集之前,要对道路图层进行拓扑差错,确保道路的连通性.具体操作流程为:道路拓扑差错-建立几何网络集-路网服务发布. 1.道路 ...

  3. 商业智能SAAS走向中小企业

    20多年前,Gartner提出了商业智能的概念,并将其定义为“一类由数据仓库.查询报表.数据分析.数据挖掘等部分组成的,以帮助企业决策的技术及应用”.从技术上讲,商业智能是数据仓库.OLAP和数据挖掘 ...

  4. iOS RunLoop简介

    一.什么是RunLoop? RunLoop是运行循环,每个Cocoa应用程序都由一个处于阻塞状态的do/while循环驱动,当有事件发生时,就把事件分派给合适的监听器,如此反复直到循环停止.处理分派的 ...

  5. Linux0.11内核--内存管理之1.初始化

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5597705.html ] Linux内核因为使用了内存分页机制,所以相对来说好理解些.因为内存 ...

  6. ImageView学习

    package liu.roundimagedemo.view; import android.content.Context; import android.graphics.Bitmap; imp ...

  7. 总结一下Android中主题(Theme)的正确玩法

    在AndroidManifest.xml文件中有<application android:theme="@style/AppTheme">,其中的@style/AppT ...

  8. Android View的几个位置坐标关系

    1. View的边界,left, top, right, bottom(即左上右下),这些值都是相对View的父容器说的: 2. View的x, translationX, y, translatio ...

  9. Ubuntu下安装中文输入法

    搜狗输入法 for Linux 是基于Fcitx 框架(fcitx-sogoupinyin). 安装环境为Ubuntu 13.04 安装过程: 卸载Ubuntu默认的ibus输入法: sudo apt ...

  10. Windows on Device 项目实践 3 - 火焰报警器制作

    在前两篇<Windows on Device 项目实践 1 - PWM调光灯制作>和<Windows on Device 项目实践 2 - 感光灯制作>中,我们学习了如何利用I ...