zoukankan      html  css  js  c++  java
  • 网站配置之最佳实践

      开发中,不论是CS还是BS,为了应对不同情况的变化,程序中 我们或多或少都会通过配置的方式来应对不同的情况,下面分享一种数据库存储配置,缓存中读取配置项的方式。

           数据库脚本:  

    CREATE TABLE Base_Config
    (
        UUId UniqueIdentifier Primary Key,    --主键
        Title nvarchar (200) ,    --标题
        SectionName nvarchar (200) ,    --节点项名称
        KeyName nvarchar (200) Not null,    --键名
        Value nvarchar (200) ,    --
        ConfigType int DEFAULT (0),    --配置类型,键值配置为 0,二级配置为 1
        Remark nvarchar (4000),    --对该配置的说明性文字
        Added datetime ,    --添加时间
        Modified datetime    --更改时间
    )

       配置类代码(注:SharpDB 是我自己封装的一个数据库操作类库,后面我会分享出来):

    using SharpDB;
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Data;
    
    /// <summary>
    /// 配置项操作类
    /// </summary>
    public class ConfigHelper
    {
        /// <summary>
        /// 防并发同步锁
        /// </summary>
        static object locker = new object();
        static Dictionary<string, string> SystemConfig = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        static Dictionary<string, NameValueCollection> SectionSystemConfig = new Dictionary<string, NameValueCollection>(StringComparer.OrdinalIgnoreCase);
    
        static ConfigHelper()
        {
            InitSystemValues();
            InitSectionValues();
        }
    
        /// <summary>
        /// 初始化所有普通键值对数据
        /// </summary>
        /// <returns></returns>
        private static void InitSystemValues()
        {
            string sel_Sql = "Select KeyName,Value From Base_Config where  ConfigType = 0 ";
            DB db = new DB(AccessType.MsSql);
            DataTable data = db.QueryTable(sel_Sql);
            if (data != null && data.Rows.Count > 0)
            {
                foreach (DataRow dr in data.Rows)
                {
                    SystemConfig.Add(dr["KeyName"].ToString(), dr["Value"].ToString());
                }
            }
        }
    
        /// <summary>
        /// 初始化所有二级键值对数据
        /// </summary>
        private static void InitSectionValues()
        {
            string sel_Sql = "Select SectionName,KeyName,Value From Base_Config where  ConfigType = 1 ";
            DB db = new DB(AccessType.MsSql);
            DataTable data = db.QueryTable(sel_Sql);
            if (data != null && data.Rows.Count > 0)
            {
                foreach (DataRow dr in data.Rows)
                {
                    NameValueCollection nvc = null;
                    if (SectionSystemConfig.TryGetValue(dr["SectionName"].ToString(), out nvc))
                    {
                        nvc.Add(dr["KeyName"].ToString(), dr["Value"].ToString());
                        SectionSystemConfig.Remove(dr["SectionName"].ToString());
                        SectionSystemConfig.Add(dr["SectionName"].ToString(), nvc);
                    }
                    else
                    {
                        nvc = new NameValueCollection();
                        nvc.Add(dr["KeyName"].ToString(), dr["Value"].ToString());
                        SectionSystemConfig.Add(dr["SectionName"].ToString(), nvc);
                    }
                }
            }
        }
    
    
        /// <summary>
        /// 二级键值配置项
        /// 获取数据节点 配置值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sectionName">节点名称</param>
        /// <param name="keyName">键名称</param>
        /// <param name="defaultValue">默认值,未找到配置则返回默认值</param>
        /// <returns></returns>
        public static T GetValue<T>(string sectionName, string keyName, T defaultValue)
        {
            if (sectionName == null)
                throw new ArgumentNullException("sectionName");
    
            if (keyName == null)
                throw new ArgumentNullException("keyName");
    
    
            var nvc = SectionSystemConfig[sectionName];
            if (nvc == null || nvc.Count <= 0)
            {
                return defaultValue;
            }
    
            string retVal = nvc[keyName];
    
            if (retVal == null)
            {
                return defaultValue;
            }
            return retVal.ChangeType<T>();
        }
    
        /// <summary>
        /// 普通键值对项
        /// 获取数据节点 配置值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keyName">键名称</param>
        /// <param name="defaultValue">默认值,未找到配置则返回默认值</param>
        /// <returns></returns>
        public static T GetValue<T>(string keyName, T defaultValue)
        {
            if (keyName == null)
                throw new ArgumentNullException("keyName");
    
            string retVal = SystemConfig[keyName];
    
            if (retVal == null)
            {
                return defaultValue;
            }
            return retVal.ChangeType<T>();
        }
    
        /// <summary>
        /// 二级键值配置项
        /// 获取配置键值数据
        /// </summary>
        /// <param name="sectionName">节点名称</param>
        /// <returns></returns>
        public static NameValueCollection GetSectionValues(string sectionName)
        {
            return SectionSystemConfig[sectionName];
        }
    
    
    
        /// <summary>
        /// 获取所有普通键名称
        /// </summary>
        /// <returns></returns>
        public static List<string> GetKeyNames()
        {
            List<string> lstKeyName = new List<string>();
            foreach (var item in SystemConfig)
            {
                lstKeyName.Add(item.Key);
            }
            return lstKeyName;
        }
    
        /// <summary>
        /// 获取所有节点名称
        /// </summary>
        /// <returns></returns>
        public static List<string> GetSectionNames()
        {
            List<string> lstSectionNames = new List<string>();
            foreach (var item in SectionSystemConfig)
            {
                lstSectionNames.Add(item.Key);
            }
            return lstSectionNames;
        }
    
        /// <summary>
        /// 设置更改键值对数据(存在则更新,不存在则插入)
        /// </summary>
        /// <param name="sectionName">节点名称</param>
        /// <param name="keyName">键名称</param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int SetValue(string sectionName, string keyName, object value)
        {
            lock (locker) //保证数据一致性
            {
                if (sectionName == null)
                    throw new ArgumentNullException("sectionName");
    
                if (keyName == null)
                    throw new ArgumentNullException("keyName");
    
                sectionName = sectionName.Trim();
                keyName = keyName.Trim();
                value = (value == null ? string.Empty : value.ToString().Trim());
    
                bool isUpdate = false;
                int ConfigType = -1;
                if (string.IsNullOrWhiteSpace(sectionName))
                {
                    ConfigType = 0;
                    isUpdate = (SystemConfig[keyName] == null ? false : true);
                }
                else
                {
                    ConfigType = 1;
                    isUpdate = SectionSystemConfig.ContainsKey(sectionName);
                }
    
    
                DB db = new DB(AccessType.MsSql);
                string exec_Sql = string.Empty;
                int res = -1;
                if (isUpdate)
                {
                    exec_Sql = "update Base_Config set value='{2}',Modified=getdate() where sectionName='{0}' and keyName ='{1}' ";
                    exec_Sql = string.Format(exec_Sql, sectionName, keyName, value);
                    res = db.ExecuteSql(exec_Sql);
    
                    //同步更新配置数据
                    if (ConfigType == 0)
                    {
                        SystemConfig[keyName] = value.ToString();
                    }
                    else
                    {
                        var nvc = SectionSystemConfig[sectionName];
                        nvc[keyName] = value.ToString();
                        SectionSystemConfig.Remove(sectionName);
                        SectionSystemConfig.Add(sectionName, nvc);
                    }
    
                }
                else
                {
                    exec_Sql = "insert into Base_Config (uuid,SectionName,keyName,value,ConfigType,added) values (newid(),'{0}','{1}','{2}',{3},getdate())";
                    exec_Sql = string.Format(exec_Sql, sectionName, keyName, value, ConfigType);
                    res = db.ExecuteSql(exec_Sql);
    
                    //同步更新配置数据
                    if (ConfigType == 0)
                    {
                        SystemConfig.Add(keyName, value.ToString());
                    }
                    else
                    {
                        NameValueCollection nvc = new NameValueCollection();
                        nvc.Add(keyName, value.ToString());
                        SectionSystemConfig.Add(sectionName, nvc);
                    }
                }
    
                return res;
            }
        }
    
        /// <summary>
        /// 设置更改键值对数据(存在则更新,不存在则插入)
        /// </summary>
        /// <param name="keyName">键名称</param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static int SetValue(string keyName, object value)
        {
            return SetValue(string.Empty, keyName, value);
        }
    
        /// <summary>
        /// 删除节点键项
        /// </summary>
        /// <param name="sectionName"></param>
        /// <param name="keyName"></param>
        /// <returns></returns>
        public static int DeleteKey(string keyName, string sectionName = "")
        {
            lock (locker)
            {
                int res = -1;
                if (sectionName == null)
                    throw new ArgumentNullException("sectionName");
    
                if (keyName == null)
                    throw new ArgumentNullException("keyName");
    
    
                DB db = new DB(AccessType.MsSql);
                string del_Sql = "delete from Base_Config where sectionName ='{0}' and keyName='{1}' ";
                del_Sql = string.Format(del_Sql, sectionName, keyName);
                res = db.ExecuteSql(del_Sql);
    
                //同步更新配置数据
                if (string.IsNullOrWhiteSpace(sectionName))
                {
                    SystemConfig.Remove(keyName);
                }
                else
                {
                    var nvc = SectionSystemConfig[sectionName];
                    nvc.Remove(keyName);
                    SectionSystemConfig[sectionName] = nvc;
                }
    
                return res;
            }
        }
    
        /// <summary>
        /// 删除节点项
        /// </summary>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        public static int DeleteSection(string sectionName)
        {
            lock (locker)
            {
                if (sectionName == null)
                    throw new ArgumentNullException("sectionName");
    
                int res = -1;
                DB db = new DB(AccessType.MsSql);
                string del_Sql = "delete from Base_Config where ConfigType=1 and sectionName ='{0}' ";
                del_Sql = string.Format(del_Sql, sectionName);
                res = db.ExecuteSql(del_Sql);
    
                //同步更新配置数据
                SectionSystemConfig.Remove(sectionName);
    
                return res;
            }
        }
    }

      说明:

        1. 该 SharpDB.DB 我另一个开源分享的 数据库操作类(后期公布分享),真正要使用时,替换成自己的数据库操作类即可。

                  2. 说是最佳实线,是因为 去除了损耗性能的 每次 读取配置 都查询的情况,而使用静态字典去存储配置的读取。

        3. 使用时,如果程序代码部署在多个网站服务器,如果对配置进行了 新增,修改,删除 操作时,需要重新启动网站或回收应用程序池,配置才会生效(读取到最新的数据)。

                  4. 说是最佳实践,不是因为这个可以满足所有情况的 应用程序配置,而是指 这样小巧好用 性能还算Ok,这是我是一个抛砖引玉的做法;最佳实践的话,如果项目中集成有Redis,那么久可以把 该配置操作 集成到Redis中最好。

  • 相关阅读:
    Ehcache缓存配置
    spring3使用task:annotation-driven开始定时
    Constructor >> @Autowired >> @PostConstruct
    面试转载
    阿里面试:MYSQL的引擎区别
    Redis的主从复制的原理介绍
    微服务的调用链
    java的零拷贝机制
    存储过程与触发器面试
    ABA问题
  • 原文地址:https://www.cnblogs.com/lztkdr/p/ConfigHelper.html
Copyright © 2011-2022 走看看