1. 说明:本项目中使用的是SSCE3.5版本。
只下载的数据,采用SnopShot模式下载;双向同步和只上传同步都采用Bidirectional模式;同步时间频率如下:
只下载:轮询同步程序,计划同步1d/次;同时,程序中做可以触发的同步;
双向同步:轮询同步程序,计划同步5mi/次;定时清理:轮询同步程序,计划同步1d/次;
只上传:同步轮询程序,计划同步1d/次;定时清理:同步轮询程序,计划同步1M/次;
当主程序登录成功后,启动后台同步线程,5分钟执行一次同步,同步时校验当前时间与配置文件中保存的LastSyncDateTime的时间差是否大于执行频率来判断是否本次执行同步。后台线程在主程序完全退出后自动结束。
举例:connectionString="Data Source=..\Database\DIH_AMS.sdf;PersistSecurity Info = False;Max Database Size = 4000; Max Buffer Size = 1024;"
项目中常用的连接SDF的写法。解释如下:
Data Source:SDF的路径和名称,上面用到了相对路径;
Persist Security Info:是否保存密码,这里true/false影响不大;
Max Database Size:SDF文件的最大支持容量,默认128,最大支持到4G,但只能写到<4091;
Max Buffer Size:缓存大小,默认640;
SDF文件,可以设置访问密码,不像MS-Server要设置UserID和Password,比如” Password= 'dih+dih123';”,设置密码时,需要Server Studio打开SDF,在属性里面添加密码。
自动压缩设置,比如”SSCE:AutoShrink Threshold=60;”这里不写的话默认也是60%,表示实际容量不超过容量的60%时,自动压缩;
参考:http://msdn.microsoft.com/zh-cn/library/system.data.sqlserverce.sqlceconnection.connectionstring(VS.85).aspx
#region InsertLocalData
public static bool InsertLocalData(string strSql, string clientConnStr)
{
bool b = true;
using (SqlCeConnection conn = new SqlCeConnection(clientConnStr))
{
try
{
conn.Open();
SqlCeCommand cmd = conn.CreateCommand();
cmd.CommandText = strSql;
cmd.ExecuteNonQuery();
}
catch
{
b = false;
conn.Close();
}
}
return strRes;
}
#endregion
#region DeleteLocalData
public static bool DeleteLocalData(string strSql, string clientConnStr)
{
bool b = true;
using (SqlCeConnection conn = new SqlCeConnection(clientConnStr))
{
try
{
conn.Open();
SqlCeCommand cmd = conn.CreateCommand();
cmd.CommandText = strSql;
cmd.ExecuteNonQuery();
}
catch
{
b = false;
conn.Close();
}
}
return b;
}
#endregion
#region UpdateLocalData
public static bool UpdateLocalData(string strSql, string clientConnStr)
{
bool b = true;
using (SqlCeConnection conn = new SqlCeConnection(clientConnStr))
{
try
{
conn.Open();
SqlCeCommand cmd = conn.CreateCommand();
cmd.CommandText = strSql;
cmd.ExecuteNonQuery();
}
catch
{
b = false;
conn.Close();
}
}
return b;
}
#endregion
#region SelectLocalData
public static DataSet SelectLocalData(string strSql,string clientConnStr)
{
DataSet ds = new DataSet();
using (SqlCeConnection conn = new SqlCeConnection(clientConnStr))
{
try
{
conn.Open();
SqlCeCommand cmd = conn.CreateCommand();
cmd.CommandText = strSql;
SqlCeDataReader rdr = cmd.ExecuteReader();
SqlCeDataAdapter dar = new SqlCeDataAdapter(cmd);
dar.Fill(ds);
}
catch
{
conn.Close();
}
}
return ds;
}
#endregion
上图即同步之后的SDF文件,__sync开头的3张表是数据同步时产生的表;MS-Server数据库上的ms_core是Schema,比如ms_core.AnesAddDrugWay对应SDF中的ms_core_AnesAddDrugWay表,该处的转化是在数据同步时自动形成的。
这是同步后的SDF文件上的一张表,最后两列[LastEditDate][CreationDate]是根据数据同步时配置产生的,其他列都是MS-Server上本身的列。其实,这里的[LastEditDate][CreationDate]都是DateTime类型的字段,分别表示[最近修改时间]和[创建时间],如果在MS-Server数据库上本身就有这样含义的字段,完全可以使用它本身的字段。区别是,如果是在创建本地数据缓存时配置的新建列(不是原有的列),会自动的在MS-Server数据端产生对应的触发器用于修改更新后的这两列的值保持当前时间;使用数据库原有的列不会产生触发器。
以上就是SDF文件的数据库结构与MS-Server之间的区别,主要体现在两点:其一是多了三张表用于同步使用;其二是每张表中多了两列用于同步校验。
MS-Server中的每张同步的表也会多了两列[LastEditDate][CreationDate],同时MS-Server对同步的表的{新增、修改、删除}会生成相应的触发器用于保存和更新[LastEditDate][CreationDate];服务端的删除数据同时保存其主键和删除时间到[表_Tombstone]中。服务端的表结构变动意义明显,此处不详说。每次同步的同步信息保存到SDF中。
此处说的下载就是指从服务端Download数据,和同步模式[只上传][只下载][双向同步][快照]不是相同的概念。下载时,需要得到Server端的数据更改,即需要得到Server端的{新增、删除、修改},该部分的内容主要体现在SyncAdapter中的Command命令通过查询得到的。Command命令包括SelectIncrementalInsertsCommand(查询服务端新增的更改),SelectIncrementalDeletesCommand(查询服务端删除的更改),SelectIncrementalUpdatesCommand(查询服务端修改的更改)。
a) 新增
this.SelectIncrementalInsertsCommand.CommandText= @"SELECT [MedicalStaffKey], [StaffID], [StaffType], [FullName], [Birth],[Gender], [IdentityNo], [StartWorking], [EndWorking], [SectionKey],[SectionName], [TitleKey], [TitleName], [DutyKey], [DutyName], [StudiesKey],[StudiesName], [PostalAddress], [Tel], [CardNo], [Fingerprint], [DescText], [State],[CreatedActorKey], [CreatedLocalDtm], [LastModifiedActorKey],[LastModifiedLocalDtm], [LastModifiedBinaryValue], [ExPrimaryKey],[ExDeleteFlag], [NameAbbr], [LastEditDate], [CreationDate] FROMms_core.MedicalStaff WHERE ([CreationDate] > @sync_last_received_anchor AND[CreationDate] <= @sync_new_received_anchor)";
Where条件的意思就是将对在上次收到的定位点值(更改时间)之后和新收到的定位点值(更改时间)之前所做的更改进行同步,说白了就是查询上次同步和本次同步之间新增的数据。同时在完成本次同步时,将这个变量修改为本次修改的时间(定位点),以便下次同步时做为新的时间依据(定位点)。
以下说明参数@sync_last_received_anchor和@sync_new_received_anchor:
@sync_last_received_anchor的值是通过__syncArticles得到的,这张表中保存了该数据库中所有同步过的表的上次同步时间,如下图:
实际对这张表进行同步之前,同步框架查询需要同步的表TableName中ReceivedAnchord的值,即是@sync_last_received_anchor的值,也就是上次本表进行同步的时间;@sync_new_received_anchor时同步框架获取的当前时间。这样介于这两个时间段内的新增数据就可以在服务端查询到了,在同步后,同步框架将@sync_new_received_anchor的值赋值给ReceivedAnchord为下次同步使用。对表__syncArticles的读写操作是同步框架完成的,不需要人工干预。
其中__syncArticles表中的SentAnchor的用法暂时不知,同步时好像没有用到。
b) 删除
this.SelectIncrementalDeletesCommand.CommandText= "SELECT [MedicalStaffKey], [DeletionDate] FROMms_core.MedicalStaff_Tombstone WHERE (@sync_initialized=1 AND[DeletionDate]>@sync_last_received_anchor AND [DeletionDate] <=@sync_new_received_anchor)";
查询服务端的删除数据,不是查询的原表;原表数据删除后会将该条数据的主键作为标识保存到[原表_Tombstone]中,查询服务端的删除更改时,只需要查询这个表即可。参数@sync_last_received_anchor和@sync_new_received_anchor的用法与上部分用法相同不提。
c) 修改
this.SelectIncrementalUpdatesCommand.CommandText= @"SELECT [MedicalStaffKey], [StaffID], [StaffType], [FullName], [Birth],[Gender], [IdentityNo], [StartWorking], [EndWorking], [SectionKey],[SectionName], [TitleKey], [TitleName], [DutyKey], [DutyName], [StudiesKey],[StudiesName], [PostalAddress], [Tel], [CardNo], [Fingerprint], [DescText],[State], [CreatedActorKey], [CreatedLocalDtm], [LastModifiedActorKey],[LastModifiedLocalDtm], [LastModifiedBinaryValue], [ExPrimaryKey],[ExDeleteFlag], [NameAbbr], [LastEditDate], [CreationDate] FROMms_core.MedicalStaff WHERE ([LastEditDate] > @sync_last_received_anchor AND[LastEditDate] <= @sync_new_received_anchor AND [CreationDate] <=@sync_last_received_anchor)";
查询服务端修改更新的用法和查询服务端的新增更改相同不提。
总结:通过上面的SQL可以看到,这些语句都是对远程服务端的数据库MS-Server进行的查询操作,对于客户端怎样获取到的本地更改数据,这里不提。在同一组同步syncTables中的数据表在同步时属于同一个事务,如果中途打断时,不会保存本次同步时间和事务编号;该同步成功后,保存该事务号到SDF的__syncTransactions表中并更新__syncArticles中相关的ReceivedAnchor的值。但是,SnopShot同步模式用法稍微不同:SnopShot同步时,不关心上次同步时间@sync_last_received_anchor,所以SDF文件中表__syncArticles中没有对SnopShot模式下表的数据,如图:
不论什么同步模式,同步成功后,都会新增一条当前的事务号到__syncTransactions中。
Sync同步,服务端的操作包括对服务端更改和查询服务端的更改。更改服务端即提交客户端的更改应用到服务端。体现在SyncAdapter.Command中:InsertCommand,DeleteCommand和UpdateCommand。
a) 新增
this.InsertCommand.CommandText= @"INSERT INTO ms_core.UserAccount ([UserAccountKey],[UserID],[PasswordHashValue],[PasswordExpirationLocalDtm],[StartLocalDtm], [EndLocalDtm], [DescText], [TempFlag], [LockedFlag],[ActiveFlag], [DeleteFlag], [State], [CreatedActorKey], [CreatedLocalDtm],[LastModifiedActorKey], [LastModifiedLocalDtm], [LastEditDate],[CreationDate])VALUES(@UserAccountKey,@UserID,@PasswordHashValue,@PasswordExpirationLocalDtm,@StartLocalDtm, @EndLocalDtm, @DescText, @TempFlag, @LockedFlag, @ActiveFlag,@DeleteFlag, @State, @CreatedActorKey, @CreatedLocalDtm, @LastModifiedActorKey,@LastModifiedLocalDtm, @LastEditDate, @CreationDate) SET @sync_row_count =@@rowcount";
参数@sync_row_count用于接收执行上述语句后影响的行数。
b) 删除
this.DeleteCommand.CommandText="DELETEFROM ms_core.UserAccount WHERE ([UserAccountKey] = @UserAccountKey) AND (@
sync_force_write= 1 OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count =@@rowcount";
删除时,通过主键删除。参数@sync_last_received_anchor由同步之前客户端SDF提供。参数@sync_row_count不提。
c) 修改
this.UpdateCommand.CommandText= @"UPDATE ms_core.UserAccount SET [UserID] = @UserID, [PasswordHashValue]= @PasswordHashValue, [PasswordExpirationLocalDtm] =@PasswordExpirationLocalDtm, [StartLocalDtm] = @StartLocalDtm, [EndLocalDtm] =@EndLocalDtm, [DescText] = @DescText, [TempFlag] = @TempFlag, [LockedFlag] =@LockedFlag, [ActiveFlag] = @ActiveFlag, [DeleteFlag] = @DeleteFlag, [State] =@State, [CreatedActorKey] = @CreatedActorKey, [CreatedLocalDtm] =@CreatedLocalDtm, [LastModifiedActorKey] = @LastModifiedActorKey,[LastModifiedLocalDtm] = @LastModifiedLocalDtm, [LastEditDate] = @LastEditDate,[CreationDate] = @CreationDate WHERE ([UserAccountKey] = @UserAccountKey) AND(@sync_force_write = 1 OR ([LastEditDate] <= @sync_last_received_anchor))SET @sync_row_count = @@rowcount";
修改时参数很简单,不详说。
总结:更改服务端的操作很简单明白,客户端将客户端的本次更改数据通过上述SQLCommand应用到服务端,将应用结果统计信息写入到SyncStatistics中。
Sync Framework在执行同步操作时,在程序Code或者框架介绍中,基本上没有提及它是怎样工作的,怎样查找到所有的需要同步的数据,以及怎样将服务端的更改更新应用到本地SDF,通过MSDN文档和一些问答来尽量的推测出在客户端的工作原理。
a) 查找SDF的更改
查找服务端的更改是根据Command实现的,但SyncAdapter只存在于Server Sync Provider中。
b) 应用服务端的更改到SDF
在创建本地数据缓存的Code中,没有找到有关本地SDF的Command之类的命令,因此对于从服务器端下载的数据更改如何应用到本地SDF,对我们来说是透明的。
数据源是服务端的更改,即通过SyncAdapter.Command查询到的结果。向SDF中应用新增数据时,执行插入操作,没有限制条件,只受到SDF的主键的约束;向SDF中应用修改的数据时,执行修改操作,修改语句的条件只是主键约束;向SDF中应用删除数据时,根据主键删除,不会判断其他条件。以上操作都是根据程序演示判断得到。
这两列只在双向同步模式下的SDF表中被生成,当数据是新增时,[__sysChangeTxBsn][__sysInsertTxBsn]值相等,同一次的同步新增的数据都是相同的值;如果数据有修改,只更改[__sysChangeTxBsn]的值,该值是同步框架产生。
每次Agent.Synchronize()之后,SDF文件中表__syncTransactions中新增一条SyncBsn;(这里将[__sysChangeTxBsn][__sysInsertTxBsn][SyncBsn]都叫做事务号),查找SDF中的更改是上次同步的事务号与本次同步需要产生的事务号之间的数据;至于怎样的查询命令就是不可见的了,同时,[__sysChangeTxBsn]>=[__sysInsertTxBsn]是成立的。但是删除的数据保存到哪里了,会不会有类似_Tombone之类的删除逻辑表存在也没有找到。补充(这里是推测):每张表同步的事务号就是SDF文件的表__syncArticles中的[SentAnchor]的值。