三年前我分享了如何分離web.config中的配置節,因為有些項目過大,造成N多配置節存在於web.config中,缺點如下:
1:不容易管理,當你想查找一個配置節時,望着整頁的code,不知所措,為此你只有ctrl+f來解決。
2:部署時也及容易出錯,部署人員需要按照你寫的部署文檔,一個一個加,即費時又容易出錯,比如一不小心將其它節點給覆蓋了諸如此類。
3:在web.config中的配置節的修改會引起站點重啟。
4:訪問配置節不夠簡單,容易出錯。
文章之前我提到過我們為了解決此種問題的解決方案,就是將配置節從web.config文件中分離出來,將配置節存入單獨的文件中。具體的方案請參考前面的文章(如何分割web.config ),有很長一段時間沒有使用了,最近在使用上發現在多項目中復用有一定問題,即每個項目都需要編寫一段不短的代碼,下面就是我對於它的優化過程,先看下原來的工作量:
第一:Webconfig,這是框架里面的內容,這也是唯一得以復用的地方。它是一個入口,所有的配置文件引用都通過它,比如訪問酒店的配置類,WebConfig.Hotel.HotelName。代碼如下:
View Code
{
/// <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:一個配置文件路徑的屬性,它標識了此項目類所有配置文件的存放目錄,可以在程序初始化時指定此屬性。
第二:自定義的配置文件類,比如我們可以添加一個數據訪問的配置類。
View Code
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>中:
View Code
/// 屬性對應的私有變量
/// </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方式來的舒服。
