使用 Microsoft Synchronization Services for ADO.NET 脱机处理数据






4.88/5 (61投票s)
使用 Microsoft Synchronization Services for ADO.NET 构建偶尔连接的应用程序
引言
当人们可以随时随地工作,而不会收到“无法连接”或“找不到服务器”之类的烦人消息时,他们的工作效率会更高。问问 Microsoft Outlook 用户就知道了,他们多么喜欢脱机模式。应用程序开发人员普遍认为,让他们的应用程序能够脱机工作是为客户增值且具有吸引力的主张。然而,在开发可靠的同步算法的同时,构建和管理本地缓存所面临的挑战是主要担忧,也是产品发布计划的潜在风险。事实是:开发人员的担忧并非夸大其词!
Microsoft Synchronization Services for ADO.NET 是一个新颖的数据同步框架,它使开发人员能够使用熟悉的概念构建断开连接的数据应用程序。在这篇文档中,我将快速概述新的同步框架是什么,以及如何使用它来构建偶尔连接的应用程序。
背景
我在 Microsoft 工作了五年多,专注于同步和复制技术。我必须承认,我从未对新的同步技术感到如此兴奋,就像我现在对 Synchronization Services 框架一样。原因很简单:这项技术是面向开发人员的,而不是一种针对管理员量身定制的端到端解决方案。无论如何,我认为写一篇关于这个新框架的入门文档并将其发布到 The Code Project 是个好主意。毕竟,你们是这个 API 的未来用户,你们的反馈是最重要的。
免责声明
本文档仅供参考。MICROSOFT 不对本文档中的信息作出任何明示、暗示或法定的保证。遵守所有适用的版权法是用户的责任。在不限制版权权利的情况下,未经 Microsoft Corporation 明确书面许可,不得以任何形式或通过任何方式(电子、机械、影印、录制或其他方式)复制、存储在检索系统中或传输本文档的任何部分,或将其用于任何目的。Microsoft 可能拥有涵盖本文档内容的专利、专利申请、商标、版权或其他知识产权。除非 Microsoft 的任何书面许可协议中有明确规定,否则提供本文档并不授予您对这些专利、商标、版权或其他知识产权的任何许可。
© 2007 Microsoft Corporation。保留所有权利。
Microsoft、MS-DOS、Windows、Windows Server、Windows Vista、SQL Server、Visual Studio 是 Microsoft Corporation 在美国和/或/或其他国家/地区注册的商标或商标。所有其他商标均为其各自所有者的财产。
下载 CTP 和文档
Synchronization Services for ADO.NET Beta 2 可在此处获取:这里。请注意,该软件包包含 SQL CE 版本 3.5。文档可在单独的软件包中找到;您可以在这里找到。
同步服务概述

ADO.NET 引入了一组断开连接的对象,允许您在从服务器获取数据后脱机查看数据。此模型中的关键是 DataSet
对象,它封装了许多其他断开连接的对象,如 DataTable
、DataColumn
等。Synchronization Services for ADO.NET 通过在客户端提供持久缓存来扩展此模型。由于数据存储在客户端计算机的磁盘上,因此客户端可以在脱机状态以及应用程序重新启动之间查看数据并进行修改。客户端和服务器上的数据更改通过数据库触发器或通过内置支持(如 SQL CE 的情况)进行跟踪。通过使用 Sync Services API,可以交换客户端和服务器上的增量更改,从而使客户端缓存与服务器同步。上面的图显示了同步框架的不同组件。下面是对每个组件的简要描述。
SyncAgent
Sync Agent 是同步的指挥官。通常,它位于客户端,并根据客户端应用程序的请求执行操作。该代理接受两个同步提供程序,一个用于客户端数据库,另一个用于服务器数据库。
SyncTable
定义客户端希望同步的表。这包括更改流动的期望方向,即 Snapshot
、Downloadonly
、Uploadonly
或 Bidirectional
。SyncTable
对象允许您指定首选的表创建选项。当在第一次同步中建立同步关系时,同步运行时将检查每个表的创建选项。然后,它决定如何继续表的架构创建。如有必要,代理将从服务器下载架构定义并将其应用于本地数据库。
ClientSyncProvider
Client Sync Provider 抽象化了客户端的底层数据存储。Sync Services 提供了用于 SQL Server CE 的 ClientSyncProvider
。SQL CE 是一个轻量级的进程内数据库,占用的空间约 2MB。在设置客户端方面,您无需担心任何事情。正如您将在示例应用程序中看到的,表的架构和所有同步跟踪基础结构都由同步运行时为您设置。
ServerSyncProvider
Server Sync Provider 是您将编写命令的地方。Sync Services 提供了针对关系存储量身定制的 DbServerSyncProvider
。它允许您与 ADO.NET 支持的任何数据库进行交互。对于您希望同步的每个表,都需要一个 SyncAdapter
对象。SyncAdapter
(如下文详细介绍)是同步数据库命令所在的位置。该提供程序还公开了两个额外的命令。
SelectNewAnchorCommand
- 服务器提供程序使用此命令获取新的同步标记。此命令获取的新锚点以及从正在同步的客户端获取的旧锚点定义了枚举新增量更改的窗口。SelectClientIdCommand
- 在构建双向同步应用程序时,每一行都需要存储哪个客户端对其进行了最后一次更改。正如您稍后将看到的,此信息是必需的。服务器提供程序使用此命令将客户端 GUID 映射到整数类型的 ID。每个正在同步的客户端都通过存储在客户端的 GUID 来标识自己。使用此命令,您可以构建从 GUID 到 int 的简单映射。这通常是为了节省空间。但是,如果您愿意,您也可以使用 GUID 本身,并使此命令为空。
SyncAdapter
SyncAdapter
模仿 DataAdapter
,但为同步过程提供了一组不同的数据库命令。
InsertCommand
- 服务器提供程序使用此命令将插入操作应用于服务器数据库。UpdateCommand
- 服务器提供程序使用此命令将更新操作应用于服务器数据库。DeleteCommand
- 服务器提供程序使用此命令将删除操作应用于服务器数据库。SelectIncrementalInsertsCommand
- 服务器提供程序使用此命令枚举自客户端上次同步以来在服务器数据库中发生的插入操作。SelectIncrementalUpdatesCommand
- 服务器提供程序使用此命令枚举自客户端上次同步以来在服务器数据库中发生的更新操作。SelectIncrementalDeletesCommand
- 服务器提供程序使用此命令枚举自客户端上次同步以来在服务器中发生的删除操作。SelectConflictUpdatedRowsCommand
- 服务器提供程序使用此命令来获取导致插入或更新命令失败的现有行。此命令将在基表中查找冲突行。SelectConflictDeletedRowsCommand
- 服务器提供程序使用此命令来获取导致插入、更新或删除命令失败的现有行。此命令将在“墓碑”表中查找冲突行。
尽管 SyncAdapter
比 DataAdapter
有更多的命令,但您不必担心,因为并非所有命令都必需。例如,如果您正在实现仅下载场景,则只需要三个 SelectIncremental
命令。此外,如果您只想快速入门,SqlSyncAdapterBuilder
可以在运行时即时生成适配器!当然,有一些限制。
部署方案
同步服务可以部署在不同的设置中。
两层部署
在两层设置中,ClientSyncProvider
、SyncAgent
和 ServerSyncProvider
运行在同一个进程或节点上。在这种情况下,应用程序将直接连接到后端数据库。
N 层部署
在 N 层设置中,如图所示,ClientSyncProvider
和 SyncAgent
在客户端的同一进程中运行。ServerSyncProvider
驻留在中间层。同步代理是一个轻量级包装器,它实现了 ServerSyncProvider
接口,以便无缝地插入 SyncAgent
并进行调用转发到 Web 服务。
示例应用程序概述
让我们开始使用同步服务。在这个示例中,我正在开发面向现场销售人员的业务线应用程序 (LOB)。我希望他们能够随时随地输入订单,使用他们的笔记本电脑。一天结束时,每位销售代表都会连接到公司网络,以同步他们的本地更改,并接收其他销售代表所做的订单。也就是说,我需要双向同步。为简单起见,我选择只关注两个表:order
和 order_details
。表的架构如下:
CREATE DATABASE pub
GO
CREATE TABLE pub..orders(
order_id int NOT NULL primary key,
order_date datetime NULL)
GO
CREATE TABLE pub..order_details(
order_id int NOT NULL primary key,
order_details_id int NOT NULL,
product nvarchar(100) NULL,
quantity int NULL)
GO
启用更改跟踪
再次快速查看 orders
和 order_details
表定义。我无法将增量更改向下同步到客户端。我需要能够编写简单的 SELECT 语句来枚举自客户端上次同步以来发生的更改。为此,我需要添加额外的列来跟踪更改,以便我能够轻松地识别客户端尚未看到的更改。
跟踪插入和更新
要跟踪插入和更新,我需要以下列:
create_timestamp
:此列指示行的创建时间。它的类型是bigint
,因为每个表只允许一列为timestamp
类型,我将其保留给下一列。我使用代码下方所示的默认值。update_timestamp
:此列反映上次更新的时间戳。它由 SQL Server 引擎自动更新。update_originator_id
:这是一个整数列,用于标识谁进行了最后一次更改。我给server_id
赋值为0
。每个客户端将有自己的 ID。此列有非常充分的理由。假设一个客户端刚刚上传了它的更改,然后去下载服务器更改。如果没有 ID 列,刚刚上传的相同更改将返回到客户端。我的增量 SELECT 语句无法区分此客户端更改与其他客户端的更改。通过使用update_orginator_id
列,我可以选择其他客户端或服务器本身所做的更改。
-- Add create timestamp column (use bigint since only one timestamp
-- column type is allowed per table)
ALTER TABLE pub..orders add create_timestamp bigint default @@DBTS+1
ALTER TABLE pub..order_details add create_timestamp bigint default @@DBTS+1
GO
-- Add last update timestamp column
ALTER TABLE pub..orders add update_timestamp timestamp
ALTER TABLE pub..order_details add update_timestamp timestamp
GO
-- Create update_originator_id column defaulted to 0 to indicate server change
ALTER TABLE pub..orders add update_originator_id int null default 0
ALTER TABLE pub..order_details add update_originator_id int null default 0
GO
设置和更新跟踪列的值是另一回事。create_timestamp
值仅在插入时设置,默认约束应处理它。update_timestamp
的类型是 timestamp
。这意味着 SQL Server 会维护插入和更新的值。
但是,update_originator_id
需要一些工作。请注意,同步运行时会处理 originator_id
列的更新,但如果更改是在同步之外进行的(即,用户启动 SQL Management Studio 并进行了一些更改),那么我们需要在后台进行一些修复。期望的行为是在插入新行时将其值设置为 server_id
。默认约束可以处理这种情况。如果更新了某一行,那么我们只需要在更改不是由同步运行时进行的情况下,将 originator_id
重置为 server_id
。为了实现这一点,我需要每个表的 update
触发器,如代码所示。
-- Create Update Triggers
-- Since there will be changes on the server outside of sync application
-- we need triggers to fix up update_originator_id back to 0 which
-- designated for server change
USE pub
GO
CREATE TRIGGER orders_update_trigger on orders FOR UPDATE
AS
-- Note: sync is the only app making changes to update_originator_id,
-- thus we only overwrite the value if it was not set by sync
UPDATE o
SET o.update_originator_id = 0
FROM [orders] o JOIN [inserted] i on o.order_id = i.order_id
WHERE NOT UPDATE(update_originator_id)
GO
CREATE TRIGGER order_details_update_trigger ON order_details FOR UPDATE
AS
-- Note: sync is the only app making changes to update_originator_id,
-- thus we only overwrite the value if it was not set by sync
UPDATE o
SET o.update_originator_id = 0
FROM [order_details] o JOIN [inserted] i on o.order_id = i.order_id
WHERE NOT UPDATE(update_originator_id)
GO
跟踪删除
在处理完插入和更新的跟踪后,删除怎么办?对于删除,我需要一个“tombstone
”表来存储已删除的行。Tombstone
是同步和复制文献中常用的术语。当然,您仍然可以称之为“已删除表”或任何对您有效的名称。以下是创建 tombstone
表的 TSQL 语句。
-- Create tombstone tables to store deletes
CREATE TABLE pub..orders_tombstone(
order_id int NOT NULL primary key,
order_date datetime NULL,
update_originator_id int default 0,
update_timestamp timestamp,
create_timestamp bigint)
CREATE TABLE pub..order_details_tombstone(
order_id int NOT NULL primary key,
order_details_id int NOT NULL,
product nvarchar(100) NULL,
quantity int NULL,
update_originator_id int default 0,
update_timestamp timestamp,
create_timestamp bigint)
GO
虽然我只需要存储 PK 来标识已删除的行,但我更喜欢复制整行。我的理由是,稍后,当我实现冲突检测和解决逻辑时(在此示例中未显示;请参阅我的博客),我想将已删除的行表示给最终用户以解决冲突。仅显示 PK 帮助不大。请注意,添加到基表中的相同跟踪列也在 tombstone
表中创建。对于下一步——我打赌您已经猜到了——我们需要一个 delete
触发器将删除操作复制到 tombstone
表中。这是它的样子。
-- Create Delete Triggers
CREATE TRIGGER orders_delete_trigger ON orders FOR DELETE
AS
INSERT INTO into pub..orders_tombstone (order_id, order_date, _
create_timestamp, update_originator_id)
SELECT order_id, order_date, create_timestamp, 0 FROM DELETED
GO
CREATE TRIGGER order_details_delete_trigger ON order_details FOR DELETE
AS
INSERT INTO pub..order_details_tombstone (order_id, order_details_id,_
product, quantity, create_timestamp, update_originator_id)
SELECT order_id, order_details_id, product, quantity, create_timestamp,_
0 FROM DELETED
GO
这样,更改跟踪就完成了,我们可以进行双向同步了。
代码演练
在这个示例中,我正在构建一个两层脱机应用程序。这意味着客户端将直接连接到服务器数据库以执行查询。为跟踪更改而对后端表进行的更改完成了服务器端工作。现在我们转向客户端。
初始化
在两层设置中,SyncAgent
、ClientSyncProvider
和 ServerSyncProvider
将在客户端应用程序中运行。下面的初始化代码显示了这三个组件的连接。
// 1. Create an instance of the SyncAgent
SyncAgent syncAgent = new SyncAgent();
// 2. Prepare server db connection and attach it to the sync agent
DbServerSyncProvider serverSyncProvider = new DbServerSyncProvider();
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder["Data Source"] = textServerMachine.Text;
builder["integrated Security"] = true;
builder["Initial Catalog"] = "pub";
SqlConnection serverConnection = new SqlConnection(builder.ConnectionString);
serverSyncProvider.Connection = serverConnection;
SyncAgent.ServerSyncProvider = serverSyncProvider;
// 3. Prepare client db connection and attach it to the sync provider
string connString = "Data Source=" + dbPathTextBox.Text;
if (false == File.Exists(dbPathTextBox.Text))
{
SqlCeEngine clientEngine = new SqlCeEngine(connString);
clientEngine.CreateDatabase();
clientEngine.Dispose();
}
SqlCeClientSyncProvider clientSyncProvider =
new SqlCeClientSyncProvider(connString);
syncAgent.ClientSyncProvider = (ClientSyncProvider)clientSyncProvider;
配置同步表
要配置一个表以进行同步,需要创建一个 SyncTable
对象并设置所需的属性。
TableCreationOption
告诉代理如何初始化本地数据库中的新表。在下面的代码中,我选择了DropExistingOrCreateNewTable
来指示运行时删除客户端数据库中已存在的表(如果有),并创建一个新表。此过程仅在第一次同步时发生。SyncDirection
是更改相对于客户端的流向 {Download
、Upload
、Bidirectional
或Snapshot
}
要一次性同步两个表的更改,我需要使用一个通用的 SyncGroup
对象。如果表具有 PK-FK 关系,这一点很重要。分组将确保在应用 PK 之前不会应用 FK 更改。
// 4. Create SyncTables and SyncGroups
SyncTable tableOrders = new SyncTable("orders");
tableOrders.CreationOption =
TableCreationOption.DropExistingOrCreateNewTable;
tableOrders.SyncDirection = SyncDirection.Bidirectional;
SyncTable tableOrderDetails = new SyncTable("order_details");
tableOrderDetails.CreationOption =
TableCreationOption.DropExistingOrCreateNewTable;
tableOrderDetails.SyncDirection = SyncDirection.Bidirectional;
SyncGroup orderGroup = new SyncGroup("AllChanges");
tableOrders.SyncGroup = orderGroup;
tableOrderDetails.SyncGroup = orderGroup;
syncAgent.SyncTables.Add(tableOrders);
syncAgent.SyncTables.Add(tableOrderDetails);
配置同步适配器
在此步骤中,我需要为 orders
和 order_details
表构建一个 SyncAdapter
,并将其附加到 DbServerSyncProvider
实例。在这里,我将利用每个表中的跟踪列。虽然我可以手动编写每个适配器,但我将使用 SqlSyncAdapterBuilder
来完成。我所需要做的就是向生成器提供跟踪列的名称以及 tombstone
表名,它将为我生成一个 SyncAdapter
。下面的代码显示了这两个表的此过程。
虽然 SqlSyncAdapterBuilder
是一个快速入门的好工具,但它不适合生产代码使用,因为它会额外调用后端数据库来了解表的架构和数据类型。此过程在每次同步时都会重复,增加了不必要的开销。
// 5. Create sync adapter for each sync table and attach it to the server
// provider
SqlSyncAdapterBuilder ordersBuilder = new SqlSyncAdapterBuilder();
ordersBuilder.Connection = serverConnection;
ordersBuilder.SyncDirection = SyncDirection.Bidirectional;
// base table
ordersBuilder.TableName = "orders";
ordersBuilder.DataColumns.Add("order_id");
ordersBuilder.DataColumns.Add("order_date");
// tombstone table
ordersBuilder.TombstoneTableName = "orders_tombstone";
ordersBuilder.TombstoneDataColumns.Add("order_id");
ordersBuilder.TombstoneDataColumns.Add("order_date");
// tracking\sync columns
ordersBuilder.CreationTrackingColumn = "create_timestamp";
ordersBuilder.UpdateTrackingColumn = "update_timestamp";
ordersBuilder.DeletionTrackingColumn = "update_timestamp";
ordersBuilder.UpdateOriginatorIdColumn = "update_originator_id";
// fix up anchor data type generated by the builder to use Binary
// instead of int64
SyncAdapter ordersSyncAdapter = ordersBuilder.ToSyncAdapter();
((SqlParameter)ordersSyncAdapter.SelectIncrementalInsertsCommand.Parameters[
"@sync_last_received_anchor"]).DbType = DbType.Binary;
((SqlParameter)ordersSyncAdapter.SelectIncrementalInsertsCommand.Parameters[
"@sync_new_received_anchor"]).DbType = DbType.Binary;
serverSyncProvider.SyncAdapters.Add(ordersSyncAdapter);
SqlSyncAdapterBuilder orderDetailsBuilder = new SqlSyncAdapterBuilder();
orderDetailsBuilder.SyncDirection = SyncDirection.Bidirectional;
orderDetailsBuilder.Connection = serverConnection;
// base table
orderDetailsBuilder.TableName = "order_details";
orderDetailsBuilder.DataColumns.Add("order_id");
orderDetailsBuilder.DataColumns.Add("order_details_id");
orderDetailsBuilder.DataColumns.Add("product");
orderDetailsBuilder.DataColumns.Add("quantity");
// tombstone table
orderDetailsBuilder.TombstoneTableName = "order_details_tombstone";
orderDetailsBuilder.TombstoneDataColumns.Add("order_id");
orderDetailsBuilder.TombstoneDataColumns.Add("order_details_id");
orderDetailsBuilder.TombstoneDataColumns.Add("product");
orderDetailsBuilder.TombstoneDataColumns.Add("quantity");
// tracking\sync columns
orderDetailsBuilder.CreationTrackingColumn = "create_timestamp";
orderDetailsBuilder.UpdateTrackingColumn = "update_timestamp";
orderDetailsBuilder.DeletionTrackingColumn = "update_timestamp";
orderDetailsBuilder.UpdateOriginatorIdColumn = "update_originator_id";
// fix up anchor data type generated by the builder to use Binary
// instead of int64
SyncAdapter orderDetailsSyncAdapter = orderDetailsBuilder.ToSyncAdapter();
((SqlParameter)
orderDetailsSyncAdapter.SelectIncrementalInsertsCommand.Parameters[
"@sync_last_received_anchor"]).DbType = DbType.Binary;
((SqlParameter)
orderDetailsSyncAdapter.SelectIncrementalInsertsCommand.Parameters[
"@sync_new_received_anchor"]).DbType = DbType.Binary;
serverSyncProvider.SyncAdapters.Add(orderDetailsSyncAdapter);
配置锚点和 ClientID 命令
如前所述,DbServerSyncProvider
还有两个命令。让我们看看如何配置它们。
SelectNewAnchorCommand
:返回当前同步的新高水位标记。此值存储在客户端,并在下次同步时用作低水位标记。旧锚点和新锚点值定义了SelectIncremental
命令收集新更改的窗口。返回值必须与服务器上使用的跟踪列类型相同。也就是说,我们需要一个时间戳值。因此,我使用“Select @@DBTS
”来获取数据库的最新时间戳。SelectClientIdCommand
:查找服务器上的客户端 ID。此命令有助于避免下载客户端已进行并已应用于服务器的更改。基本上,它打破了循环。有几种方法可以设置此值,其复杂性各不相同。最简单的方法是将客户端 ID 分配为1
。
// 6. set up provider wide commands
// select new anchor command
SqlCommand anchorCmd = new SqlCommand();
anchorCmd.CommandType = CommandType.Text;
anchorCmd.CommandText =
"Select @" + SyncSession.SyncNewReceivedAnchor + " = @@DBTS";
anchorCmd.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor,
SqlDbType.Timestamp).Direction = ParameterDirection.Output;
serverSyncProvider.SelectNewAnchorCommand = anchorCmd;
// client ID command (give the client id of 1)
// in remote server scenario (middle tear), this command will reference a
// local client table for the ID
SqlCommand clientIdCmd = new SqlCommand();
clientIdCmd.CommandType = CommandType.Text;
clientIdCmd.CommandText = "SELECT @" + SyncSession.SyncOriginatorId + " = 1";
clientIdCmd.Parameters.Add("@" + SyncSession.SyncOriginatorId,
SqlDbType.Int).Direction = ParameterDirection.Output;
serverSyncProvider.SelectClientIdCommand = clientIdCmd;
客户端 ID 1
如果只有一个客户端,工作得很好。这只是为了让您快速入门。但是,如果您需要处理多个客户端,该怎么办?在这种情况下,您需要为每个客户端提供一个唯一的数字。为了做到这一点而不编写大量代码,您可以使用另一个内置的会话参数,称为 sync_client_id
。此参数的类型是 GUID,它在第一次同步初始化时由客户端生成。虽然使用 GUID 作为 update_originator_id
是可能的,但它不是一个很有吸引力的想法,因为它不必要地增加了表的大小。我真正需要的是一个整数,这就是我从一开始就选择它的原因。最佳解决方案是实现一个简单的映射表 {identity
, client_guid
} 并在服务器上维护它。为此,SelectClientIdCommand
将实现以下语义:
- 给定一个 GUID,查找相应的标识符值。
- 如果标识符不存在,则向映射添加新行并返回新分配的标识符。
您明白了。幸运的是,框架提供了一种简单的方法来避免在实验阶段这样做。这是通过传递 GUID 的哈希值来实现的,该值是整数类型。当然,可能会发生哈希冲突,但同样,这仅用于测试目的。代码如下。
SqlCommand clientIdCmd = new SqlCommand();
clientIdCmd.CommandType = CommandType.Text;
clientIdCmd.CommandText =
"SELECT @" + SyncSession.SyncOriginatorId + " = @sync_client_id_hash";
clientIdCmd.Parameters.Add("@sync_client_id_hash", SqlType.Int);
clientIdCmd.Parameters.Add("@" + SyncSession.SyncOriginatorId,
SqlDbType.Int).Direction = ParameterDirection.Output;
serverSyncProvider.SelectClientIdCommand = clientIdCmd;
同步!
现在,我们准备开始同步。在此之前,我想在两个提供程序上订阅 SyncProgressEvent
,以便在同步进行时显示进度对话框。
// 7. Kickoff Sync
clientSyncProvider.SyncProgress += new
EventHandler<SyncProgressEventArgs>(ShowProgress);
serverSyncProvider.SyncProgress += new
EventHandler<SyncProgressEventArgs>(ShowProgress);
SyncStatistics syncStats = syncAgent.Synchronize();
结论
对脱机体验的兴趣正在迅速增长,这得益于新一代的富互联网应用程序。软件即服务(SaaS)的愿景,即应用程序具有 Web 组件和桌面组件,强调了同步作为将这些组件连接在一起的粘合剂的必要性。然而,同步技术尚未普及。很少有开发人员具备构建支持同步的应用程序的知识。借助 Microsoft Synchronization Services for ADO.NET,开发人员有了一个起点,可以深入研究同步算法,并学习如何为他们的数据应用程序添加脱机组件。
下一步
在这篇文档中,我与您分享了为展示新 API 而准备的一系列演示中的第一个。其余的演示发布在我的博客和网站上。每个演示都建立在前一个演示的基础上,并展示了 API 的一项新功能。这是演示系列的快速概述:
- 演示 I:脱机应用程序 - SyncAdapterBuilder 向您展示如何使用
SyncAdapterBuilder
快速入门 - 本文档中介绍的演示。 - 演示 II:脱机应用程序 - TSQL+SProcs 向您展示如何使用 TSQL 语句和存储过程手动编写
SyncAdapter
。 - 演示 III:脱机应用程序 - WebService 向您展示如何使用 Web 服务设置 N 层。
- 演示 IV:脱机应用程序 - Conflict Handling 向您展示如何进行冲突检测和解决。
- 演示 V:脱机应用程序 - Oracle backend 向您展示如何针对 Oracle 数据库使用同步服务。
- 演示 VI:脱机应用程序 - Decoupled Change Tracking 向您展示如何通过使用单独的跟踪表来跟踪更改而不更改基表的架构。
- 演示 VII:脱机应用程序 - Tombstone Cleanup 向您展示如何清理和删除旧的墓碑,并防止过时的客户端同步。
- 演示 VIII:脱机应用程序 - Batching Changes 向您展示如何将大量更改划分为较小的批次,并监控下载更改的进度。
反馈
我很乐意看到大家在公共论坛上发布问题、请求、疑问等。该论坛由同步团队的许多人监控,因此是一种更有效的沟通方式。我也希望通过这篇文章能向您介绍这个新框架。我唯一知道您是否喜欢它的方法就是通过评分。请在您方便的时候投票。您也可以通过我的博客与我联系。
历史
- 2007 年 3 月 15 日:初始发布 - 示例基于 Synchronization Services for ADO.NET CTP 1
- 2007 年 5 月 1 日:修复了
update
触发器。扩展了SelectClientId
逻辑。添加了指向演示 VI 的链接。 - 2007 年 5 月 28 日:更新了项目以与 Beta 1 位一起使用;添加了指向演示 VII 的链接。
- 2007 年 8 月 17 日:更新了项目以与 Beta 2 位一起使用。
- 2007 年 11 月 27 日:更新了 Sync Services for ADO.NET 的最终版本。
- 2008 年 1 月 2 日:添加了指向演示 VIII 的链接。