网站配置之最佳实践

  开发中,不论是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中最好。

posted @ 2017-06-19 15:00  TakeTry  阅读(254)  评论(0编辑  收藏  举报