• 使用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

  • 相关阅读:
    C#编程的最佳工具
    Visual Studio Code搭建python开发环境
    Python打包文件夹的方法小结(zip,tar,tar.gz等)
    【转】python文件和目录操作方法大全(含实例)
    win764位系统上让32位程序能申请到4GB内存方法
    [转]bigbluebutton中文社区 / 开放API / bbb API
    [转]26款 网络会议/视频会议开源软件
    【转】用python比对数据库表数据的脚本
    plsql查询数据库-中文显示问号问题
    plsql 使用desc命令提示invalid sql statement
  • 原文地址:https://www.cnblogs.com/yanweidie/p/5659015.html
Copyright © 2020-2023  润新知