如何為數據庫同步篩選數據
了解篩選器
我們知道,在為數據庫准備同步時,我們需要定義同步作用域來描述同步的數據范圍,它是對進行同步的對象的一種邏輯分組。對於數據庫同步,一個同步作用域通常是一組數據表。但有時候,我們希望在同步作用域的基礎上對同步的數據進行篩選,比如,按照銷售人員同步訂單數據。這個時候,我們就可以使用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 }
完整的項目源代碼請點擊這里下載。