在7月份中我曾经写过一篇随笔叫,叫" .NET2.0 框架中的 AbstractFactory 模式 " 。
里面主要说了在2.0框架下的数据库链接工厂中新增的几个类,而这几个类采用的就是 抽象类工厂模式
(Abstract Factory)。因为在Discuz!NT 2。0中使用了这些新的类,所以导致我们的产品dbhelper.cs
可以支持几种数据库(目前官方实现的有sqlserver ,access ,mysql)。但同时因为1.0框架下没有这些类,
所以我们采用自已简单实现其中主要的类代码来解决这个问题。这就有了今天文章的内容!
为了便于大家对照我将抽象类工厂模式结构图与项目中实现的结构图同时贴上:
首先请大家看一下AbstractFactory模式的结构图

然后请大家看一下Discuz!NT项目中的数据链接结构图

好,现在开始上路了。
这个架构中最主要的核心数据库链接所需要的(connection, Command,DataAdapter)对象就是围绕下面
m_factory的使用和赋值展开的, 请看如下代码
1 /// <summary>
2 /// DbProviderFactory实例
3 /// </summary>
4 private static IDbProviderFactory m_factory = null;
5
6 /// <summary>
7 /// DbFactory实例
8 /// </summary>
9 public static IDbProviderFactory Factory
10 {
11 get
12 {
13 if (m_factory == null)
14 {
15 m_factory = Provider.Instance();
16 }
17 return m_factory;
18 }
19 }
20
而其中的Provider.Instance();实现的代码如下
1 /// <summary>
2 /// Discuz!NT数据接口
3 /// </summary>
4 private static IDbProvider m_provider = null;
5
6 /// <summary>
7 /// IDbProvider接口
8 /// </summary>
9 public static IDbProvider Provider
10 {
11 get
12 {
13 if (m_provider == null)
14 {
15 lock(lockHelper)
16 {
17 if (m_provider == null)
18 {
19 try
20 {
21 m_provider = (IDbProvider)Activator.CreateInstance(Type.GetType(string.Format("Discuz.Data.{0}Provider, Discuz.Data.{0}", BaseConfigs.GetDbType)));
22 }
23 catch
24 {
25 throw new Exception("请检查DNT.config中Dbtype节点数据库类型是否正确,例如:SqlServer、Access、MySql,注意大小写。");
26 }
27
28 }
29 }
30
31 //m_provider = new DbProviderFinder().GetDbProvider("accesss");
32
33
34 }
35 return m_provider;
36 }
37 }
38
大家看到这里使用了反射,而BaseConfigs.GetDbType这个属性的调用在一个叫做Dnt.config的文件
中有相应设置,它标识了当前数据库是Sqlserver,Access还是MySql。
那么目前我们假设使用的是SqlServer型的数据库,那么上面的"Discuz.Data.{0}Provider, Discuz.Data.{0}"
就会变成是"Discuz.Data.SqlServerProvider, Discuz.Data.SqlServer",我们不妨在这里找一下Discuz.Data.SqlServer
这个项目中,为方便,我这里直接将Discuz.Data.SqlServer这个类代码贴在这
1 public class SqlServerProvider : IDbProvider
2 {
3 public IDbProviderFactory Instance()
4 {
5 return SqlClientFactory.Instance;
6 }
7
8 public void DeriveParameters(IDbCommand cmd)
9 {
10 if ((cmd as SqlCommand) != null)
11 {
12 SqlCommandBuilder.DeriveParameters(cmd as SqlCommand);
13 }
14 }
15
16 public IDataParameter MakeParam(string ParamName, DbType DbType, Int32 Size)
17 {
18 SqlParameter param;
19
20 if (Size > 0)
21 param = new SqlParameter(ParamName, (SqlDbType)DbType, Size);
22 else
23 param = new SqlParameter(ParamName, (SqlDbType)DbType);
24
25 return param;
26 }
27
28 

29
30 }
31
32 //SqlClientFactory工厂类,提供对SqlServerProvider中的Instance返回实例的支持
33 public class SqlClientFactory : IDbProviderFactory
34 {
35 public static readonly SqlClientFactory Instance;
36
37 static SqlClientFactory()
38 {
39 Instance = new SqlClientFactory();
40 }
41
42 private SqlClientFactory()
43 {
44 }
45
46
47 public IDbConnection CreateConnection()
48 {
49 return new SqlConnection();
50 }
51
52
53 public IDbCommand CreateCommand()
54 {
55 return new SqlCommand();
56 }
57
58 public IDbDataAdapter CreateDataAdapter()
59 {
60 return new SqlDataAdapter();
61 }
62
63 }
64
65
其中SqlClientFactory是一个在.net2.0框架下才有的类,这里因为考虑产品架构在1。0和2。0下的对应关系,这里
使用了相同的类名称。而这里类在抽象类工厂模式中的位置就是ConcreateFactory部分所需要实现的代码。
同理大家可以找到另个两个项目DLL,Discuz.Data.Access中的AccessProvider.cs和Discuz.Data.MySql中的
MySqlProvider.cs,大家会发现类似的代码实现。
现在大家应该清楚了这个数据库链接架构的核心了吧。
另外就是发表完这篇文章后,关于DISCUZ!NT系列的随笔会暂时先告一段落。主要是因为另外一系列的文章将要登场,
这也是我最想写的文章,主要是关于ICONIX(UML)的。
最后再把这个系列中的随笔进行一下分类归纳如下:
控件类:
Discuz!NT控件剖析 之 Button [原创: 附源码]
Discuz!NT控件剖析 之 TextBox [原创: 附源码]
Discuz!NT控件剖析 之 ColorPicker(颜色拾取) 和Calendar(日历) [原创: 附源码]
Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]
架构类:
Discuz!NT 缓存设计简析 [原创]
Discuz!NT 聚合功能页面程序架构(重构到Facade与Observer模式)
Discuz!NT 中的数据库链接类(抽象类工厂模式)
插件类:
Discuz!NT 邮件插件机制分析
posted on 2007-09-24 09:39
代震军 阅读(5427)
评论(28) 编辑 收藏 网摘 所属分类:
设计模式VS2005Discuz!NT
发表评论
ICONIX? 不懂
楼主为啥不讲讲DNT的模版相关的东西 我觉得那是dnt比较有趣的 轻量级的模版引擎
研究discuz代码已经很长时间了,里面的功能大部分都能看懂,
你们里面用了好多我经常没见过也没用过的系统提供的类,可我在做系统的时候只会用别人用过的类,我想问下daizhj,你们在实现功能的时候是怎么知道这些类的,或者说是怎么知道系统类库里以前没接触过的类的,是不是我的学习方法有问题,希望能得到您的帮助,不胜感激!
你的文章我每篇都看,可都只能看个大概!哎。。。。
请教: 代振军大哥....
为什么discuz没有采用MemberShip作为用户权限系统?
@cw
主要是您所说的MemberShip应该是.net2框架上自带的吧,而我们要出.net1,net2(将来还可能有.net3),所以从产品线角度讲不允许我们直接用某种框架中特有(独有)而其它框架没有的特性.做这一种决定对于总喜欢用新技术的程序员而言是很痛苦的:( ,这也可以说是产品和项目的一个区别吧!
另外就是我们本地项目中是有MemberShip这个项目的,但目前是自己实现一套还是用微软的还有待权衡.同时也感谢您对我们产品的关注:)
@赤裸小绵羊
其实从MSDN,微软官方的教程,开源项目中的代码,还有就是多看像博客园这样的站点都会获取很多的信息,必定条条大路通罗马,您说是不是这个道理.
当然买一些书(有相当深度)也是一个途径(我目前主要获取知识的主要途径).
另外就是与人多沟通,不要为不知道而惭愧,没人是生来知之的:)
同时感谢您对我们产品的关注:)
今天下载了2.0正式版源码,正在对照您的文章看,发现数据库链接类这一块变化很大,能否再重新写一篇文章呢
@胡大壮
好的,我会在适当时候将新做的修改和设计思路做一个总结发上来的,同时也希望大家能够继续关注和支持我们的产品:)
想知道你们开发discuznt的时候是按照你的iconx的模型来进行分析设计的么。
很想看到你们分析的类图或者设计思路相关的文章呢。
@暗香浮动
就我个人而言,我希望国内的开发都采用类似ICONIX这样的UML方法。但事实告诉我,太理想化会让项目和产品举步唯艰。所以我变得越来越现实。
我们目前开发的方法并不是ICONIX。这是公司规模,同事能力,产品研发进度,测试等很多因素综合决定的。
但我会从下周开始想法补充一下架构(MVC)上的文章,以便大家从全局了解这个产品,从而使大家有的放矢.
刚看了discuz!nt 2.0代码.发现继续自IDataProvider接口的类并不用完全实现此接品所定义所有的方法.可以告诉我为什么吗?实在搞不懂
@只吃豆芽猪
我们所提供的三个实现了IDataProvider接口的类(分别是Discuz.Data.SqlServer.DataProvider,Discuz.Data.Access.DataProvider,Discuz.Data.MySql.DataProvider)应该把该接口中的方法都实现了呀。
@只吃豆芽猪
呵呵,partial如果用好的话,可以写出布局优良的代码
博主的文章正是我所需要的,以前看了DZ NT源码半月不及博主本篇文章受益,希望博主能进入讲解下DZ NT 中的MYSQL实现,现在网上ASP.NET + MYSQL 的太少了,教程更是只有几篇被转了上万个网站,可参考价值太低。。。
博主如能详解下 DZ NT 中的MYSQL实现,相信对很多人(特别是像我一样关注DN NT ,同时在学习 ASP.NET+MYSQL的朋友来说是非常非常有帮助的。。。。。
@笑筑
您好,关于MYSQL,我本人也不是很熟悉,因为我的开发主要是在sqlserver上,当然我们也有开发人员来设计MYSQL这块实现.相信看到DISCUZ.DATA.MYSQL这个项目的朋友应该能从中找到这种数据库的实现语句.当然如果您觉得看着比较吃力,可以到我们的官方站点来反映,相信我们的开发人员会给您做出解答的:)
--引用--------------------------------------------------
代震军: @只吃豆芽猪
呵呵,partial如果用好的话,可以写出布局优良的代码
--------------------------------------------------------
最新版本没用到partial拉?
@路过
用了,只不过在编译在.net2.0版本下的,且编译完成后partial会被从DLL中去掉
将所的接口都写在Sunease.Data下的IDataProvider里面是不是太庞大了呢。如果一个项目分多个模块,而且多人开发的话那就会好多人动用IDataProvider,能不能将所IDataProvider里面的借口分开的呢
代震军大哥,我也是按你说的做了一次,可是到了
private static void GetProvider()
{
try
{
string typeName = string.Format("Mic.Data.{0}.DataProvider, Mic.Data.{0}", BaseConfigs.GetDbType);
Type type = Type.GetType(typeName, false, true);
_instance = (IDataProvider)Activator.CreateInstance(type);
}
catch
{
throw new Exception("请检查DNT.config中Dbtype节点数据库类型是否正确,例如:SqlServer、Access、MySql");
}
}
中的
Type type = Type.GetType(typeName, false, true);
这句,得到的类型总是空!
这是怎么回事啊?
@japhie
莫非你修改了我们的代码,我们的代码是
private static void GetProvider()
{
try
{
_instance = (IDataProvider)Activator.CreateInstance(Type.GetType(string.Format("Discuz.Data.{0}.DataProvider, Discuz.Data.{0}", BaseConfigs.GetDbType), false, true));
}
catch
{
throw new Exception("请检查DNT.config中Dbtype节点数据库类型是否正确,例如:SqlServer、Access、MySql");
}
}
而不是Mic.Data的呀
我不是改,我是自己建了一个解决方案,做一下这个工厂模式,
string typeName = string.Format("Mic.Data.{0}.DataProvider, Mic.Data.{0}", BaseConfigs.GetDbType);
Type type = Type.GetType(typeName, false, true);
_instance = (IDataProvider)Activator.CreateInstance(type);
这三句是用来测试的!
看一下错在哪里,错在了type这里,每次总是得到空值!
你能告诉我这里是怎么一回事吗?
谢谢了
最近看到单例模式与静态方法
有疑问:
静态方法,在并发访问的时候是否会生成另外一个实例?
单例模式:始终只有一个实例,如果出现资源并发访问时,怎么办?
谢谢
@elevenbus
静态方法在当前进程中会只初始化一次,而其访问的属性或变量也只是唯一实例(在当前进程中)。
单例模式可能会造成并发访问时的资源争用问题,所以要在该用(唯一)实例时认真考虑这个问题,一般是采用lock方式锁定可能出现资源争用的代码段,这块可以参见我们代码中的缓存那块,就是这个原因。