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

    要使用 npm 需要安装 node.js,因为 node.js 中会附带 npm 查看 node 的安装路径 which node 查看 npm 的安装路径 which npm npm 分为两种安装模 ...

  2. SharePoint 2013 页面访问,Url中间多一段"_layouts/15/start.aspx#"

    问题描述: 我想访问如下页面 http://Host/_layouts/15/ManageFeatures.aspx 点击以后页面地址没有错,但是中间多了一段“_layouts/15/start.as ...

  3. 隐藏tabbar的属性hidesBottomBarWhenPushed

    项目中有需求是A视图控制器push之后B视图控制器需要隐藏底部的tabbar,在pop之后A视图控制器仍然显示tabbar. 其实不需要在push操作时敲 self.hidesBottomBarWhe ...

  4. ASP.NET 4.0尚未在 Web 服务器上注册 解决方法

    使用VS2010创建web应用程序时出现如下提示ASP.NET 4.0尚未在 Web 服务器上注册.为了使网站正确运行,可能需要手动将 Web 服务器配置为使用 ASP.NET 4.0,按 F1 可了 ...

  5. Android UI ListView的使用

    一.ListView的理解  1.什么ListView?   一种用来显示多个可滑动项(Item)列表的的ViewGroup 需要使用Adapter将集合数据和每一个Item所对应的布局动态适配到Li ...

  6. js(javascript)与OC(Objective-C)交互

    实质上oc与js的通信交互就是发送消息,也即函数调用,iOS7以后官方公布JavaScriptCore framework中很方便我们对他们之间的相互调用.在以前我们只能通过UIWebView的UIW ...

  7. Java异常之自定义异常

    哎呀,妈呀,又出异常了!俗话说:"代码虐我千百遍,我待代码如初恋". 小Alan最近一直在忙着工作,已经很久没有写写东西来加深自己的理解了,今天来跟大家聊聊Java异常.Java异 ...

  8. TOP命令各个参数代表意义详解

    Top命令是Linux下常用的系统性能分析工具,能实时查看系统中各个进程资源占用情况. top - 16:24:25 up 284 days, 4:59, 1 user, load average: ...

  9. 将String转化成Stream,将Stream转换成String

    using System;using System.IO;using System.Text;namespace CSharpConvertString2Stream{     class Progr ...

  10. SpringMVC 自定义一个拦截器

    自定义一个拦截器方法,实现HandlerInterceptor方法 public class FirstInterceptor implements HandlerInterceptor{ /** * ...