如何分离web.config改进版本
三年前我分享了如何分离web.config中的配置节,因为有些项目过大,造成N多配置节存在于web.config中,缺点如下:
1:不容易管理,当你想查找一个配置节时,望着整页的code,不知所措,为此你只有ctrl+f来解决。
2:部署时也及容易出错,部署人员需要按照你写的部署文档,一个一个加,即费时又容易出错,比如一不小心将其它节点给覆盖了诸如此类。
3:在web.config中的配置节的修改会引起站点重启。
4:访问配置节不够简单,容易出错。
文章之前我提到过我们为了解决此种问题的解决方案,就是将配置节从web.config文件中分离出来,将配置节存入单独的文件中。具体的方案请参考前面的文章(如何分割web.config ),有很长一段时间没有使用了,最近在使用上发现在多项目中复用有一定问题,即每个项目都需要编写一段不短的代码,下面就是我对于它的优化过程,先看下原来的工作量:
第一:Webconfig,这是框架里面的内容,这也是唯一得以复用的地方。它是一个入口,所有的配置文件引用都通过它,比如访问酒店的配置类,WebConfig.Hotel.HotelName。代码如下:
{
/// <summary>
/// 启动配置类
/// </summary>
public static void OnStart(string configFilePath, FileUpdate fileUpdate)
{
#region 实现配置文件独立
//第一次启动需要执行委托方法更新配置类
fileUpdate(configFilePath);
//启动文件监视
Log4netFileWatchHelper.StartWatching(fileUpdate, configFilePath);
#endregion
}
/// <summary>
/// Config/Web 文件夹的磁盘路径
/// </summary>
public static string ConfigFilePathRoot
{
get;
set;
}
}
1:包含一个重要方法OnStart,可以对配置文件进行初始化,其实这个初始化也可以省略掉,因为完全可以将初始化配置类变成延迟加载模式。
2:一个配置文件路径的属性,它标识了此项目类所有配置文件的存放目录,可以在程序初始化时指定此属性。
第二:自定义的配置文件类,比如我们可以添加一个数据访问的配置类。
public class DataAccessConfig
{
#region 需要序列化的配置文件属性
/// <summary>
/// 数据库信息列表
/// </summary>
public List<DataBase> DataBaseList { get; set; }
#endregion
#region 相对于Config文件夹的子文件路径,不需要序列化.
/// <summary>
/// 相对于Config文件夹的子文件路径,不需要序列化.
/// </summary>
[NonSerialized()]
private static string m_SubFilePath = @"DataAccessConfig.config";
/// <summary>
/// 子文件路径(排除config文件夹路径后的部分)
/// </summary>
public static string SubFilePath
{
get { return m_SubFilePath; }
set { m_SubFilePath = value; }
}
#endregion
public static DataAccessConfig CreateInstance()
{
FileUpdate fileUpdate = new FileUpdate(WebConfig.DataAccessConfigOnUpdate);
string configFilePath = WebConfig.ConfigFilePathRoot + DataAccessConfig.SubFilePath;
if (!File.Exists(configFilePath))
{
return null;
}
DataAccessConfig config = SerializationHelper.Load(typeof(DataAccessConfig), configFilePath) as DataAccessConfig;
//启动文件监视
Log4netFileWatchHelper.StartWatching(fileUpdate, configFilePath);
return config;
}
}
public partial class WebConfig
{
#region 第二步:为DataAccessConfig类添加入口
/// <summary>
/// 属性对应的私有变量
/// </summary>
private static DataAccessConfig m_DataAccessConfig = null;
/// <summary>
/// 属性访问器.通过WebConfig.SimpleFileDemoConfig可以访问此类.
/// </summary>
public static DataAccessConfig DataAccessConfig
{
get
{
if (m_DataAccessConfig == null)
Interlocked.CompareExchange<DataAccessConfig>(ref m_DataAccessConfig,
DataAccessConfig.CreateInstance(), null);
return m_DataAccessConfig;
}
}
#endregion
#region 第三步:为DataAccessConfig类添加更新函数
/// <summary>
/// 更新函数
/// </summary>
/// <param name="status"></param>
public static void DataAccessConfigOnUpdate(object status)
{
lock (WebConfig.DataAccessConfig)
{
try
{
m_DataAccessConfig = DataAccessConfig.CreateInstance();
}
catch (Exception ex)
{
throw ex;
}
}
}
#endregion
}
这段代码就是我们要实现的部分,不能复用,总觉的需要改进一下,问题分析:
1:由于需要采用静态调用方式,为此要想通过WebConfig的静态属性调用,就需要在WebConfig类中增加自定义配置类的属性,比如我们增加一个Hotel相关的配置类,而要想WebConfig.Hotel,就需要增加一个静态属性方式实现。这里可以稍微修改下,即最后在配置自定义配置类时只编写自定义配置类,而不用去编写WebConfig类。
2:自定义配置类中的CreateInstance,也要考虑复用。
原则就是自定义的配置类不关心如何读取配置文件,只关心自己的配置属性即可。
改善后的版本:
1:针对WebConfig.自定义类方式。这里我修改的方法也不是最好的,用起来没有修改前的顺畅,但代码确实精简了。思路就是在WebConfig类中生成一个泛型配置类。
这里新增了WebConfig的泛型版本,而将WebConfig提取成基类,WebConfig类中包含一个属性,即配置文件所在文件夹路径。
/// Config/Web 文件夹的磁盘路径
/// </summary>
public static string ConfigFilePathRoot
{
get;
set;
}
WebConfig<T>主要目的是为了生成T类型的配置类,这里将原本在自定义配置类中的代码提取到WebConfig<T>中:
/// 属性对应的私有变量
/// </summary>
private static T m_DataAccessConfig ;
/// <summary>
/// 属性访问器.通过WebConfig.SimpleFileDemoConfig可以访问此类.
/// </summary>
public static T DataAccessConfig
{
get
{
if (m_DataAccessConfig == null)
Interlocked.CompareExchange<T>(ref m_DataAccessConfig,
CreateInstance(), null );
return m_DataAccessConfig;
}
}
/// <summary>
/// 更新函数
/// </summary>
/// <param name="status"></param>
public static void DataAccessConfigOnUpdate(object status)
{
lock (WebConfig<T>.DataAccessConfig)
{
try
{
m_DataAccessConfig = CreateInstance();
}
catch (Exception ex)
{
throw ex;
}
}
}
2:将自定义配置类生成实例的方法也进行封装。
{
SubFilePath = typeof(T).Name+".config";
string configFilePath = WebConfig<T>.ConfigFilePathRoot + SubFilePath;
if (!File.Exists(configFilePath))
{
return null;
}
T config = SerializationHelper.Load(typeof(T), configFilePath) as T;
//启动文件监视
FileWatch(configFilePath);
return config;
}
有了上面的提取封装,下面就是改版后的自定义类了,到目前为止,我们编写一个自定义配置类的成本已经非常低了:
public class MyConfig : WebConfig<DataAccessConfig>
{
#region 需要序列化的配置文件属性
public string ExpenseTemplateFile { get; set; }
public bool Switch { get; set; }
#endregion
}
最后我们添加一个配置文件,名称和自定义配置类保持一致。
<MyConfig>
<ExpenseTemplateFile>ssssss3</ExpenseTemplateFile>
<Switch>false</Switch>
</MyConfig>
客户端调用方式:
虽然本次改版并不完美,但在多个项目中利用时还是起到了很大的作用,编写简单,调用方便,唯一不满意的就是需要这样写:WebConfig<MyConfig>,没有以前的WebConfig.MyConfig方式来的舒服。