在很多应用系统里面,虽然一般采用一种数据库运行,但是由于各种情况的需要,可能业务系统会部署在不同类型的数据库上,如果开发的系统能够很方便支持多种数据库的切换,那可以为我们减少很多烦恼,同时提高系统的适应性和强壮型。还有一种情况,由于业务数据库的不断膨胀或者方便数据库的切割隔离,有时候也会把不同的业务数据库进行分拆,如权限提供数据库,客户关系管理数据库,工作流程数据库,企业营运数据库等等,因此在一个系统里面,同时使用2个或者以上的数据库的情况也是有的。针对这两种情况,本文介绍在我的Winform开发框架(也使用我的其他框架),如何具体处理这两个问题的。
在我的各种开发框架里面,底层都是采用同一种数据库的访问方式,就是采用了Enterprise Library的数据库访问模块,这个是微软开源的企业应用模块,里面各种应用模块,都堪称是开发的最佳实践。当然利用里面的数据库访问模块,是很广泛的,这个数据库访问模块,可以通过配置的方式支持多种数据库的变化。因此整合到我的Winform开发框架里面,也很容易实现这种多数据库的方式处理。
Winform开发框架,常见的分层模式,可以分为UI层、BLL层、DAL层、IDAL层、Entity层、公用类库层等等,B/S的Web开发框架也是提供类似的架构模式,它们只是Web界面层有所不同,这样就给我们提供了一个统一性的开发模式,使得开发起来更加高效,统一性更好。框架界面层以下的架构设计图如下所示。
1、支持多数据库的设置
1)数据库访问基类的了解
为了介绍支持多种数据库的模式,我们需要先来了解下整个框架的层次结构。
AbstractBaseDAL是抽象了所有数据库实现的超级基类。
BaseDALSQL是针对SqlServer数据库部分调整基类(很小的调整)。
BaseDALSQLite是针对Sqlite数据库的部分调整基类(很小的调整)。
BaseDALMySql是针对MySqlite数据库的部分调整基类(很小的调整)。
BaseDALAccess是针对Access数据库的部分调整基类(很小的调整)。
IBaseDAL是所有基础数据库访问类的接口。
数据访问接口实现层(如Customer)和接口定义层(ICustomer)一样,都有一个基类,如基于SqlServer实现的基类为BaseDALSQL,这个基于SqlServer的数据访问基类,它也是继承自一个超级基类(大多数的实现在这里)AbstractBaseDAL。他们之间的继承关系如下所示
而我们刚才在项目工程的图里面看到,BaseDALSQL、IBaseDAL、AbstractBaseDAL这些类库由于具有很大的通用性,为了减少在不同的项目中进行复制导致维护问题,因此我们全部把这些经常使用到的基类或者接口,抽取到一个独立的类库里面,为了和普通的DotNET公用类库命名进行区分(WHC.Framework.Commons),我们把它命名为WHC.Framework.ControlUtil。
2)多数据库的代码实现
为了实现多数据库的支持,我们需要在配置文件里面读取相关的配置,看具体是构造那种该数据库的,然后进行初始化不同的程序集类,从而实现调用不同数据库类型的数据库访问类。
在BaseBLL的Init函数里面的实现代码如下所示。
01.
#region 根据不同的数据库类型,构造相应的DAL层
02.
AppConfig config =
new
AppConfig();
03.
string dbType = config.AppConfigGet(
"ComponentDbType"
);
04.
if
(string.IsNullOrEmpty(dbType))
05.
{
06.
dbType =
"sqlserver"
;
07.
}
08.
dbType = dbType.ToLower();
09.
10.
string DALPrefix =
""
;
11.
if
(dbType ==
"sqlserver"
)
12.
{
13.
DALPrefix =
"DALSQL."
;
14.
}
15.
else
if
(dbType ==
"access"
)
16.
{
17.
DALPrefix =
"DALAccess."
;
18.
}
19.
else
if
(dbType ==
"oracle"
)
20.
{
21.
DALPrefix =
"DALOracle."
;
22.
}
23.
else
if
(dbType ==
"sqlite"
)
24.
{
25.
DALPrefix =
"DALSQLite."
;
26.
}
27.
else
if
(dbType ==
"mysql"
)
28.
{
29.
DALPrefix =
"DALMySql."
;
30.
}
31.
#endregion
32.
33.
this
.dalName = bllFullName.Replace(bllPrefix, DALPrefix);
//替换中级的BLL.为DAL.,就是DAL类的全名
34.
baseDal = Reflect<IBaseDAL<T>>.Create(
this
.dalName, dalAssemblyName);
//构造对应的DAL数据访问层的对象类
在具体的业务对象的调用的时候,我们不知道它具体是调用哪个数据库的处理类进行处理的,只需要调用它的基础接口就可以了,如下是界面层的部分调用代码。
01.
//删除关联的附件
02.
if
(!string.IsNullOrEmpty(ids))
03.
{
04.
string[] idArray = ids.Split(
','
);
05.
foreach (string id in idArray)
06.
{
07.
InformationInfo info = BLLFactory<Information>.FindByID(id);
08.
if
(info !=
null
&& !string.IsNullOrEmpty(info.Attachment_GUID))
09.
{
10.
BLLFactory<FileUpload>.Instance.DeleteByAttachGUID(info.Attachment_GUID);
11.
}
12.
}
13.
}
在具体的配置文件里面,我们就可以根据需要配置好相关的数据库了。
根据Enterprise Library的配置,我们只要制定了<dataConfiguration defaultDatabase="sqlserver">,那么就会获取sqlServer的节点,而代码通过解析ComponentDbType配置项,就可以构造对应的数据库访问对象了。两者合一就可以正确获取到处理对象,并成功处理数据库的访问。
01.
<configuration>
02.
<configSections>
03.
<section name=
"dataConfiguration"
type=
"Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data"
/>
04.
</configSections>
05.
<connectionStrings>
06.
<add name=
"<strong>sqlserver</strong>"
providerName=
"System.Data.SqlClient"
connectionString=
"Persist Security Info=False;Data Source=(local);Initial Catalog=MVCWebMis;Integrated Security=SSPI"
/>
07.
<add name=
"access"
providerName=
"System.Data.OleDb"
connectionString=
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|ComponentData.mdb;User ID=Admin;Jet OLEDB:Database Pass<a href="
http:
//www.it165.net/edu/ebg/" target="_blank" class="keylink">word</a>=;"/>
08.
<add name=
"<strong>sqlite</strong>"
providerName=
"System.Data.SQLite"
connectionString=
"Data Source=|DataDirectory|ComponentData.db;Version=3;"
/>
09.
<add name=
"<strong>oracle</strong>"
providerName=
"System.Data.OracleClient"
connectionString=
"Data Source=whcdb;User ID=whc;Pass<a href="
http:
//www.it165.net/edu/ebg/" target="_blank" class="keylink">word</a>=whc"/>
10.
</connectionStrings>
11.
<dataConfiguration defaultDatabase=
"<strong>sqlserver</strong>"
>
12.
<providerMappings>
13.
<add databaseType=
"EntLibContrib.Data.SQLite.SQLiteDatabase, EntLibContrib.Data.SqLite"
name=
"System.Data.SQLite"
/>
14.
</providerMappings>
15.
</dataConfiguration>
16.
17.
<appSettings>
18.
<!--组件的数据库类型:access、sqlserver、sqlite等-->
19.
<add key=
"ComponentDbType"
value=
"<strong>sqlserver</strong>"
/>
2、支持分拆不同数据库的设置
上面介绍的方式,一次性只能访问一个数据库,因此默认在代码构造数据库访问对象的时候,是通过下面的代码进行的。
1.
Database db = DatabaseFactory.CreateDatabase();
这样每次只会获取defaultDatabase设置的数据库进行构造,如果我们在一个系统里面,同时支持多个数据库的访问,那么我们应该如何处理呢。
在框架的基类AbstractBaseDAL里面,我们对构造数据库的代码进行了封装。
01.
/// <summary>
02.
/// 根据配置数据库配置名称生成Database对象
03.
/// </summary>
04.
/// <returns></returns>
05.
protected
virtual Database CreateDatabase()
06.
{
07.
Database db =
null
;
08.
if
(string.IsNullOrEmpty(dbConfigName))
09.
{
10.
db = DatabaseFactory.CreateDatabase();
11.
}
12.
else
13.
{
14.
db = DatabaseFactory.CreateDatabase(dbConfigName);
15.
}
16.
return
db;
17.
}
而每个DAL层都会继承自AbstractBaseDAL,这样也就可以通过在DAL层指定dbConfigName进行使用不同的数据库的了。
但是这样问题出现了,假如我们有5个不同类型(SqlServer、Oracle、Mysql、Access、Sqlite)的数据库的DAL层,那么每个DAL层的实现类都要写一些代码,这样挺不方便,那么是否可以把它抽象到BLL层,这样只写一次配置就可以了,这个思路很好,我们具体来看看如何实现。
1)在IBaseDAL层定义接口
01.
/// <summary>
02.
/// 数据访问层的接口
03.
/// </summary>
04.
public
interface
IBaseDAL<T> where T : BaseEntity
05.
{
06.
/// <summary>
07.
/// 设置数据库配置项名称
08.
/// </summary>
09.
/// <param name="dbConfigName">数据库配置项名称</param>
10.
void
SetDbConfigName(string dbConfigName);<br>
11.
.............
12.
}
2、在AbstractBaseDAL添加默认实现代码
01.
/// <summary>
02.
/// 数据访问层的超级基类,所有数据库的数据访问基类都继承自这个超级基类,包括Oracle、SqlServer、Sqlite、MySql、Access等
03.
/// </summary>
04.
public
abstract
class
AbstractBaseDAL<T> where T : BaseEntity,
new
()
05.
{
06.
/// <summary>
07.
/// 设置数据库配置项名称
08.
/// </summary>
09.
/// <param name="dbConfigName">数据库配置项名称</param>
10.
public
virtual
void
SetDbConfigName(string dbConfigName)
11.
{
12.
this
.dbConfigName = dbConfigName;
13.
}<br>
14.
....................
15.
}
3、在BaseBLL里面的Init函数进行调用设置处理
01.
/// <summary>
02.
/// 参数赋值后,初始化相关对象
03.
/// </summary>
04.
/// <param name="bllFullName">BLL业务类的全名(子类必须实现),子类构造函数传入this.GetType().FullName</param>
05.
/// <param name="dalAssemblyName">数据访问层程序集的清单文件的文件名,不包括其扩展名。设置为NULL或默认为Assembly.GetExecutingAssembly().GetName().Name</param>
06.
/// <param name="bllPrefix">BLL命名空间的前缀(BLL.)</param>
07.
/// <param name="dbConfigName">数据库配置项名称</param>
08.
protected
void
Init(string bllFullName, string dalAssemblyName =
null
, string bllPrefix =
"BLL."
)
09.
{
10.
.............
11.
baseDal.SetDbConfigName(dbConfigName);
//设置数据库配置项名称
12.
}
4、在具体BLL层的业务类进行初始化处理。
01.
/// <summary>
02.
/// 政策法规公告动态
03.
/// </summary>
04.
public
class
Information : BaseBLL<InformationInfo>
05.
{
06.
public
Information() : base()
07.
{
08.
base.Init(
this
.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name,
"BLL."
,
"annotherConfig"
);
09.
}
这样设置后,我们具体调用的代码不变,但是指定业务的数据库访问已经使用了特定的配置项名称了,如果配置项不存在,那么还是会获取默认的配置项进行处理了。
通过这样的实现步骤,我们就能实现在一个业务系统里面,分拆不同的数据库,进行统一管理,而且又不会增加额外的调用难度,对于我们很多业务表,这种框架的处理方式 ,应该是不错的。