我介绍了SnapShot(快照方式)和“只同步新更改和增量更改”这两种同步方式。将使用设计器同步向导生成了两个相应的DEMO。今天我们会一起分析一下这两个DEMO中相应的同步文件内容和相互差异(DEMO下载,请点击这里)。
首先要分析的DEMO是“只同步新更改和增量更改”,我们用VS2008打开DEMO的解决方案方案,如下图:
之前所说的关于MSF为我们生成的主要的类代码就保存在了BiDirectSyncData.sync文件中,下面先分析一下BiDirectSyncDataClientSyncProvider这个类,如下:
public
partial
class
BiDirectSyncDataClientSyncProvider :
Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider {
public BiDirectSyncDataClientSyncProvider() {
this .ConnectionString = global ::MSF_WinFormDemo.Properties.Settings.
Default.ClientBiDirectSynce2ConnectionString;
}
public BiDirectSyncDataClientSyncProvider( string connectionString) {
this .ConnectionString = connectionString;
}
}
Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider {
public BiDirectSyncDataClientSyncProvider() {
this .ConnectionString = global ::MSF_WinFormDemo.Properties.Settings.
Default.ClientBiDirectSynce2ConnectionString;
}
public BiDirectSyncDataClientSyncProvider( string connectionString) {
this .ConnectionString = connectionString;
}
}
其实现代码相对简单,只定义和配置了获取本地数据库文件链接的方法,另外就是其是继承自:
Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider,而最终的父类指向是SyncProvider(注:与BiDirectSyncDataServerSyncProvider最终的父类相同)。
这类的功能就是针对 SQL Server Compact 对要与客户端进行通信的(客户端)同步提供程序支持,并将同步代理(sync Agent)与客户端数据库的特定实现(本文中为SQLCE)分离开来。
Microsoft.Synchronization.Data.SqlServerCe.SqlCeClientSyncProvider,而最终的父类指向是SyncProvider(注:与BiDirectSyncDataServerSyncProvider最终的父类相同)。
这类的功能就是针对 SQL Server Compact 对要与客户端进行通信的(客户端)同步提供程序支持,并将同步代理(sync Agent)与客户端数据库的特定实现(本文中为SQLCE)分离开来。
而图中的Sync Agent(代理)是通过BiDirectSyncDataSyncAgent类提供的。在之前我们所做的DEMO中,进行数据同步时,都是通过此代理进行的。可以说这是我们同步的核功能代码,下面是其构造方法,如下:
public
BiDirectSyncDataSyncAgent() {
this .InitializeSyncProviders();
this .InitializeSyncTables();
this .OnInitialized();
}
this .InitializeSyncProviders();
this .InitializeSyncTables();
this .OnInitialized();
}
首先它会调用InitializeSyncProviders方法来构造两个对象实例,如下:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeSyncProviders() {
// Create SyncProviders.
this .RemoteProvider = new BiDirectSyncDataServerSyncProvider(); // 远程服务器
this .LocalProvider = new BiDirectSyncDataClientSyncProvider(); // 本地数据
}
private void InitializeSyncProviders() {
// Create SyncProviders.
this .RemoteProvider = new BiDirectSyncDataServerSyncProvider(); // 远程服务器
this .LocalProvider = new BiDirectSyncDataClientSyncProvider(); // 本地数据
}
注:RemoteProvider,LocalProvider属性继承自BiDirectSyncDataSyncAgent的父类(SyncAgent)
这样该代理就会拥有同时对客户端和服务器端进行数据操作的对象实例。然后就是要清楚同步时的要操作(同步)的内容了,也就是上面初始化代码的第二行,InitializeSyncTables方法。如下:
这样该代理就会拥有同时对客户端和服务器端进行数据操作的对象实例。然后就是要清楚同步时的要操作(同步)的内容了,也就是上面初始化代码的第二行,InitializeSyncTables方法。如下:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeSyncTables() {
// Create SyncTables.
this ._dnt_posts1SyncTable = new dnt_posts1SyncTable();
this ._dnt_posts1SyncTable.SyncGroup = new Microsoft.Synchronization.
Data.SyncGroup( " dnt_posts1SyncTableSyncGroup " );
this .Configuration.SyncTables.Add( this ._dnt_posts1SyncTable);
}
private void InitializeSyncTables() {
// Create SyncTables.
this ._dnt_posts1SyncTable = new dnt_posts1SyncTable();
this ._dnt_posts1SyncTable.SyncGroup = new Microsoft.Synchronization.
Data.SyncGroup( " dnt_posts1SyncTableSyncGroup " );
this .Configuration.SyncTables.Add( this ._dnt_posts1SyncTable);
}
在这个方法中首先是声明一个dnt_posts1SyncTable对象实例,而该实例的类声明时是继承自Microsoft.Synchronization.Data.SyncTable(也就是我们上图中所说的那个SyncTable)。其自身是表示在同步过程中涉及的表的客户端设置。而dnt_posts1SyncTable的构造方法中会设置同步数据时所要同步的本地数据库中的表名称,以及定义可用于在客户端数据库中创建表的选项(CreationOption):
TableName
=
"
dnt_posts1
"
;
CreationOption = Microsoft.Synchronization.Data.TableCreationOption.DropExistingOrCreateNewTable;
CreationOption = Microsoft.Synchronization.Data.TableCreationOption.DropExistingOrCreateNewTable;
在这里有发现写说明一下的就是“在客户端数据库中创建表的选项”有五种,分别对不同的使用意思,依次是:
CreateNewTableOrFail:在客户端数据库中创建表。如果某个现有表具有相同的名称,则引发异常。
DropExistingOrCreateNewTable:在客户端数据库中创建表。如果某个现有表具有相同的名称,则先删除现有表。
TruncateExistingOrCreateNewTable:如果客户端数据库中不存在要创建的表,则在客户端数据库中创建该表。如果某个现有表具有相同的名称,则删除此表中的所有行。
UploadExistingOrCreateNewTable:如果客户端数据库中不存在要创建的表,则在客户端数据库中创建该表。如果某个现有表具有相同的名称,则在首次同步时上载此表中的所有行。此选项仅在 SyncDirection 为 Bidirectional 或 UploadOnly 时有效。
UseExistingTableOrFail:使用客户端数据库中与要创建的表同名的现有表。如果该表不存在,则引发异常。
DropExistingOrCreateNewTable:在客户端数据库中创建表。如果某个现有表具有相同的名称,则先删除现有表。
TruncateExistingOrCreateNewTable:如果客户端数据库中不存在要创建的表,则在客户端数据库中创建该表。如果某个现有表具有相同的名称,则删除此表中的所有行。
UploadExistingOrCreateNewTable:如果客户端数据库中不存在要创建的表,则在客户端数据库中创建该表。如果某个现有表具有相同的名称,则在首次同步时上载此表中的所有行。此选项仅在 SyncDirection 为 Bidirectional 或 UploadOnly 时有效。
UseExistingTableOrFail:使用客户端数据库中与要创建的表同名的现有表。如果该表不存在,则引发异常。
通过该项设置,就可以在同步数据时,对本地数据表中已存在同名表示时,进行相应的后续操作了。
当然,上面的InitializeSyncTables方法中还包括对SyncTables.Add(this._dnt_posts1SyncTable);
方法的调用, 因为SyncTables返回是SyncTableCollection类型(同步表对象的集合),通过这个设置会最终完成本次要同步那些数据(表)的设置。
方法的调用, 因为SyncTables返回是SyncTableCollection类型(同步表对象的集合),通过这个设置会最终完成本次要同步那些数据(表)的设置。
好的,到这里,基本上完成了对客户端同步程序的代码介绍。接下来就是服务器端的同步代码了。
服务器端同步代码主要包括两个类:
1.dnt_posts1SyncAdapter,封装一组命令,用于获取架构信息以及在服务器数据库中检索和应用更改等操作。
1.dnt_posts1SyncAdapter,封装一组命令,用于获取架构信息以及在服务器数据库中检索和应用更改等操作。
2.BiDirectSyncDataServerSyncProvider,提供与服务器数据库同步通信的程序,并将同步代理与数据库的特定实现进行分离(上面所说的Agent中的RemoteProvider属)。
首先要说明的就是SyncAdapter类:dnt_posts1SyncAdapter,其主要提供了对于服务器端数据进行CRUD的SQL对象的初始化和参数声明,其构造方法如下:
public
dnt_posts1SyncAdapter() {
this .InitializeCommands();
this .InitializeAdapterProperties();
this .OnInitialized();
}
this .InitializeCommands();
this .InitializeAdapterProperties();
this .OnInitialized();
}
上面的InitializeCommands()方法即是对SQL对象的初始化(SQL对象由父类中提供):
public
IDbCommand DeleteCommand {
get
;
set
; }
public IDbCommand InsertCommand { get ; set ; }
public IDbCommand SelectConflictDeletedRowsCommand { get ; set ; }
public IDbCommand SelectConflictUpdatedRowsCommand { get ; set ; }
public IDbCommand SelectIncrementalDeletesCommand { get ; set ; }
public IDbCommand SelectIncrementalInsertsCommand { get ; set ; }
public IDbCommand SelectIncrementalUpdatesCommand { get ; set ; }
public IDbCommand UpdateCommand { get ; set ; }
public IDbCommand InsertCommand { get ; set ; }
public IDbCommand SelectConflictDeletedRowsCommand { get ; set ; }
public IDbCommand SelectConflictUpdatedRowsCommand { get ; set ; }
public IDbCommand SelectIncrementalDeletesCommand { get ; set ; }
public IDbCommand SelectIncrementalInsertsCommand { get ; set ; }
public IDbCommand SelectIncrementalUpdatesCommand { get ; set ; }
public IDbCommand UpdateCommand { get ; set ; }
看来只要实现相应的命令对象实例绑定即可了。因为该方法的内容有些长,但并不复杂,下面简单对里面的更新操作进行说明,其余的代码大家一看便知:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeCommands() {
// 之前是添回和删除SQL对象的设置
// dnt_posts1SyncTableUpdateCommand command.
this .UpdateCommand = new System.Data.SqlClient.SqlCommand();
this .UpdateCommand.CommandText = @" UPDATE dbo.dnt_posts1 SET [fid] = @fid, [tid] = @tid,
[parentid] = @parentid, [layer] = @layer, [poster] = @poster, [posterid] = @posterid,
[title] = @title, [postdatetime] = @postdatetime, [message] = @message, [ip] = @ip,
[lastedit] = @lastedit, [invisible] = @invisible, [usesig] = @usesig, [htmlon] = @htmlon,
[smileyoff] = @smileyoff, [parseurloff] = @parseurloff, [bbcodeoff] = @bbcodeoff,
[attachment] = @attachment, [rate] = @rate, [ratetimes] = @ratetimes, [LastEditDate] =
@LastEditDate, [CreationDate] = @CreationDate WHERE ([pid] = @pid) AND (@sync_force_write = 1
OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount " ;
this .UpdateCommand.CommandType = System.Data.CommandType.Text;
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @fid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @tid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @parentid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @layer " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @poster " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @posterid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @title " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @postdatetime " ,
System.Data.SqlDbType.SmallDateTime));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @message " , System.Data.SqlDbType.NText));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @ip " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @lastedit " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @invisible " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @usesig " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @htmlon " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @smileyoff " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @parseurloff " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @bbcodeoff " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @attachment " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @rate " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @ratetimes " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @LastEditDate " , System.Data.SqlDbType.DateTime));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @CreationDate " , System.Data.SqlDbType.DateTime));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @pid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @sync_force_write " , System.Data.SqlDbType.Bit));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " ,
System.Data.SqlDbType.DateTime));
System.Data.SqlClient.SqlParameter updatecommand_sync_row_countParameter =
new System.Data.SqlClient.SqlParameter( " @sync_row_count " , System.Data.SqlDbType.Int);
updatecommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output;
this .UpdateCommand.Parameters.Add(updatecommand_sync_row_countParameter);
}
private void InitializeCommands() {
// 之前是添回和删除SQL对象的设置
// dnt_posts1SyncTableUpdateCommand command.
this .UpdateCommand = new System.Data.SqlClient.SqlCommand();
this .UpdateCommand.CommandText = @" UPDATE dbo.dnt_posts1 SET [fid] = @fid, [tid] = @tid,
[parentid] = @parentid, [layer] = @layer, [poster] = @poster, [posterid] = @posterid,
[title] = @title, [postdatetime] = @postdatetime, [message] = @message, [ip] = @ip,
[lastedit] = @lastedit, [invisible] = @invisible, [usesig] = @usesig, [htmlon] = @htmlon,
[smileyoff] = @smileyoff, [parseurloff] = @parseurloff, [bbcodeoff] = @bbcodeoff,
[attachment] = @attachment, [rate] = @rate, [ratetimes] = @ratetimes, [LastEditDate] =
@LastEditDate, [CreationDate] = @CreationDate WHERE ([pid] = @pid) AND (@sync_force_write = 1
OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount " ;
this .UpdateCommand.CommandType = System.Data.CommandType.Text;
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @fid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @tid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @parentid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @layer " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @poster " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @posterid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @title " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @postdatetime " ,
System.Data.SqlDbType.SmallDateTime));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @message " , System.Data.SqlDbType.NText));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @ip " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @lastedit " , System.Data.SqlDbType.NVarChar));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @invisible " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @usesig " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @htmlon " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @smileyoff " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @parseurloff " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @bbcodeoff " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @attachment " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @rate " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @ratetimes " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @LastEditDate " , System.Data.SqlDbType.DateTime));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @CreationDate " , System.Data.SqlDbType.DateTime));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @pid " , System.Data.SqlDbType.Int));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @sync_force_write " , System.Data.SqlDbType.Bit));
this .UpdateCommand.Parameters.Add( new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " ,
System.Data.SqlDbType.DateTime));
System.Data.SqlClient.SqlParameter updatecommand_sync_row_countParameter =
new System.Data.SqlClient.SqlParameter( " @sync_row_count " , System.Data.SqlDbType.Int);
updatecommand_sync_row_countParameter.Direction = System.Data.ParameterDirection.Output;
this .UpdateCommand.Parameters.Add(updatecommand_sync_row_countParameter);
}
上面代码首先是对命令(SqlCommand)实例的相关属性绑定,包括CommandText,CommandType, Parameters。当然细心的朋友会发现,上面的UpdateCommand.CommandText绑定时在SQL命令行的后半部有如下内容(摘录):
[LastEditDate] = @LastEditDate, [CreationDate] = @CreationDate WHERE ([pid] = @pid)
AND (@sync_force_write = 1 OR ([LastEditDate] <= @sync_last_received_anchor))
SET @sync_row_count = @@rowcount " ;
其中我们看到是 [LastEditDate],[CreationDate]这两个表字段实际上就是在上一篇文章中我们使用同步向导进行设计时所做的相应设置如下:
而@sync_force_write参数与@sync_last_received_anchor参数则是Synchronization Services提供的一组会话变量中的两个,其中@sync_force_write会强制应用由于冲突或错误而未能应用的更改(强制写
入服务端数据库),而@sync_last_received_anchor则是将对在上次收到的定位点值(修改时间)之后和新收到的定位点值(修改时间)之前所做的更改进行同步,说白了就是将最近一定修改的时间与当前修改发生的时间进行同步,以便于本次只修改那些介于这两个时间点中间的数据即可。同时在完成本次同步时,将这个变量修改为本次修改的时间(定位点),以便下次同步时做为新的时间依据(定位点)。
入服务端数据库),而@sync_last_received_anchor则是将对在上次收到的定位点值(修改时间)之后和新收到的定位点值(修改时间)之前所做的更改进行同步,说白了就是将最近一定修改的时间与当前修改发生的时间进行同步,以便于本次只修改那些介于这两个时间点中间的数据即可。同时在完成本次同步时,将这个变量修改为本次修改的时间(定位点),以便下次同步时做为新的时间依据(定位点)。
当然这种类型的变量的主要用场就是在同步期间通过它们将值传递给SyncAdapter 命令。这些变量的指定方式与 ADO.NET 命令中查询或存储过程的其他参数相似(上面代码中已给出)。
当然Synchronization Services还提供了一些别的变量,这里为了大家开发方便,直接罗列一下了。
会话变量:sync_client_id、sync_client_id_hash 和 sync_originator_id
用法: 用于标识当前正在同步的客户端。ID 通常用于冲突检测,并防止在双向同步过程中将更改发送回客户端。有关更多信息,请参见如何:在客户端和服务器间交换双向增量数据更改。
默认情况下,Synchronization Services 用 GUID 标识每个客户端,而 GUID 由 sync_client_id 返回。此外,还可以创建 ID 的散列值并在查询中使用 sync_client_id_hash;或者将 GUID 映射到一个整数值并使用 sync_originator_id。
会话变量:sync_last_received_anchor, sync_new_received_anchor
用法: 用于定义在会话期间要同步的更改集。在当前同步期间,为 SelectNewAnchorCommand 属性指定的命令提供一个新的定位点值。将对在上次收到的定位点值之后和新收到的定位点值之前所做的更改进行同步。然后,存储新收到的定位点并在下一次同步时将其用作上一次收到的定位点值。
会话变量:sync_force_write
用法: 与 RetryWithForceWrite 的 ApplyAction 一起使用,强制应用由于冲突或错误而未能应用的更改。
会话变量:sync_row_count
用法:返回服务器上受上一次操作影响的行数。在 SQL Server 数据库中,@@ROWCOUNT 提供此变量的值。行数为 0 指示操作失败,通常是由于冲突或错误。
会话变量:sync_initialized
用法:返回一个值,指示当前同步是初始同步(值为 0)还是后续同步(值为 1)。
会话变量:sync_table_name 和sync_group_name
用法:当必须在查询中指定表名或组名时使用。
会话变量:sync_batch_count、sync_batch_size 和 sync_max_received_anchor
用法:当进行批量更改时使用。有关更多信息,请参见如何:指定更改的顺序和批大小。
会话变量:sync_session_id
用法:返回标识当前同步会话的 GUID 值。
用法: 用于标识当前正在同步的客户端。ID 通常用于冲突检测,并防止在双向同步过程中将更改发送回客户端。有关更多信息,请参见如何:在客户端和服务器间交换双向增量数据更改。
默认情况下,Synchronization Services 用 GUID 标识每个客户端,而 GUID 由 sync_client_id 返回。此外,还可以创建 ID 的散列值并在查询中使用 sync_client_id_hash;或者将 GUID 映射到一个整数值并使用 sync_originator_id。
会话变量:sync_last_received_anchor, sync_new_received_anchor
用法: 用于定义在会话期间要同步的更改集。在当前同步期间,为 SelectNewAnchorCommand 属性指定的命令提供一个新的定位点值。将对在上次收到的定位点值之后和新收到的定位点值之前所做的更改进行同步。然后,存储新收到的定位点并在下一次同步时将其用作上一次收到的定位点值。
会话变量:sync_force_write
用法: 与 RetryWithForceWrite 的 ApplyAction 一起使用,强制应用由于冲突或错误而未能应用的更改。
会话变量:sync_row_count
用法:返回服务器上受上一次操作影响的行数。在 SQL Server 数据库中,@@ROWCOUNT 提供此变量的值。行数为 0 指示操作失败,通常是由于冲突或错误。
会话变量:sync_initialized
用法:返回一个值,指示当前同步是初始同步(值为 0)还是后续同步(值为 1)。
会话变量:sync_table_name 和sync_group_name
用法:当必须在查询中指定表名或组名时使用。
会话变量:sync_batch_count、sync_batch_size 和 sync_max_received_anchor
用法:当进行批量更改时使用。有关更多信息,请参见如何:指定更改的顺序和批大小。
会话变量:sync_session_id
用法:返回标识当前同步会话的 GUID 值。
看到这里,大家也就清楚了,所谓“只同步新更改和增量更改”只是在 CUD这类SQLCOMAND中加入相应的判断条件即完成了相应的数据操作了,这的确要比“整表”同步(下面我们会看到)在效率速度上要高多了。
当然,除了必要的CUD操作,同步设计器还会为我们生成一些其它的SQL命令,比如说:
SelectConflictDeletedRowsCommand: 获取或设置查询或存储过程,用于标识与其他更改相冲突的已删除行。 在本DEMO中的部分设置如下:
this
.SelectConflictDeletedRowsCommand
=
new
System.Data.SqlClient.SqlCommand();
this .SelectConflictDeletedRowsCommand.CommandText = "" ;
this .SelectConflictDeletedRowsCommand.CommandType = System.Data.CommandType.Text;
this .SelectConflictDeletedRowsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @pid " , System.Data.SqlDbType.Int));
this .SelectConflictDeletedRowsCommand.CommandText = "" ;
this .SelectConflictDeletedRowsCommand.CommandType = System.Data.CommandType.Text;
this .SelectConflictDeletedRowsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @pid " , System.Data.SqlDbType.Int));
SelectConflictUpdatedRowsCommandl: 获取或设置查询或存储过程,用于标识与其他更改相冲突的已更新行。在本DEMO中的部分设置如下:
this
.SelectConflictUpdatedRowsCommand
=
new
System.Data.SqlClient.SqlCommand();
this .SelectConflictUpdatedRowsCommand.CommandText = @" SELECT [pid], [fid], [tid], [parentid],
[layer], [poster], [posterid], [title], [postdatetime], [message], [ip], [lastedit],
[invisible], [usesig], [htmlon], [smileyoff], [parseurloff], [bbcodeoff], [attachment],
[rate], [ratetimes], [LastEditDate], [CreationDate] FROM dbo.dnt_posts1 WHERE ([pid] = @pid) " ;
this .SelectConflictUpdatedRowsCommand.CommandType = System.Data.CommandType.Text;
this .SelectConflictUpdatedRowsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @pid " , System.Data.SqlDbType.Int));
this .SelectConflictUpdatedRowsCommand.CommandText = @" SELECT [pid], [fid], [tid], [parentid],
[layer], [poster], [posterid], [title], [postdatetime], [message], [ip], [lastedit],
[invisible], [usesig], [htmlon], [smileyoff], [parseurloff], [bbcodeoff], [attachment],
[rate], [ratetimes], [LastEditDate], [CreationDate] FROM dbo.dnt_posts1 WHERE ([pid] = @pid) " ;
this .SelectConflictUpdatedRowsCommand.CommandType = System.Data.CommandType.Text;
this .SelectConflictUpdatedRowsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @pid " , System.Data.SqlDbType.Int));
SelectIncrementalInsertsCommand: 获取或设置查询或存储过程,用于检索自上次同步之后在服务器数据库中进行的插入。本DEMO中的部分设置如下:
this
.SelectIncrementalInsertsCommand
=
new
System.Data.SqlClient.SqlCommand();
this .SelectIncrementalInsertsCommand.CommandText = @" SELECT [pid], [fid], [tid], [parentid],
[layer], [poster], [posterid], [title], [postdatetime], [message], [ip], [lastedit],
[invisible], [usesig], [htmlon], [smileyoff], [parseurloff], [bbcodeoff], [attachment],
[rate], [ratetimes], [LastEditDate], [CreationDate] FROM dbo.dnt_posts1 WHERE ([CreationDate] >
@sync_last_received_anchor AND [CreationDate] <= @sync_new_received_anchor) " ;
this .SelectIncrementalInsertsCommand.CommandType = System.Data.CommandType.Text;
this .SelectIncrementalInsertsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalInsertsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalInsertsCommand.CommandText = @" SELECT [pid], [fid], [tid], [parentid],
[layer], [poster], [posterid], [title], [postdatetime], [message], [ip], [lastedit],
[invisible], [usesig], [htmlon], [smileyoff], [parseurloff], [bbcodeoff], [attachment],
[rate], [ratetimes], [LastEditDate], [CreationDate] FROM dbo.dnt_posts1 WHERE ([CreationDate] >
@sync_last_received_anchor AND [CreationDate] <= @sync_new_received_anchor) " ;
this .SelectIncrementalInsertsCommand.CommandType = System.Data.CommandType.Text;
this .SelectIncrementalInsertsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalInsertsCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime));
SelectIncrementalDeletesCommand: 获取或设置查询或存储过程,用于检索自上次同步之后在服务器数据库中进行的删除。在本DEMO中的部分设置如下:
this
.SelectIncrementalDeletesCommand
=
new
System.Data.SqlClient.SqlCommand();
this .SelectIncrementalDeletesCommand.CommandText = " SELECT [pid], [DeletionDate] FROM
dbo.dnt_posts1_Tombstone WHERE (@sync_initializ " +
" ed = 1 AND [DeletionDate] > @sync_last_received_anchor AND [DeletionDate] <= @sy " +
" nc_new_received_anchor) " ;
this .SelectIncrementalDeletesCommand.CommandType = System.Data.CommandType.Text;
this .SelectIncrementalDeletesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_initialized " , System.Data.SqlDbType.Bit));
this .SelectIncrementalDeletesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalDeletesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalDeletesCommand.CommandText = " SELECT [pid], [DeletionDate] FROM
dbo.dnt_posts1_Tombstone WHERE (@sync_initializ " +
" ed = 1 AND [DeletionDate] > @sync_last_received_anchor AND [DeletionDate] <= @sy " +
" nc_new_received_anchor) " ;
this .SelectIncrementalDeletesCommand.CommandType = System.Data.CommandType.Text;
this .SelectIncrementalDeletesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_initialized " , System.Data.SqlDbType.Bit));
this .SelectIncrementalDeletesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalDeletesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime));
SelectIncrementalUpdatesCommand: 获取或设置查询或存储过程,用于检索自上次同步之后在服务器数据库中进行的更新。在本DEMO中的部分设置如下:
this
.SelectIncrementalUpdatesCommand
=
new
System.Data.SqlClient.SqlCommand();
this .SelectIncrementalUpdatesCommand.CommandText = @" SELECT [pid], [fid], [tid], [parentid],
[layer], [poster], [posterid], [title], [postdatetime], [message], [ip], [lastedit], [invisible],
[usesig], [htmlon], [smileyoff], [parseurloff], [bbcodeoff], [attachment], [rate], [ratetimes],
[LastEditDate], [CreationDate] FROM dbo.dnt_posts1 WHERE ([LastEditDate] > @sync_last_received_anchor AND [LastEditDate] <= @sync_new_received_anchor AND [CreationDate] <= @sync_last_received_anchor) " ;
this .SelectIncrementalUpdatesCommand.CommandType = System.Data.CommandType.Text;
this .SelectIncrementalUpdatesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalUpdatesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalUpdatesCommand.CommandText = @" SELECT [pid], [fid], [tid], [parentid],
[layer], [poster], [posterid], [title], [postdatetime], [message], [ip], [lastedit], [invisible],
[usesig], [htmlon], [smileyoff], [parseurloff], [bbcodeoff], [attachment], [rate], [ratetimes],
[LastEditDate], [CreationDate] FROM dbo.dnt_posts1 WHERE ([LastEditDate] > @sync_last_received_anchor AND [LastEditDate] <= @sync_new_received_anchor AND [CreationDate] <= @sync_last_received_anchor) " ;
this .SelectIncrementalUpdatesCommand.CommandType = System.Data.CommandType.Text;
this .SelectIncrementalUpdatesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_last_received_anchor " , System.Data.SqlDbType.DateTime));
this .SelectIncrementalUpdatesCommand.Parameters.Add(
new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime));
因为语句很简单,这里不一一介绍了。更多信息大家可以查看SDK中的文档即可。
说到这里,关于dnt_posts1SyncAdapter这个服务器端的数据Adapter操作类就介绍的差不多了。下面是BiDirectSyncDataServerSyncProvider类,其实这两个服务器端类的关系是一个封装引用关系,因为BiDirectSyncDataServerSyncProvider类中提供了对dnt_posts1SyncAdapter的属性引用,所以如果我们有对dnt_posts1SyncAdapter的访问操作,最好通过这个类进行访问。我们可以通过对该类的构造方法来大致了解一下这个类的工作流程:
public
BiDirectSyncDataServerSyncProvider() {
string connectionString = global ::MSF_WinFormDemo.Properties.Settings.Default.ServertestConnectionString;
this .InitializeConnection(connectionString);
this .InitializeSyncAdapters();
this .InitializeNewAnchorCommand();
this .OnInitialized();
}
string connectionString = global ::MSF_WinFormDemo.Properties.Settings.Default.ServertestConnectionString;
this .InitializeConnection(connectionString);
this .InitializeSyncAdapters();
this .InitializeNewAnchorCommand();
this .OnInitialized();
}
从代码中可以看到,其构造方法首先会调用config文件中的设置来初始化一个SQL链接如下:
this
.Connection
=
new
System.Data.SqlClient.SqlConnection(connectionString);
然后就是对相应的Adapter进行初始化了,代码如下:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeSyncAdapters() {
this ._dnt_posts1SyncAdapter = new dnt_posts1SyncAdapter();
this .SyncAdapters.Add( this ._dnt_posts1SyncAdapter);
}
private void InitializeSyncAdapters() {
this ._dnt_posts1SyncAdapter = new dnt_posts1SyncAdapter();
this .SyncAdapters.Add( this ._dnt_posts1SyncAdapter);
}
紧接着就是对之前所说的Synchronization Services进行初始化:
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitializeNewAnchorCommand() {
this .SelectNewAnchorCommand = new System.Data.SqlClient.SqlCommand();
this .SelectNewAnchorCommand.CommandText = " Select @sync_new_received_anchor = GETUTCDATE() " ;
this .SelectNewAnchorCommand.CommandType = System.Data.CommandType.Text;
System.Data.SqlClient.SqlParameter selectnewanchorcommand_sync_new_received_anchorParameter = new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime);
selectnewanchorcommand_sync_new_received_anchorParameter.Direction = System.Data.ParameterDirection.Output;
this .SelectNewAnchorCommand.Parameters.Add(selectnewanchorcommand_sync_new_received_anchorParameter);
}
private void InitializeNewAnchorCommand() {
this .SelectNewAnchorCommand = new System.Data.SqlClient.SqlCommand();
this .SelectNewAnchorCommand.CommandText = " Select @sync_new_received_anchor = GETUTCDATE() " ;
this .SelectNewAnchorCommand.CommandType = System.Data.CommandType.Text;
System.Data.SqlClient.SqlParameter selectnewanchorcommand_sync_new_received_anchorParameter = new System.Data.SqlClient.SqlParameter( " @sync_new_received_anchor " , System.Data.SqlDbType.DateTime);
selectnewanchorcommand_sync_new_received_anchorParameter.Direction = System.Data.ParameterDirection.Output;
this .SelectNewAnchorCommand.Parameters.Add(selectnewanchorcommand_sync_new_received_anchorParameter);
}
这里要注意的是 "Select @sync_new_received_anchor = GETUTCDATE()" 这一句,因为这里使用的是GETUTCDATE(),即以UTC 时间(通用协调时间或格林尼治标准时间)表示的系统当前日期。所以在本地数据库中的相应的[LastEditDate],[CreationDate]字段中的数据都比我机器上的时间早个小时(因为我的机器时间设置在“东八区”) 。这里可修改为GETDATE()即可。
好了,聊到这里,今天的内容就差不多了,相应大家对“只同步新更改和增量更改”这种同步方式所生成的代码清楚了一些。下面再简要说明一下SnapShot(快照方式)方式的生成文件(SyncSnapData.sync)。
相比较“只同步新更改和增量更改”方式,快照方式的结构与其大同小异。区别主要体现在了服务器端的SyncAdapter类上。大家可以看一下源码中的dnt_topicsSyncAdapter类的InitializeCommands方法即可,因为快照方式不用创建[LastEditDate],[CreationDate] 等用于比较差异的字段。所以在查询条件上(where)要精简了不少(没有了关于[LastEditDate] , [CreationDate]等字段的比较和更新操作)。另外就是其只有SelectIncrementalInsertsCommand命令对象而没有其余的几个对象(SelectConflictDeletedRowsCommand,
SelectIncrementalUpdatesCommand等,因为整表同步时用不上了)。
SelectIncrementalUpdatesCommand等,因为整表同步时用不上了)。
好了,今天的内部代码很多,但对照ado.net同步框架图,相应大家心里已经有数了。其余在MSF设计向导所生成的代码中,还包括一类文件,即相应表的数据集(dataset)文件。这类文件中包括了对客户端数据进行操作(CRUD)的一些通用方法(本地Adapter提供),以及序列化,数据集的Clone,相应表的实体类对象(TypedTableBase类型),本地事务绑定等,并最终以TableAdapterManager类的方式进行相关实例封装和绑定。相关内容可以参数DEMO中的如下即可:
SqlCeDB\BiDirectSynceDataSet.Designer.cs文件
SqlCeDB\LocalDataSet_Topic.Designer.cs文件
本文转自 daizhenjun 51CTO博客,原文链接:http://blog.51cto.com/daizhj/124348,如需转载请自行联系原作者