在项目开发中,尤其是二次开发的项目,经常遇到不同数据库的数据需要同步的问题,如果只是同步一下数据,不需要在程序中调用的话,那用第三方工具,像【mapforce】,就相对简单多了,除了一些复杂的业务数据很难解决外,其他一些数据结构的差异都可以用第三方的软件解决。
但往往我们遇到的是,需要把数据同步这个功能运用到程序中来,虽然一些工具【mapforce】可以生成代码,并在程序中修改下就可以使用了,但这里的修改一般相对有点困难,因为生成的代码量很大,要去修改,一般先要去理解里面的逻辑,不然也不知道怎么去修改,或者说我们只要一个比较简单的同步功能,甚至是可以直接修改源代码的(开源嘛,呵呵),不需要一个很强大,部署又很麻烦的第三方软件(一般第三方软件生成的东西很多,因为要开了的问题很多吧,所以在部署的时候就很麻烦),所以有必要自己搞一个暂时可以解决项目中的实际问题的程序,我所说的就是仅仅可以解决公司项目的问题,当然也许你的项目中也是可以使用的。
一说到数据同步,大家对映射应该也不陌生,是的,数据同步是必须通过一个中介来进行的,因为源数据和目标数据的诸多不一致的问题,所以我们要把这些不一致的问题给统一了,这样才可以解决数据异构的问题。
下面不废话了,先说下我刚写的一个DataTable同步的程序吧,首先为什么说DataTable呢,那是因为这个数据同步中的源数据就是一个DataTable,而不是其他数据库,这个DataTable可能是调用webservice或者其他接口得到的返回值;其次是同步这个概念,就是对数据库的Insert、Update、Delete了。其实这个同步就是把一个DataTable的数据insert、Update、Delete到一个目标数据库的功能,呵呵,很简单吧。
那么,如果不要开发这个程序,那么我们写代码(伪代码)一般是这样的:
2 /// 一般这个方法是从一个服务或者其他数据库获取的,然后返回的是一个DataTable对象
3 /// </summary>
4 /// <returns></returns>
5 public DataTable GetSource()
6 {
7 DataTable dt = new DataTable();
8 return dt;
9 }
10
11 /// <summary>
12 /// 这个方法就是把目标数据库的表结构读取出来,而不需要数据
13 /// </summary>
14 /// <returns></returns>
15 public DataTable GetTarget(out System.Data.SqlClient.SqlDataAdapter ad)
16 {
17 ad = new System.Data.SqlClient.SqlDataAdapter("SELECT语句","连接字符串");
18 DataTable dt = new DataTable();
19 return dt;
20 }
21
22 public void Execute()
23 {
24 System.Data.SqlClient.SqlDataAdapter ad = null;
25 DataTable source = GetSource();
26 DataTable target = GetTarget(out ad);
27 //对target里面的每个字段进行赋值
28 DataRow r = null;
29 foreach (DataRow row in source.Rows)
30 {
31 r["目标表字段名称"] = row["原始数据表字段名称"];
32 //如果是没有业务的话,就是一个一个的赋值了
33 }
34 //最后同步到数据库,这里是Insert了
35 ad.Update(target);
36 }
这样的代码,大家看了应该都会觉得很繁琐,尤其是那个赋值操作,如果是一百多个字段的话,那就,这个时候我们就可以使用XML映射来解决,如果以后有什么修改,也只要修改XML文件据可以,当然这个修改的前提是同步程序已经可以实现的情况。
下面在看看XML配置,这个是一个简单的SQL数据库的例子,自己太懒了,不想写一些复杂的例子了,真想敲下自己
2 <mapping dbprovider="LBC.Data.Mapping.Provider.SqlProvider,LBC.Data.Mapping"
3 connectionstring="server=.;database=Test;trusted_connection=yes">
4 <table name="t_user" source="user">
5 <column name="username" source="name"></column>
6 <column name="userid" source="id" pk="true"></column>
7 <column name="usertype">
8 <component class="Sqlserver.TestComponent,Sqlserver" method="GetTypeName">
9 <parameter type="int" value-mode="column" column="type" default-value="3"></parameter>
10 </component>
11 </column>
12 <column not-insert="true" not-update="true">
13 <component class="Sqlserver.TestComponent,Sqlserver" method="InsertUserRight" behavior="always">
14 <parameter type="string" column="id" value-mode="column"></parameter>
15 <parameter type="string" column="right" value-mode="column"></parameter>
16 <parameter type="string" value-mode="action"></parameter>
17 </component>
18 </column>
19 </table>
20
21 <table name="t_News" source="News">
22 <column name="NewsID" source="NewsID" pk="true" not-insert="true"></column>
23 <column name="Title" source="Title"></column>
24 <column name="ChannelID" source="ChannelID">
25 <constant value="10" type="int"></constant>
26 </column>
27 <column name="CreateTime" source="CreateTime"></column>
28 <column name="Description" source="Description"></column>
29 <column name="Content" source="Content"></column>
30 <column name="Author" source="Author"></column>
31 <column name="OrderNum" source="OrderNum"></column>
32 <column name="Hits" source="Hits"></column>
33 </table>
34 </mapping>
配置左边的,即table、column里的name属性都表示目标数据源,其他说明请往下开,写的不咋滴,请批斗吧
2 1.1、dbprovider属性,表示一个数据库提供者,请参考LBC.Data.Mapping.Provider命名空间的类
3 1.2、connectionstring属性,表示数据库连接字符串
4 1.3、table节点,表示一个目标数据库(被导入数据的数据库)的一个表
5 1.3.1、name目标表名
6 1.3.2、source源表(提供DataTable的数据源)表名
7 1.3.3、column节点,表示目标表的一个字段
8 1.3.3.1、name目标表字段名
9 1.3.3.2、source源表字段名
10 1.3.3.3、pk属性,表示该字段是否为主键,true表示是
11 1.3.3.4、not-insert属性,表示该字段是否需要运用在insert上,true表示需要
12 1.3.3.5、not-update属性,表示该字段是否需要运用在update上,true表示需要
13 1.3.3.6、constant节点,表示一个运用在字段上的特殊组件,用来表示一个常量值
14 1.3.3.6.1、value属性,表示常量值
15 1.3.3.6.2、type属性,表示常量值的类型名称
16 1.3.3.7、component节点,表示一个运用在字段上的组件,当目标表字段与源表字段不匹配,
17 需要使用程序来转换时,可以使用组件来执行,组件返回值会自动同步到目标表字段中
18 1.3.3.7.1、name属性,表示组件的名称,当某个字段上的组件的参数需要使用一个已经执行的组件的返回值时,改属性为必填
19 1.3.3.7.2、class属性,组件的程序集签名,格式为类全名称,程序集名称
20 1.3.3.7.3、method属性,表示组件调用的方法名称
21 1.3.3.7.4、behavior属性,表示组件的行为,即在什么时候可以执行该组件,
22 包括:always总是执行、never从不执行、insert执行insert的时候执行,delete执行delete的时候执行
23 update执行update的时候执行
24 1.3.3.7.4.1、parameter节点,表示组件的参数列表
25 1.3.3.7.4.1.1、type属性,表示参数的类型名称
26 1.3.3.7.4.1.2、value-mode属性,表示参数值的模式,其中包括:const常量值、action执行的动作(insert、delete、update)、
27 column从源表字段取值、component表示从已经执行的组件返回值中取值
28 1.3.3.7.4.1.3、column属性,表示value-mode等于column时,取值的字段名称
29 1.3.3.7.4.1.4、default-value属性,表示value-mode等于column时的默认值,如果字段的值为null或DbNull的时候,用该值替换
30 1.3.3.7.4.1.5、value属性,表示当value-mode等于const时的常量值
31 1.3.3.7.4.1.6、component属性,表示当value-mode等于component时,使用哪个命名组件的名称
最后,我们来说下程序中是怎么调用的吧,这里有两种方式,一种是自己提供DbProvider,就是在程序中自己new一个IDbProvider的实现类和一个数据库的连接字符串,另外一种是在XML的mapping节点中设置(查看xml文件)。
1、配置IDbProvider方式的代码:
2 DataTable dt = GetUserTable();
3 //实例化映射配置
4 LBC.Data.Mapping.Configuration cfg = new LBC.Data.Mapping.Configuration();
5 //读取配置
6 cfg.Configure();
7 //配置数据源方式,当调用了Configure方法后,就可以从配置中创建DataTableMapping类
8 LBC.Data.Mapping.DataTableMapping mapping = cfg.BuildMappingClass();
9 //执行insert
10 mapping.Insert(dt, "user", "t_user");
2、手动new方式的代码:
2 LBC.Data.Mapping.Provider.IDbProvider provider = new LBC.Data.Mapping.Provider.SqlProvider();
3 provider.ConnectionString = constring;
4 LBC.Data.Mapping.DataTableMapping mapping = new LBC.Data.Mapping.DataTableMapping(cfg.Mapping, provider);
5 mapping.Insert(dt, "user", "t_user");
具体代码请下载DEMO