Microsoft Sync Framework同步数据库 4:如何为数据库同步筛选数据

如何为数据库同步筛选数据

了解筛选器

我们知道,在为数据库准备同步时,我们需要定义同步作用域来描述同步的数据范围,它是对进行同步的对象的一种逻辑分组。对于数据库同步,一个同步作用域通常是一组数据表。但有时候,我们希望在同步作用域的基础上对同步的数据进行筛选,比如,按照销售人员同步订单数据。这个时候,我们就可以使用Sync Framework的筛选器机制来对同步数据进行筛选。

Sync Framework 可以创建两种类型的筛选器:“静态筛选器”和“基于参数的筛选器”。

静态筛选器定义为同步作用域的一部分,并且定义筛选字段的值。静态筛选器在源数据库使用的存储过程中进行编码,以便为作用域枚举变更。定义了某一静态筛选器后,就不能对其进行更改。在本篇的设置数据库一节中,我们已经定义了一个静态筛选器的完整示例。

基于参数的筛选器由一个筛选子句以及映射到同步作用域中表的列的一组参数定义。基于参数的筛选器在两个阶段中定义。第一个阶段定义筛选子句和参数,并且建立与该筛选器相关联的作用域的说明。在这个阶段中,筛选器和作用域仅采用模板格式。第二个阶段为筛选器设置参数值并且根据模板创建同步作用域。在此阶段中创建的作用域是目标提供程序用来与源数据库同步的作用域。用于基于参数的筛选的源数据库可以是 SQL Server 或 SQL Azure 数据库,目标数据库可以是 SQL Server、SQL Azure 或 SQL Server Compact 数据库。

在针对筛选的典型方案中,数据库管理员或应用程序开发人员定义基于参数的筛选器并且为筛选的同步准备服务器数据库。数据库管理员或应用程序开发人员还可以创建简单工具(例如基于 Web 的订阅工具),该工具使用 Sync Framework 对象以便让用户指定其筛选参数值并为同步订阅其客户端数据库。通过创建订阅工具,该数据库管理员不必为单独的用户创建筛选器,而是用户使用该工具指定适合自己的参数值,并且根据需要订阅同步。

创建基于参数的筛选器

通过两个步骤创建基于参数的筛选器。首先,定义筛选器和作用域模板。然后,创建一个经过筛选的作用域,该作用域具有针对筛选器参数的特定值。这个由两个步骤构成的过程具有以下优势:

  • 易于设置。筛选器模板只需定义一次。
  • 易于订阅。客户端指定要创建的参数值并且根据需要订阅经过筛选的作用域。
  • 易于维护。即使在合并若干参数并且创建许多经过筛选的作用域时,维护工作也很简单,因为使用基于参数的单一过程来枚举变更。

定义筛选器模板

创建基于参数的筛选器的第一个步骤是定义一个筛选器模板,以后可以使用该模板创建经过筛选的作用域。筛选器模板存储在源数据库中并且要求创建同步表和存储过程。因此,在源数据库中需要适当的权限。

筛选器模板与同步作用域一起定义。按如下所示为作用域中的表定义筛选器模板:

  • 通过使用 AddFilterColumn,向同步作用域中的 SqlSyncTableProvisioning 对象添加一个筛选器列。这会将该筛选器列添加到用于跟踪基表变更的跟踪表。
  • 通过将 SqlParameter 对象添加到 SqlSyncTableProvisioning 对象的 FilterParameters 集合,定义一个或多个筛选参数。这会将指定的参数添加到在同步过程中枚举变更的存储过程的参数列表中。
  • 添加一个筛选子句,该子句通过设置 SqlSyncTableProvisioning 对象的 FilterClause 属性,定义参数值和列值之间的关系。筛选子句是不带 WHERE 关键字的 WHERE 子句。[side] 别名是该跟踪表的别名。这些参数匹配在 FilterParameters 集合中指定的参数。此时,您只是定义筛选器参数和列之间的关系。以后在创建经过筛选的作用域时将指定参数的实际值。

然后,通过使用 SqlSyncScopeProvisioning 对象的 Apply 方法将筛选器模板和作用域模板应用于源数据库,此时创建适当的同步表和存储过程。

在筛选子句中,别名 [base] 和 [side] 由 Sync Framework 定义。[base] 指代表的基名称,[side] 指代变更跟踪表。例如,基于 CustomerType 列筛选 Customer 表。默认情况下,[base] 是 [Customer] 的别名,[side] 是 [Customer_tracking] 的别名。由于 CustomerType 列在基表和跟踪表中都存在,在筛选子句中必须限定对它的引用,否则会引起歧义,出现错误。还可以使用实际表来替代 [base] 和 [side] 别名,如 [Customer_tracking].[CustomerType] = @customertype。

下面的示例定义一个筛选器模板并且将其应用于源数据库:

 1             // 创建同步作用域模板customertype_template,向其添加两个表
2 DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customertype_template");
3 // 设置一个易于理解的注释
4 scopeDesc.UserComment = "Template for Customer and CustomerContact tables. Customer data is filtered by CustomerType parameter.";
5
6 // 定义作用域中的表
7 DbSyncTableDescription customerDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
8 scopeDesc.Tables.Add(customerDescription);
9 DbSyncTableDescription customerContactDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn);
10 scopeDesc.Tables.Add(customerContactDescription);
11
12 // 创建数据库设置对象以创建同步作用域模板
13 SqlSyncScopeProvisioning serverTemplate = new SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template);
14 serverTemplate.ObjectSchema = "Sync";
15
16 // 为Customer表指定用于过滤数据的列
17 // 为变更跟踪表设置过滤子句,[side]是变更跟踪表的别名
18 // 并且指定过滤参数
19 serverTemplate.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
20 serverTemplate.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = @customertype";
21 SqlParameter param = new SqlParameter("@customertype", SqlDbType.NVarChar, 100);
22 serverTemplate.Tables["Sales.Customer"].FilterParameters.Add(param);
23
24 serverTemplate.Apply();

 

创建经过筛选的作用域

在某一客户端可以使用筛选器与服务器同步前,该客户端必须首先为筛选器参数定义特定值。这个步骤将通过服务器上的筛选器模板来创建经过筛选的作用域,并将此筛选的作用域应用于服务器。

在对服务器数据库指定了经过筛选的作用域后,我们就可以通过调用 GetDescriptionForScope 以便获取该作用域,然后将该作用域应用于客户端数据库,对客户端数据库进行设置。

下面的示例为一个筛选器定义参数值,将该筛选器应用于服务器数据库,并且使用经过筛选的作用域设置客户端数据库以便准备进行同步:

 1             // 为retail customers创建同步作用域"RetailCustomers",并且设置服务器数据库
2 SqlSyncScopeProvisioning serverProvRetail = new SqlSyncScopeProvisioning(serverConn);
3 serverProvRetail.ObjectSchema = "Sync";
4 serverProvRetail.PopulateFromTemplate("RetailCustomers", "customertype_template");
5 serverProvRetail.Tables["Sales.Customer"].FilterParameters["@customertype"].Value = "Retail";
6 serverProvRetail.UserComment = "Customer data includes only retail customers.";
7 serverProvRetail.Apply();
8
9 // 通过上面创建的同步作用域"RetailCustomers"来设置客户端数据库
10 DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("RetailCustomers", null, "Sync", serverConn);
11 SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlConn, clientSqlDesc);
12 clientSqlConfig.ObjectSchema = "Sync";
13 clientSqlConfig.Apply();

 

使用筛选作用域同步客户端

在定义了经过筛选的作用域并且设置了客户端数据库后,可以通过在客户端和服务器数据库中为经过筛选的作用域创建 SqlSyncProvider 对象、将提供程序与 SyncOrchestrator 对象相关联和调用 Synchronize 方法,同步客户端。

下面的示例执行两个数据库的筛选同步:

1             SyncOrchestrator syncOrchestrator = new SyncOrchestrator();
2 syncOrchestrator.LocalProvider = new SqlSyncProvider("RetailCustomers", clientSqlConn, null, "Sync");
3 syncOrchestrator.RemoteProvider = new SqlSyncProvider("RetailCustomers", serverConn, null, "Sync");
4 SyncOperationStatistics syncStats = syncOrchestrator.Synchronize();

 

订阅同步

从上面的介绍中我们可以看到,使用同步作用域模板来同步某一客户端数据库的过程包括:定义筛选器参数值、创建筛选器作用域并应用于服务器数据库以及设置客户端数据库。试想,如果我们需要为每个不同的参数值来创建筛选器作用域、设置服务器数据库、设置客户端数据库,那将会一件非常无聊而繁重的任务。当然,我们可以想出相应的解决方案,来让这些步骤自动化,这个解决方案就是创建同步订阅服务。

用于定义筛选器参数值、将新指定的筛选器应用于服务器数据库以及设置客户端数据库的代码可以轻松地封装在单独的工具中,该工具收集来自某一用户的筛选器参数值并且为筛选的同步订阅该用户的客户端数据库。请参阅下面的步骤来完成一个简单的同步订阅服务:

准备数据库

请参阅“同步数据库示例:同步 SQL Server 和 SQL Server Compact” 一篇的“创建示例服务器数据库”一节来创建SyncDB数据库

定义筛选器模板

运行下面的程序来定义一个筛选器模板

 1         // 测试同步订阅之前,请使用此方法先在服务器数据库中创建同步作用域模板
2 static void CreateSyncTemplateOnServer()
3 {
4 string ServerConnectionString = "Data Source=localhost; Initial Catalog=SyncDB; Integrated Security=True";
5 using (var serverConn = new SqlConnection(ServerConnectionString))
6 {
7 //定义筛选同步作用域OrdersScope-NC
8 DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("OrdersScope_Template");
9
10 //从SyncDB 服务器数据库检索Orders表的架构并添加至同步作用域
11 DbSyncTableDescription tableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("Orders", serverConn);
12 scopeDesc.Tables.Add(tableDesc);
13
14 //创建一个同步设置对象
15 SqlSyncScopeProvisioning serverProvision = new SqlSyncScopeProvisioning(serverConn, scopeDesc, SqlSyncScopeProvisioningType.Template);
16 serverProvision.Tables["Orders"].AddFilterColumn("OriginState");
17 serverProvision.Tables["Orders"].FilterClause = "[side].[OriginState] = @originState";
18 SqlParameter param = new SqlParameter("@originState", SqlDbType.NVarChar, 2);
19 serverProvision.Tables["Orders"].FilterParameters.Add(param);
20
21 // 开始设置过程
22 serverProvision.Apply();
23 }
24 }

 

创建同步订阅服务

新建一个WCF Service Application,创建的订阅服务如下:

 1         /// <summary>
2 /// Subscribe Sync Scope for Client
3 /// Create Sync Scope from Sync Scope Template, then Provision the server database, then Provision the client database.
4 /// </summary>
5 /// <param name="ScopeTemplateName"></param>
6 /// <param name="SyncObjectSchema"></param>
7 /// <param name="FilterParameters"></param>
8 /// <returns></returns>
9 public string SubscribeSyncScopeForClient(string ScopeTemplateName, string SyncObjectSchema, List<SyncScopeParameter> FilterParameters, string clientConnectionString)
10 {
11 //1. 生成客户端ScopeName, 依据传入的Scope Template Name外加Guid生成,确保唯一性
12 Guid clientId = Guid.NewGuid();
13 string _clientScopeName = String.Format(CultureInfo.InvariantCulture, "{0}_{1}", ScopeTemplateName, clientId);
14
15 //2. 设置服务器端数据库
16 //2.1 设置服务器端连接字符串,本例中将对SyncDB数据库进行设置
17 string ServerConnectionString = "Data Source=localhost; Initial Catalog=SyncDB; Integrated Security=True";
18 using (var serverConnection = new SqlConnection(ServerConnectionString))
19 {
20 var provisioning = new SqlSyncScopeProvisioning(serverConnection);
21
22 //如果需要设置同步架构名称
23 if (!String.IsNullOrEmpty(SyncObjectSchema))
24 {
25 provisioning.ObjectSchema = SyncObjectSchema;
26 }
27
28 //判断传入的ScopeName是否是同步作用域模板
29 if (!provisioning.TemplateExists(ScopeTemplateName))
30 {
31 throw new Exception(string.Format("No scope temple '{0}' found in server database.", ScopeTemplateName));
32 }
33
34 provisioning.PopulateFromTemplate(_clientScopeName, ScopeTemplateName);
35 //处理过滤参数
36 if (null != FilterParameters && 0 != FilterParameters.Count)
37 {
38 foreach (var param in FilterParameters)
39 {
40 provisioning.Tables[param.TableName].FilterParameters[param.ParameterName].Value = param.ParameterValue;
41 }
42 }
43 //同步服务器
44 if (!provisioning.ScopeExists(_clientScopeName))
45 {
46 provisioning.Apply();
47 }
48 //设置客户端数据库,此处假设客户端数据库类型为Sql Server数据库
49 using (var clientConnection = new SqlConnection(clientConnectionString))
50 {
51 DbSyncScopeDescription scopeDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope(_clientScopeName, serverConnection);
52 SqlSyncScopeProvisioning clientProvision = new SqlSyncScopeProvisioning(clientConnection, scopeDesc);
53 clientProvision.Apply();
54 }
55 return _clientScopeName;
56 }
57 }

 

调用同步订阅服务

新建一个Console Application客户端程序来测试该同步订阅服务,关键代码如下:

 1         static void Main(string[] args)
2 {
3 //准备服务参数
4 string scopeTemplateName = "OrdersScope_Template";
5 List<ServiceReference1.SyncScopeParameter> list = new List<ServiceReference1.SyncScopeParameter>();
6 list.Add(new ServiceReference1.SyncScopeParameter() { TableName = "Orders", ParameterName = "@originState", ParameterValue = "WA" });
7 string clientConnectionString = @"Data Source=localhost; Initial Catalog=SyncDB2; Integrated Security=True";
8 //调用同步订阅服务
9 string clientScopeName = (new ServiceReference1.DatabaseSyncSubscriberClient())
10 .SubscribeSyncScopeForClient(scopeTemplateName, null, list.ToArray(), clientConnectionString);
11
12 Console.WriteLine("Both server and client databases are provisioned for scope {0}", clientScopeName);
13 }

完整的项目源代码请点击这里下载。

 

posted @ 2012-03-31 10:20  Life a Poem  阅读(3585)  评论(12编辑  收藏  举报