使用MEF实现通用参数设置

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

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

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

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

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

时间: 2024-10-11 09:55:31

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

OpenStack Horizon 参数设置说明

OpenStack Horizon 参数设置说明 一.写在前面 这篇文章主要介绍了OpenStack(Mitaka) Horizon官方介绍的部署配置.参数设置说明,从而进行简单的翻译学习,这里主要基于目前最新的Mitakaz版本来说明,帮助自己理解Mitaka Horizon中新增的一些参数功能,提高自己在horizon组件开发方面的理解.因为时间仓促以及个人理解有限,固有错误的地方请指出,后续将会不定期更新完善,谢谢! 如果转载,请保留作者信息. 邮箱地址:[email protected]

阿里云RDS-MYSQL数据库参数设置,K哥

2016.9.2 最近被阿里云的数据库要搞疯掉了 自打阿里云抽风,非要取消myisam引擎,都换成innodb 没事总是主备切换,也没有错误日志 一问客服就是物理机波动,擦,波动是什么???????? 服务器自己跳舞了吗 看了看参数设置,很多都不知道 这两天有时间自己搜索整理了下 发给大家,有需要的看看 我的服务器应用主要是WEB网站服务 有一些不懂的地方或者不对的地方,还请大牛不吝赐教! 回复在评论中就可以了,thank you 我是K哥 auto_increment_offset表示自增长字

Oracle shared_pool_reserved_size参数设置说明

进入SQLPLUS,查询shared_pool,往往看到结果如下: SQL> show parameter shared_pool NAME                                    TYPE              VALUE------------------------------------ ----------- ------------------------------shared_pool_reserved_size           big in

Socket中的异常和参数设置

1.常见异常 1.java.net.SocketTimeoutException . 这个异 常比较常见,socket 超时.一般有 2 个地方会抛出这个,一个是 connect 的 时 候 , 这 个 超 时 参 数 由connect(SocketAddress endpoint,int timeout) 中的后者来决定,还有就是 setSoTimeout(int timeout),这个是设定读取的超时时间.它们设置成 0 均表示无限大. 2.java.net.BindException:Ad

基于V4L2 MFC 视频编解码 H264参数设置

上篇博文硬件视频编码,在默认参数设置后,编码1000帧640x480 H264文件大小为180m左右,非常大,必须设置参数 测试发现再使能EnableMBRateControl后,编码数据才开始大幅缩小: 这里先将54个控制参数全部初始化 -1,在这54个参数中,包含H264,H263和MPEG视频格式的设置,对每个视频格式仅仅选择对应他的参数即可.这里仅仅测试H264格式 所有控制参数如下: struct { int num; int id; char *name; int value; }

C3p0的参数设置

C3p0的参数设置:ComboPooledDataSource和BasicDataSource一样提供了一个用于关闭数据源的close()方法,这样我们就可以保证Spring容器关闭时数据源能够成功释放. C3P0拥有比DBCP更丰富的配置属性,通过这些属性,可以对数据源进行各种有效的控制: acquireIncrement:当连接池中的连接用完时,C3P0一次性创建新连接的数目: acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30: acqu

调用enqueue执行异步请求有一个参数设置回调。

前面的内容介绍了OkHttp,本章就来教大家okhttp的基本使用,学了这些基本东西之后,大家有其他的需求可以自行扩展.以下的所有请求都是异步请求服务器,在真实的企业开发中,基本都是异步. 首先创建一个request对象,通过request设置请求url,通过这个类还可以设置更多的请求信息.然后通过Request去构造一个Call对象. 调用enqueue执行异步请求,有一个参数设置回调.请求成功或者失败会调用Callback接口的onResponse跟onFailure方法,因为这是异步请求,

Hibernate 参数设置一览表

Hibernate 参数设置一览表 属性名 用途 hibernate.dialect 一个Hibernate Dialect类名允许Hibernate针对特定的关系数据库生成优化的SQL. 取值 full.classname.of.Dialect hibernate.show_sql 输出所有SQL语句到控制台. 有一个另外的选择是把org.hibernate.SQL这个log category设为debug. eg. true | false hibernate.format_sql 在log

Hive一些参数设置

在使用union all的时候,系统资源足够的情况下,为了加快hive处理速度,可以设置如下参数实现并发执行 set mapred.job.priority=VERY_HIGH; set hive.exec.parallel=true; 设置map reduce个数 -- 设置map capacity set mapred.job.map.capacity=2000; set mapred.job.reduce.capacity=2000; -- 设置每个reduce的大小 set hive.e