【IT168 技术文档】目前很多软件厂商针对移动设备的数据库同步解决方案,这些方案主要分为两种:第一种,是针对某种特定数据库提供的数据同步方案,比如SQL Sever的RDA和Replication、Oracle Lite和Sybase SQL Anywhere等;第二种是ISV开发的针对不同数据库的数据同步方案。
SQL Mobile支持两种数据同步的方法:Remote data access(RDA)和Replication。这两种方法都支持从远程的SQL Server服务器中下载数据到设备端的SQL Mobile数据库中,在本地对数据进行浏览和修改,再将修改结果更新到SQL Server服务器中。
RDA和Replication都需要配置SQL Mobile Server Tools,也就是说,必须要有一个运行IIS的Web Server。Server Tools的主要作用是将移动设备通过网络发送的数据库访问请求,转发给SQL Server数据库,并将结果集发送回移动设备中。
移动设备使用RDA和Replication时,SQL Mobile只需要连接IIS上的Server Tools。而数据同步需要HTTP或HTTPS的网络连接支持。网络连接可以是无线局域网(Wi-Fi)、无线网或通过AtiveSync连接的PC网络。
RDA使用了SQL Mobile中的三个组件:SQL Mobile Database Engine、SQL Mobile Client Agent和SQL Mobile Server Agent。RDA的运行过程如下图:
SQL Mobile Database Engine用于管理存储于SQL Mobile数据库中的数据。如果这些数据来自于远程SQL Server数据库,并且在调用Pull操作时设置了跟踪改变的选项,Database Engine还将跟踪整个程序运行过程中数据表的变化情况,比如insert、update和delete等操作。Database Engine将为每条记录维护更新记录。如果SQL Server数据表建有索引,那么RDA也支持在SQL Mobile数据表中建立索引。
SQL Mobile Client Agent是运行于Windows Mobile上的一个组件,主要负责与Server Agent的通讯。Client Agent实现了RDA对象的API,应用程序通过API来操作RDA。
RDA的Pull方法被调用时,Client Agent会通过HTTP向SQL Mobile Server Agent发送请求。当接收到从SQL Server返回的结果集后,Client Agent将结果集保存到SQL Mobile数据库中。
Push方法被调用时,Client Agent从SQL Mobile数据库中取出所有插入、更新和删除的记录,将它们发送给Server Agent。
SubmitSQL方法,将SQL语句通过HTTP发送给Server Agent。
RDA使用运行于IIS上的Server Agent作为SQL Server数据库与SQL Mobile数据之间的通信代理。Server Agent负责监听来自SQL Mobile Client Agent的HTTP请求。Server Agent使用临时消息文件(*.in和*.out)来管理SQL Server与SQL Mobile的交换数据。
当Client Agent调用Pull方法时,Server Agent会收到请求,然后通过OLE DB连接SQL Server数据库,调用SQL语句。Server Agent会将返回的结果集通过HTTP发送给Client Agent。
Push方法,Server Agent会收到所有被客户端修改的记录,通过OLE DB连接SQL Server数据库,然后对SQL Server中相应记录进行insert、update和delete操作。如果发生错误,Server Agent会向Client Agent报告这个错误。
SubmitSQL方法,接受指定的SQL语句,通过OLE DB连接SQL Server数据库,然后调用SQL语句。
在.NET Compact Framework中有一个SqlCeRemoteDataAccess对象,提供了RDA的应用程序编程接口。SqlCeRemoteDataAccess类的命名空间是System.Data.SqlServerCe。
SqlCeRemoteDataAccess类中包括了三个最主要的方法:
1)Pull方法:从SQL Server数据库中获取一个数据表,储存在SQL Mobile数据表中
2)Push方法:将Pull方法获得的SQL Mobile数据表,在设备端的修改传回到SQL Server 数据库中
3)SubmitSql方法:将SQL语句直接提交到SQL Server数据库中执行。
在调用Pull方法之前,我们必须创建SqlCeRemoteDataAccess的对象,并对RDA的属性进行设置。
我们通过下面的一段代码来说明如何创建一个SqlCeRemoteDataAccess对象,并调用Pull方法。
// Connection String to the SQL Server. string rdaOleDbConnectString = " Provider=sqloledb; Data Source=MySqlServer;Initial Catalog=Northwind; " + " User Id=username;Password = <password> " ; // Initialize RDA Object. SqlCeRemoteDataAccess rda = null ; try { //Try the Pull Operation. rda =new SqlCeRemoteDataAccess(); rda.InternetLogin ="MyLogin"; rda.InternetPassword ="<password>"; rda.InternetUrl ="<http://www.northwindtraders.com/sqlce/sscesa20.dll>"; rda.LocalConnectionString =@"Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0;Data Source="ssce.sdf"; rda.Pull( "Employees", "Select * from Employees", rdaOleDbConnectString, RdaTrackOption.TrackingOnWithIndexes, "ErrorTable"); } catch (SqlCeException) { //Use you own Error Handling Routine. } finally { //Dispose of the RDA Object. rda.Dispose(); }
SqlCeRemoteDataAccess对象创建后,我们需要设置InternetUrl属性,该属性是指定SQL Mobile Server Tools的URL,也就是我们在IE中输入的URL。
如果Server Tools所在的IIS设置了访问用户名和密码的话,我们还需要设置InternetLogin和InternetPassword属性,这两个属性分别对应IIS访问用户名和密码。除此之外,我们还需要设置LocalConnectionString属性,该属性提供一个SQL Mobile的数据库连接字符串。我们可以不必在连接字符串中设置Provider,这样系统就会默认使用SQL Mobile数据库支持库了,但是Data Source必须指定SQL Mobile数据库在Windows Mobile设备中的绝对路径。
大家也可以看到,SqlCeRemoteDataAccess对象中的属性主要是和SQL Mobile数据库、SQL Mobile Server Tools有关,而没有SQL Server的相关设置,我们稍后会进行说明。
当SqlCeRemoteDataAccess对象所需要的属性被设置完成后,我们就可以调用Pull方法了。Pull方法最多可以包括五个参数,我们依次来看一下函数原型和相关参数:
public void Pull ( string localTableName, string sqlSelectString, string oleDBConnectionString, RdaTrackOption trackOption, string errorTable )
localTableName参数指定了在SQL Mobile数据库中的数据表名称,该数据表用于存放Pull方法从SQL Server获得的数据。
sqlSelectString参数,用于设置从SQL Server获取的数据集的SQL语句,我们可以通过这个SQL语句设置获得结果集是某个数据表的子集。我们可以通过设置WHERE子句来获得某个数据表的子集。
oleDBConnectionString参数指定SQL Server的数据库连接字符串,在Pull方法指定SQL Server数据库连接字符串,而不是在SqlCeRemoteDataAccess对象属性中设置,是因为我们可以在一个SqlCeRemoteDataAccess对象中指定从多个SQL Server中获取数据。
trackOption参数用于SQL Mobile数据表的数据跟踪设置,指定RdaTrackOption枚举变量。
errorTable属性用于设置一个数据表的名称,该数据表用于在Push方法的时候发生数据冲突时,会将数据错误信息放到Error Table中。
下面我们来讨论下trackOption参数,如果从SQL Server中获取的数据需要在设备端进行修改,并且要将修改结果提交回SQL Server数据库的话,就需要将该参数设置为TrackingOn;如果数据不需要提交回SQL Server数据库的话,就需要设置为TrackingOff,比如需要获得商品的价格信息,而这些信息不需要在设备端修改,我们就可以设置为TrackingOff,这样做还可以节省一部分空间。
TrackingOff |
SQL Mobile不会跟踪本地数据改变,PRIMARY KEY约束在本地创建 |
TrackingOffWithIndexes |
SQL Mobile不会跟踪本地数据改变,存在于SQL Server数据表中的索引和PRIMARY KEY约束会被在本地表创建。 |
TrackingOn |
SQL Mobile会跟踪本地数据改变,PRIMARY KEY约束在本地创建,这是默认设置 |
TrackingOnWithIndexes |
SQL Mobile会跟踪本地数据改变,存在于SQL Server数据表中的索引和PRIMARY KEY约束会被在本地表创建。 |
这里需要注意的是,Pull方法一次只可以从SQL Server获取一个数据表,而设置的本地表名称,SQL Mobile数据库不能存在同名的数据表,所以每次通过Pull方法获取新的数据时,必须先删除上次Pull方法获得的本地表。
我们下边来看一下Push方法。Push方法用来把SQL Mobile本地表的修改结果传递回SQL Server数据库中。下面的代码相对比较简单:
rda.Push( " MyLocalTable " , rdaOleDbConnectString, RdaBatchOption.BatchingOn);
我们先来看下函数原型与参数:
public void Push ( string localTableName, string oleDBConnectionString, RdaBatchOption batchOption )
localTableName是SQL Mobile数据库中的通过Pull方法获取的数据表,这个表不能是一个SQL Mobile普通的本地数据表;也不能是Pull方法设置为TrackingOff时获取的表。
oleDBConnectionString和Pull方法中同名的参数作用相同,用于连接SQL Server的数据库连接字符串,该字符串必须和对应Pull方法的oleDBConnectionString相同。
batchOption参数是用于设置我们的数据是否批量提交,该参数是一个RdaBatchOption枚举变量。如果设置成BatchingOff,被修改的数据将被逐条提交,如果其中某些数据产生冲突,将不会影响另外数据的提交,但是这样的问题是数据量会增加,这也是默认选项;BatchingOn会将所有发生改变的数据记录批量提交。
最后来看SubmitSQL方法,该方法会将一个SQL语句提交到SQL Server服务器中执行。为什么还需要这样的方法呢。如果我们需要写一个数据捕捉的程序,将捕捉到的数据提交到SQL Server服务器中。如果使用Pull方法从SQL Server获取一个数据表,然后向其中添加新数据,然后再使用Push方法提交到SQL Server中,这样十分麻烦,所以直接使用SubmitSql方法直接将SQL语句提交到SQL Server中执行就可以了。
rda.SubmitSql( " CREATE TABLE MyRemoteTable (colA int) " , rdaOleDbConnectString);
我们来看SubmitSQL的原型:
public void SubmitSql ( string sqlString, string oleDBConnectionString )
sqlString参数需要指定SQL语句,该SQL语句是在远程SQL Server中执行,而不是在SQL Mobile数据库中执行。另外由于SubmitSql方法不会返回一个结果集,所以我们可以用SubmitSql方法提交一个insert、update、delete和create table等SQL语句,但是提交select的SQL语句将没有任何效果。
oleDBConnectionString与Pull、Push方法中的同名属性相同,用于设置SQL Server数据库的连接字符串。
SQL Mobile的RDA主要有下面的几个限制:
1) 在使用RDA的Pull方法从SQL Server数据库中复制数据时,SQL语句不能使用*号代替数据字段,也就是说,我们应该将SQL语句写成“SELECT CompanyName, ContactName FROM Customers”,而不是“SELECT * FROM Customers”。
2) SQL Mobile不支持触发器。
3) 如果需要跟踪数据表的改变,数据表中字段的个数作为为1017(1024个字段减去七个系统字段),那七个系统字段用于跟踪RDA数据的变化。
4) 如果Pull方法的SQL语句中包括Computed column,方法调用将失败。
5) 如果SQL Server数据表中包含IDENTITY字段。RDA无法自动管理IDENTITY字段,因为IDENTITY字段默认起始值是1,每次增量为1。可是在多个SQL Mobile数据库执行RDA的情况下,不同数据子集插入记录的IDENTITY字段可能相同。所以我们必须手工处理可能的冲突。我们可以通过“ALTER TABLE”SQL语句来修改数据表的“SEED”和“INCREMENT”的值。
例如,SQL Server数据表中最大的IDENTITY值为99,我们执行下面的SQL语句,再次插入记录时,IDENTITY值将为100。
ALTER TABLE Orders ALTER COLUMN OrderId IDENTITY ( 100 , 1 )
6) 对于SQL Mobile本地表结构改变,从上边的例子可以看到,我们可以对Pull方法产生的本地数据表schema进行一些改变。比如,改变数据表IDENTITY字段的初始值和增长量。除此之外,我们还可以添加或删除字段的默认值约束、添加或删除外键、添加或删除索引,以及删除表。这些操作是不会影响RDA的Push方法正常执行的。
但是,我们不能进行诸如增加、删除字段或修改字段名称、修改表名称、删除主键、改变数据类型定义等操作。这些Schema的改变将造成Push方法不能正常执行。
当这些Schema改变之后,我们必须删除这些SQL Mobile数据表,通过Pull方法再次获得该数据表。
在RDA将SQL Mobile数据库中的修改数据上传到SQL Server中的过程中,如果发生错误,RDA提供了一个Error Table,Push方法发生的错误将被记录在该表中。在调用Push方法时,我们可以指定一个Error Table的名称,如果Push过程中发生错误,SQL Mobile将自动在SQL Mobile数据库中创建Error Table,记录错误信息。
当SQL Mobile中的一条记录无法被Push到SQL Server中时,RDA会产生一个错误。产生错误的原因可能是:
1)在SQL Mobile数据库中插入的记录主键在SQL Server数据表中已经存在。
2)SQL Mobile试图更新一条已经被其他用户删除的记录时。
3)如果Push过程因为网络原因发生中断,但是一些insert操作已经被插入到SQL Mobile数据库中,第二个Push时,会因为记录主键重复而失败。
为了跟踪这些错误,我们在Pull方法的使用指定TRACKINGON 或者TRACKINGON_INDEXES,然后指定Error Table的名称。在Push的时候如果有错误发生的话,SQL Mobile将在Error Table里创建一条记录。该表主要有三个字段:s_ErrorDate,设置错误发生的日期和时间;s_OLEDBErrorNumber,为OLEDB中返回该错误的HResult;s_OLEDBErrorString,则是该错误的描述字符串。
我们可以根据这些错误提示,来修改错误数据。如果相关连的SQL Mobile数据表被删除,Error Table也将自动被删除。所以,我们可以将这些数据暂存到其他数据表中,或者下次Pull时使用另外的本地表名称,以保留这些错误数据。
SQL Mobile数据库支持多用户访问,本节我们要讨论在RDA过程中,多用户访问需要注意的一些问题。应用程序只需要通过Database Engine与SQL Mobile数据库进行连接,Database Engine控制来自应用程序的访问请求,应用程序不需要对数据库访问进行控制。如果需要SQL Mobile Database Engine可以锁定指定的数据,防止数据被其他应用程序修改。
SQL Mobile RDA的Server端没有提供并发访问控制机制,也就是说,SQL Server不会将被下载的数据记录锁定。当应用程序调用Push方法时,如果SQL Server中的数据已经发生了变化,Push过程会将SQL Mobile中的数据覆盖SQL Server中已经改变的记录。当然,这会造成被修改数据的丢失。
你可以在程序中通过获取不同数据子集的方式来控制这种情况的发生。也可以通过一个单独的应用程序来操作数据库,使用该应用程序来跟踪记录被修改的情况。
在RDA过程中,Database Engine允许其他用户对同一个数据库进行访问。因为Pull方法会在本地创建一个数据表,以存储来自SQL Server上的数据。所以,在Pull方法完成之前,其他用户无法访问该表。当数据被Push回SQL Server中时,如果在Push方法开始之后,SQL Mobile数据库中的数据发生改变,新的改变将在下一次Push方法中被提交。如果SQL Mobile在执行Push方法后必须再次通过Pull方法获取新数据,应用程序则必须保证在Push和Pull方法调用过程中,不会改变SQL Mobile数据库中的数据。这样可以保证在SQL Mobile数据库中的变化不会在删除数据表时丢失。