博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

小菜梦游Discuz!NT (第二篇 配置文件处理1)

Posted on 2008-04-28 22:47  a-peng  阅读(1646)  评论(5编辑  收藏  举报

小菜今天开始做梦了,梦到进入了Discuz!NT项目开发小组.-_-!
老大向我走了过来,扔过来一个配置文件DNT.config,说到小菜啊!把这个搞定吧,项目组的其他兄弟等着急用呢.记住给兄弟们提供方便的调用接口.
小菜激动了半天,终于有事情做了.马上动工,打开DNT.config配置文件.

<?xml version="1.0"?>
<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配置文件信息的描述而已.

using System;

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;
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;
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;
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你要想办法解决啊.

小菜说好,我回去好好想想........小菜梦醒了........(呵呵,,小菜想去洗澡了...请见下回做梦.把这个问题解决,由于不想把篇幅写的太长)