小菜今天开始做梦了,梦到进入了Discuz!NT项目开发小组.-_-!
老大向我走了过来,扔过来一个配置文件DNT.config,说到小菜啊!把这个搞定吧,项目组的其他兄弟等着急用呢.记住给兄弟们提供方便的调用接口.
小菜激动了半天,终于有事情做了.马上动工,打开DNT.config配置文件.
<BaseConfigInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DbConnectString>Data Source=(local);User ID=sqlname;Password=sqlpassword;
Initial Catalog=databasename;Pooling=true</DbConnectString>
<TablePrefix>dnt_</TablePrefix>
<ForumPath>/</ForumPath>
<FounderUid>0</FounderUid>
<DbType>SqlServer</DbType>
</BaseConfigInfo>
开始分析:
(1)这是一个XML文件
(2)DbConnectString : 数据库连接字符串
TablePrefix : 数据库表前缀
ForumPath : 论坛在站点内的位置
FounderUid : 论坛创始人编号
DbType : 数据库类型(Discuz!NT)支持多数据库Access,MySql,SqlServer等
哇!这些基本信息都是整个Discuz!NT项目中最重要的东西,项目组的兄弟们都要用到这个,我一定要把它做好.
怎么做呢?
(1)首先这是一个XML的配置文件,我必须首先将它反序列化为一个类BaseConfigInfo
(2)但是这个配置文件的路径在哪里呢?问了下老大,它说会把DNT.config放在论坛的根目录下.
(在asp.net中,我们通常会使用 ~/ 来取得根目录,很明显,你在很多地方都会看到它!)
例如:<asp:Image ID="Image1" runat="server" ImageUrl="~/Images/1.jpg" />
具体可以参考 小菜梦游Discuz!NT (第一篇 开篇有益)
小菜忍不住想动手了.
先定义一个BaseConfigInfo类-----基本配置文件描述类.也就是DNT.config配置文件信息的描述而已.
namespace Discuz.Config
{
/// <summary>
/// 基本配置文件描述类 支持序列化与反序列化
/// </summary>
[Serializable]
public class BaseConfigInfo
{
private string m_dbConnectString; //数据库连接字符串
private string m_tablePrefix; //数据库表前缀
private string m_forumPath; //论坛在站点内的位置
private int m_founderUid; //论坛创始人编号
private string m_dbType; //数据库类型
/// <summary>
/// 数据库连接字符串
/// </summary>
public string DbConnectString
{
get
{
return m_dbConnectString;
}
set
{
m_dbConnectString = value;
}
}
/// <summary>
/// 数据库表前缀
/// </summary>
public string TablePrefix
{
get
{
return m_tablePrefix;
}
set
{
m_tablePrefix = value;
}
}
/// <summary>
/// 论坛在站点内的位置
/// </summary>
public string ForumPath
{
get
{
return m_forumPath;
}
set
{
m_forumPath = value;
}
}
/// <summary>
/// 论坛创始人编号
/// </summary>
public int FounderUid
{
get
{
return m_founderUid;
}
set
{
m_founderUid = value;
}
}
/// <summary>
/// 数据库类型
/// </summary>
public string DbType
{
get
{
return m_dbType;
}
set
{
m_dbType = value;
}
}
}
}
很简单对吧!虽然我是小菜但我也会!
接下来,我需要写个BaseConfigFileManager来管理这个基本信息描述类吧
也就是通过BaseConfigFileManager将DNT.config这个配置文件反列化出来
using System.IO;
using System.Web;
using System.Xml.Serialization;
namespace Discuz.Config
{
/// <summary>
/// 基本配置文件管理类
/// </summary>
public class BaseConfigFileManager
{
private static string m_configFilePath; //基本配置文件路径
/// <summary>
/// 基本配置文件路径
/// </summary>
private static string ConfigFilePath
{
get
{
if (m_configFilePath == null)
{
HttpContext context = HttpContext.Current;
m_configFilePath = context.Server.MapPath("~/DNT.config");
}
return m_configFilePath;
}
}
/// <summary>
/// 反序列化基本配置信息描述类
/// </summary>
/// <returns></returns>
public static BaseConfigInfo DeserializeInfo()
{
BaseConfigInfo configInfo;
FileStream fs = null;
try
{
fs = new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
XmlSerializer serializer = new XmlSerializer(typeof(BaseConfigInfo));
configInfo = (BaseConfigInfo)serializer.Deserialize(fs);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (fs != null)
fs.Close();
}
return configInfo;
}
}
}
小菜严格遵守封装,不需要public的就不要public
项目组的兄弟们只需要按下面这种方式就能调用基本信息描述类了.
Discuz.Config.BaseConfigInfo configInfo = Discuz.Config.BaseConfigFileManager.DeserializerInfo();
Response.Write(configInfo.DbType);
//运行输出:SqlServer
//运行结果就是原先在DNT.config中<DbType>SqlServer</DbType>结点的值
哈哈!太兴奋的小菜马上把代码给老大过目下.本想接受老大的表扬.
可惜老大马上对代码提出了一些质疑
(1)小菜啊,你必须知道,基本配置文件对整个站点来说是独一无二的,所以你的BaseConfigFileManager不能让大家乱实例化.否则后果不堪设想.
(就像注册表设置的对象,你不希望这样的对象有多个拷贝吧?那会把把设置搞的一团乱,确保程序中使用的全局资源只有一份)
小菜是个模式白痴,一看到独一无二这两个字眼.就想到了单件模式,而且就想用上..........
(2)项目组的兄弟们反映想要一些比较简单的调用方式.比如BaseConfigFileManager.GetDbType就可以得到数据库类型.
小菜开始思考,根据第一点,是否可以使用单件模式,它是独一无二的.
小菜刚学了点模式的皮毛,所以决定先把单件模式拿来试试在说. (佛主云:对与不对,试了便知......佛主好像没说过这句话,呵)
using System.IO;
using System.Web;
using System.Xml.Serialization;
namespace Discuz.Config
{
/// <summary>
/// 基本配置文件管理类
/// </summary>
public class BaseConfigFileManager
{
private static BaseConfigFileManager m_uniqueInstance = null; //基本配置文件管理对象
private BaseConfigInfo m_configInfo = DeserializeInfo(); //基本配置信息描述对象
private static string m_configFilePath; //基本配置文件路径
private BaseConfigFileManager()
{
}
/// <summary>
/// 使用单件模式,取出唯一的基本配置文件管理对象
/// </summary>
/// <returns></returns>
private static BaseConfigFileManager GetInstance()
{
if (m_uniqueInstance == null)
{
m_uniqueInstance = new BaseConfigFileManager();
}
return m_uniqueInstance;
}
/// <summary>
/// 基本配置文件路径
/// </summary>
private static string ConfigFilePath
{
get
{
if (m_configFilePath == null)
{
HttpContext context = HttpContext.Current;
m_configFilePath = context.Server.MapPath("~/DNT.config");
}
return m_configFilePath;
}
}
/// <summary>
/// 返回基本配置信息描述对象
/// </summary>
private BaseConfigInfo GetBaseConfig
{
get
{
return m_configInfo;
}
}
/// <summary>
/// 返回数据库类型
/// </summary>
public static string GetDbType
{
get
{
return GetInstance().GetBaseConfig.DbType;
}
}
/// <summary>
/// 反序列化基本配置信息描述类
/// </summary>
/// <returns></returns>
private BaseConfigInfo DeserializeInfo()
{
BaseConfigInfo configInfo;
FileStream fs = null;
try
{
fs = new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
XmlSerializer serializer = new XmlSerializer(typeof(BaseConfigInfo));
configInfo = (BaseConfigInfo)serializer.Deserialize(fs);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (fs != null)
{
fs.Close();
}
}
return configInfo;
}
}
}
项目组的兄弟们现在只需按如下方式调用
Response.Write(Discuz.Config.BaseConfigFileManager.GetDbType);
//运行输出:SqlServer
看来我们成功了....现在来看下小菜的实现代码.
GetDbType通过调用GetInstance()判断BaseConfigFileManager m_uniqueInstance是否已经被实例化,==null实例化它,被调用的是私有构造函数.和单件模式唯一的一点点不是区别的区别,小菜把GetInstance()设为private因为项目组的其它兄弟想要方便调用的接口.不想要GetInstance()
(说明一下:这个单件BaseConfigFileManager,在多线程下可能出现问题,m_uniqueInstance可能不唯一,具体可参考Head First设计模式的单件模式一章.写的很好很强大!适合我们这些小菜们看!)
小菜为自己用上了模式兴奋不已,认为老大这下该好好的表扬了下我了.
就把自己的实现给了老大看.
老大看过后,先给了小菜一个肯定,表扬了小菜的进步
但老大接着说到了:小菜啊,用到模式的时候,我们应该考虑,如果不用模式的话,我们是否有更容易的方式实现,如果有的话,我们就不一定需要模式.static构造函数如果用在这里的话,你觉得怎么样.......回去试试吧!
(说明一下:不能对模式太过痴狂,并不一定模式都适合你)
小菜回去,想了想,类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数,之后将不再被执行. ------------------好象可以完全代替GetInstance()的作用.
小菜马上动手修改了代码.
using System.IO;
using System.Web;
using System.Xml.Serialization;
namespace Discuz.Config
{
/// <summary>
/// 基本配置文件管理类
/// </summary>
public class BaseConfigFileManager
{
private static BaseConfigInfo m_configInfo; //基本配置文件信息描述对象
private static string m_configFilePath; //基本配置文件路径
private BaseConfigFileManager()
{
}
static BaseConfigFileManager()
{
m_configInfo = DeserializeInfo();
}
/// <summary>
/// 基本配置文件路径
/// </summary>
private static string ConfigFilePath
{
get
{
if (m_configFilePath == null)
{
HttpContext context = HttpContext.Current;
m_configFilePath = context.Server.MapPath("~/DNT.config");
}
return m_configFilePath;
}
}
/// <summary>
/// 返回基本配置文件信息描述对象
/// </summary>
private static BaseConfigInfo GetBaseConfig
{
get
{
return m_configInfo;
}
}
/// <summary>
/// 返回数据库连接串
/// </summary>
public static string GetDbType
{
get
{
return GetBaseConfig.DbType;
}
}
/// <summary>
/// 反序列化基本配置信息描述类
/// </summary>
/// <returns></returns>
private static BaseConfigInfo DeserializeInfo()
{
BaseConfigInfo configInfo;
FileStream fs = null;
try
{
fs = new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
XmlSerializer serializer = new XmlSerializer(typeof(BaseConfigInfo));
configInfo = (BaseConfigInfo)serializer.Deserialize(fs);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (fs != null)
{
fs.Close();
}
}
return configInfo;
}
}
}
现在项目组的其它成员只要如下调用即可了.
Discuz.Config.BaseConfigFileManager.GetDBType
很简单不是吗.看看小菜用上了静态构造函数了,代码的简明度比用单件模式要清晰的多.....所以我们要小心不要得模式病才对.
小菜很兴奋的笑了笑.老大不错吧,我写的
不过老大测试了下代码摇了摇头,对小菜说,小菜啊,以后做东西要细心,要经过详细的测试确认无误才行.
你看,我新建了一个Default.aspx页面,在其中加入了如下测试代码
Response.Write(Discuz.Config.BaseConfigFileManager.GetDbType);
输出结果是:SqlServer
小菜马上插嘴到,这不是正确吗?
老大继续说,那你接着看.
我修改了下DNT.config的配置项<DbType>SqlServer</DbType>改为<DbType>Access</DbType>
我刷新了页面,输出结果还是:SqlServer..而且我等了很久一直刷新,还是没有改过来.
小菜说到,这个啊...是啊,我刚才用了静态构造函数使得m_configInfo在BaseConfigFileManager的生命周期中只被实例化一次.所以无论你怎么刷新还是一样.
老大说,你那个静态构造函数用的很对.提高了性能.但我刚才测试的那个bug你要想办法解决啊.
小菜说好,我回去好好想想........小菜梦醒了........(呵呵,,小菜想去洗澡了...请见下回做梦.把这个问题解决,由于不想把篇幅写的太长)