一个良好的产品,可能往往需要支持多种数据库的接入,根据实际业务的需要进行调整,有时候可能需要2到3种数据库的支持。
在很多应用系统里面,虽然一般采用一种数据库运行,但是由于各种情况的需要,可能业务系统会部署在不同类型的数据库上,如果开发的系统能够很方便支持多种数据库的切换,那可以为我们减少很多烦恼,同时提高系统的适应性和强壮型。还有一种情况,由于业务数据库的不断膨胀或者方便数据库的切割隔离,有时候也会把不同的业务数据库进行分拆,如权限提供数据库,客户关系管理数据库,工作流程数据库,企业营运数据库等等,因此在一个系统里面,同时使用2个或者以上的数据库的情况也是有的。
Winform开发框架默认已经内置支持多种数据库,包括SqlServer、Mysql、Oracle、Sqlite、Access、PostgreSQL、达梦数据库都可以支持,其中达梦数据库是国产数据库中的一员,由于最近一个客户需要支持国产数据库人大金仓,本篇介绍如何整合支持这个国产数据库。
1、Winform开发框架的数据库处理层
由于Winform开发框架,是基于微软企业库的底层数据处理,其本身内在支持或者扩展支持多数据库的统一模型接入,因此我们可以在不同的DAL 分层上进行一些扩展的、特定数据库的实现即可。

Winform开发框架,常见的分层模式,可以分为UI层、BLL层、DAL层、IDAL层、Entity层、公用类库层等等,
为了介绍支持多种数据库的模式,我们需要先来了解下整个框架的层次结构。
AbstractBaseDAL是抽象了所有数据库实现的超级基类。
BaseDALSQL是针对SqlServer数据库部分调整基类(很小的调整)。
BaseDALSQLite是针对Sqlite数据库的部分调整基类(很小的调整)。
BaseDALMySql是针对MySqlite数据库的部分调整基类(很小的调整)。
......
针对数据访问层,我们需要设计好对应的继承关系,以便使得我们的基类能够封装大多数的操作,并给子类相对的弹性处理空间,如对于客户Customer的对象,数据接口层和数据访问实现层的关系如下所示。

使用微软企业库Enterprise Library的好处就是可以统一编程模型,实现对多种数据库的兼容处理,而微软企业库Enterprise Library最大的特点是基于配置项实现多种数据库的处理,通过对使用不同的配置项,就可以迅速切换到对应的数据库上来,代码不需要修改。
对于一般的企业库配置处理,我们增加配置项如下所示。
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data"/>
</configSections>
然后为不同的数据库添加不同的连接字符串
对于默认支持的SQLServer数据库,它的连接字符串如下所示。
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data"/>
</configSections>
<connectionStrings>
<!--SQLServer数据库的连接字符串-->
<add name="sqlserver" providerName="System.Data.SqlClient" connectionString="Persist Security Info=False;Data Source=(local);Initial Catalog=WinFramework;Integrated Security=SSPI"/>
</connectionStrings>
<dataConfiguration defaultDatabase="sqlserver">
</dataConfiguration>
</configuration>
不过对于一些扩展支持的数据库,我们还需要添加一些映射处理,如对于MySQL的支持,我们需要添加连接字符串:
<!--MySQL数据库的连接字符串-->
<add name="mysql" providerName="MySql.Data.MySqlClient" connectionString="Server=localhost;Database=WinFramework;Uid=root;Pwd=123456;"/>
还需要添加ProviderMappings的支持,如下所示的XML。
<dataConfiguration defaultDatabase="mysql">
<providerMappings>
<add databaseType="EntLibContrib.Data.MySql.MySqlDatabase, EntLibContrib.Data.MySql" name="MySql.Data.MySqlClient" />
</providerMappings>
</dataConfiguration>
具体开发项目的时候,不同数据库有一些不同的处理,如分页操作、获取指定记录的处理等等,这些我们就需要发挥上面提到的数据库基类的功能了,通过基类功能的封装,我们可以除了可以使用所有数据库的共性外,还可以使用它的一些特定处理操作,这样我们就可以充分利用各种不同数据库的特点,但是又统一到一个开发模型上来,降低了各种不同数据库之间开发的成本,同时也减少不同数据库之间的迁移难度,提高代码的可阅读性和可扩展性。
对于多种数据库,如SqlServer、Mysql、Oracle、Sqlite、PostgreSQL、达梦数据库等的接入,我们把关系型数据库的接入抽象为一个基类,来封装绝大多数的数据处理,如对于业务核心模块的分层结构如下所示。

2、增加对国产数据库人大金仓的支持
国产数据库人大金仓,官网地址为:https://www.kingbase.com.cn/
国产数据库人大金仓和PostgreSQL数据库很接近,几乎可以说语法有95% 以上的相似度,因此如果我们了解PostgreSQL数据库的使用,那么对于人大金仓的使用也就非常熟悉了。
PostgreSQL数据库的引用也很多,PostgreSQL逐渐成为开源关系型数据库的首选,在功能上是全面超越MySQL的一个开源数据库。
很早之前在随笔《PostgreSQL介绍以及如何开发框架中使用PostgreSQL数据库》、《在 PostgreSQL 中,解决图片二进制数据,由于bytea_output参数问题导致显示不正常的问题》中大致介绍了该数据库的一些使用经验。
对于MySQL、PostgreSql,我一般倾向于使用Navicat 管理工具来管理数据库,如下所示。

而对于国产的人大金仓数据库,需要用其自己的管理工具了,Navicate好像支持不了,如下所示。

很多时候,我们只需把生成PostGreSql的数据库脚本在国产数据库人大金仓的管理工具上运行即可创建数据库表和插入数据。不过有一些值得注意的地方。
PostgreSql很多时候常用到一些自动的GUID值,使用内置函数 uuid_generate_v4() ,类似SqlServer里面的**newid()**的函数,
CREATE TABLE public.tw_businesstrip (
id varchar(50) NOT NULL,
reason varchar(250),
location varchar(50),
starttime timestamp(6),
endtime timestamp(6),
duration varchar(50),
attachguid varchar(50) DEFAULT uuid_generate_v4(),
apply_id varchar(50) DEFAULT uuid_generate_v4(),
applydate timestamp(6),
applydept varchar(50),
note text,
creator varchar(50),
createtime timestamp(6),
editor varchar(50),
edittime timestamp(6)
)
在人大金仓里面,默认是没有这个内置函数的,如果需要使用,可以引入对应的扩展包。
我们只需再Sql脚本的头部声明引入扩展包即可。
----金仓数据库uuid-ossp插件
create extension "uuid-ossp";
select uuid_generate_v4();
而对于一些自增长的类型,和PostGreSql一样,我们可以声明一个序列来处理。
DROP SEQUENCE IF EXISTS public.t_acl_ou_id_seq;
CREATE SEQUENCE public.t_acl_ou_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
然后就可以引用它来自增长了。
DROP TABLE IF EXISTS public.t_acl_ou;
CREATE TABLE public.t_acl_ou (
id int4 DEFAULT nextval('t_acl_ou_id_seq'::regclass) NOT NULL,
pid int4 DEFAULT '-1'::integer,
......
);
和PostGreSql中的数据库类型对应一样,一般C#的int对应的是int4, long对应的是int8, 日期是timestamp(6),如下是一个表的SQL。
DROP TABLE IF EXISTS public.mps_mailattach;
CREATE TABLE public.mps_mailattach (
id int4 DEFAULT nextval('mps_mailattach_id_seq'::regclass) NOT NULL,
company_id int4,
user_idint4,
doctype varchar(10),
createtime timestamp(6),
doc_id int8,
filename varchar(100),
realfilename varchar(100),
filesize int8
);
导入数据库表和数据后,我们就可以再Winform开发框架中测试人大金仓数据库的运行效果了。
由于前面介绍了,人大金仓使用PostGreSql协议处理即可,因此不用特别的调整就可以重用PostGreSql的底层处理逻辑了。
完整的数据库连接如下所示。
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data" />
</configSections>
<connectionStrings>
<!--Sqlserver数据库的连接字符串-->
<add name="sqlserver" providerName="System.Data.SqlClient" connectionString="Persist Security Info=False;Data Source=(local);Initial Catalog=WinFramework;Integrated Security=SSPI" />
<!--PostgreSQL数据库的连接字符串-->
<add name="npgsql" providerName="Npgsql" connectionString="Server=localhost;Port=5432;Database=winframework;User Id=postgres;Password=123456" />
<!--MySQL数据库的连接字符串-->
<add name="mysql" providerName="MySql.Data.MySqlClient" connectionString="Server=localhost;Database=WinFramework;Uid=root;Pwd=root;" />
<!--路径符号|DataDirectory|代表当前运行目录-->
<add name="access" providerName="System.Data.OleDb" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\WinFramework.mdb;User ID=Admin;Jet OLEDB:Database Password=;" />
<!--sqlite数据库字符串,路径符号|DataDirectory|代表当前运行目录-->
<add name="sqlite" providerName="System.Data.SQLite" connectionString="Data Source=|DataDirectory|\WinFramework.db;Version=3;" />
<!--达梦数据库的连接字符串-->
<add name="Dm" providerName="Dm" connectionString="Server=localhost;User ID=SYSDBA;PWD=SYSDBA;Database=WINFRAMEWORK;" />
<!--IBM DB2数据库的连接字符串-->
<add name="db2" providerName="IBM.Data.DB2" connectionString="database=whc;uid=whc;pwd=123456" />
<!--Oracle数据库的连接字符串-->
<!--不受驱动影响,32位64位均可使用-->
<add name="oracle3" providerName="OracleManaged" connectionString="Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)));User ID=win;Password=win" />
<!--人大金仓数据库的连接字符串,-->
<add name="kingbase" providerName="Npgsql" connectionString="Server=localhost;Port=54321;Database=test;User Id=system;Password=system;SslMode=Prefer;" />
</connectionStrings>
<dataConfiguration defaultDatabase="sqlserver">
<providerMappings>
<add databaseType="EntLibContrib.Data.PostgreSql.NpgsqlDatabase, EntLibContrib.Data.PostgreSql" name="Npgsql" />
<add databaseType="EntLibContrib.Data.MySql.MySqlDatabase, EntLibContrib.Data.MySql" name="MySql.Data.MySqlClient" />
<add databaseType="EntLibContrib.Data.SQLite.SQLiteDatabase, EntLibContrib.Data.SqLite" name="System.Data.SQLite" />
<add databaseType="EntLibContrib.Data.Dm.DmDatabase, EntLibContrib.Data.Dm" name="Dm" />
<add databaseType="EntLibContrib.Data.DB2.DB2Database, EntLibContrib.Data.DB2" name="IBM.Data.DB2" />
<add databaseType="EntLibContrib.Data.OracleManaged.OracleDatabase, EntLibContrib.Data.OracleManaged" name="OracleManaged" />
</providerMappings>
</dataConfiguration>
完成后测试正常,并不需要额外的处理,界面效果和数据库无实际耦合。

测试不同的界面处理,确保切换到人大金仓数据库后,各项功能表现一致即可。
对于微软企业库的通用模型处理,它们对于不同数据库的兼容还是非常不错的,这样增加一个国产数据库,很平滑的切换过去了。