コラボレーション同期を構成して実行する方法 (SQL Server)
このトピックでは、Sync Framework を使用して SQL Server および SQL Server Compact のデータベースを同期するアプリケーションの主要部分について説明します。このアプリケーションのコードでは、次の Sync Framework クラスを中心に説明しています。
サンプル コードを実行する方法の詳細については、「SQL Server と SQL Server Compact の同期」の「操作方法に関するトピックのサンプル アプリケーション」を参照してください。
「コラボレーション同期のアーキテクチャとクラス」でも取り上げているように、同期は、2 つの SqlSyncProvider インスタンス間、2 つの SqlCeSyncProvider インスタンス間、または、それぞれ 1 つずつのインスタンス間で発生します。このトピックのコード例は 2 層アプリケーションを想定しています。N 層構成を必要とする、SqlCeSyncProvider の 2 つのインスタンス間の同期には適用できません。N 層構成の例については、Sync Framework SDK に付属のサンプル WebSharingAppDemo-CEProviderEndToEnd を参照してください。
プロバイダーの種類の比較
このトピックでは、Sync Framework 2.0 で導入された 2 つの同期プロバイダー SqlSyncProvider と SqlCeSyncProvider を使用して、SQL Server データベースと SQL Server Compact データベースを同期する方法を説明します。Sync Framework の他のプロバイダーを使用してこれらのデータベースを同期することもできますが、新しいプロバイダーには次のような利点があります。
SqlSyncProvider は機能的には DbSyncProvider と同等ですが、必要なコードの量が圧倒的に少なく、また、Sync Framework がデータを同期する際に使用するクエリについての理解も最小限で済みます。ただし、SQL Server 以外のデータベースについては、やはり DbSyncProvider が適しています。
SqlSyncProvider および SqlCeSyncProvider はクライアント/サーバー、ピア ツー ピア、混合の 3 つのトポロジに適しています。これに対し、DbServerSyncProvider および SqlCeClientSyncProvider が適しているのはクライアント/サーバー トポロジだけです。さらに、SqlSyncProvider および SqlCeSyncProvider は、より高度な機能 (行数ではなく、データのサイズに基づいて変更をバッチ分割できるなど) にも対応しています。
SqlSyncProvider および SqlCeSyncProvider は柔軟性が高く、設定も簡単です。SQL Server Express や SQL Server Compact を含め、SQL Server のすべてのエディションの同期に対応しています。
ノードの設定と同期の実行
トポロジ内のノードを同期する処理は、1) 同期対象のノードを設定し、2) その 1 対のノード間で実際に同期を実行する、という 2 つのフェーズに分けることができます。SqlSyncProvider および SqlCeSyncProvider の場合、ノードの設定は次の 2 つのタスクで構成されます。
同期対象を定義する
同期の対象は、スコープを少なくとも 1 つ記述することによって定義します。スコープは一連のテーブルであり、その一部または全部をフィルター選択することができます。データベース内の既存のテーブルを使用できるほか、Sync Framework オブジェクトを使ってテーブルを記述し、実行時、基になるストアが同期されている間に生成することもできます。詳細については、この後の「スコープについて」を参照してください。
注意
いったんスコープを同期した後で、変更する必要が生じた場合は、既存のスコープに関連付けられているメタデータを削除してから作成し直す必要があります。
Sync Framework の変更追跡用のデータベースを準備する
テーブルとスコープを定義した後は、Sync Framework のオブジェクトを使用して、各ノードに準備スクリプトを適用します。このスクリプトによって、変更の追跡と変更の適用に必要な、メタデータ テーブル、トリガー、ストアド プロシージャから成るインフラストラクチャが形成されます。
ノードの準備が済むと、それらのノードを同期できるようになります。開発者の視点で見ると、単に同期オプションを設定し、Synchronize()
を呼び出すだけで、複雑な作業はないように見えます。しかし、その裏側では、Sync Framework が指定されたスコープとテーブルの記述情報を使用しながら、スコープごとの構成オブジェクトと、(各テーブルにつき 1 つ存在する) 同期アダプターごとの構成オブジェクトを構築します。こうして Sync Framework は、それぞれのデータベースに存在している情報を基に、2 つのノード間で行われる同期セッションごとに必要な情報を構築できます。SqlSyncProvider も SqlCeSyncProvider も、準備中に作成された変更追跡テーブルなどのオブジェクトを認識して、必要な DbSyncAdapter オブジェクトを自動的に生成します。これらのプロバイダーを使用してデータを同期すると、必要なコード量が大幅に削減されるのにはこうした背景があります。
次の表は、データベースおよびプロバイダーのセットアップに使用されるクラスの一覧です。
SQL Server | SQL Server Compact | 説明 |
---|---|---|
DbSyncScopeDescription |
DbSyncScopeDescription |
同期スコープを表します。同期スコープは、まとめて同期されるテーブルの論理グループです。 |
SqlSyncScopeProvisioning |
DbSyncScopeDescription オブジェクトによって表される特定のスコープに対応する SQL Server データベースまたは SQL Server Compact データベースの準備を表します。 |
|
特定のスコープの SqlSyncProvider または SqlCeSyncProvider によって使用される構成情報を表します。 |
||
DbSyncTableDescription |
DbSyncTableDescription |
同期スコープに含まれるテーブルのスキーマを表します。 |
DbSyncColumnDescription |
同期スコープに含まれるテーブルに属している列のプロパティを表します。 |
|
SqlSyncDescriptionBuilder |
SqlCeSyncDescriptionBuilder |
同期に関係する SQL Server データベースまたは SQL Server Compact データベースのスコープおよびテーブル情報を表します。SQL Server データベースまたは SQL Server Compact データベースから |
SqlSyncTableProvisioning |
DbSyncTableDescription オブジェクトによって表される SQL Server または SQL Server Compact のデータベース テーブル (必要に応じてフィルターを使用) の準備を表します。 |
|
SqlSyncProviderAdapterConfiguration |
SQL Server データベースまたは SQL Server Compact データベース内のテーブルの同期アダプターの構成情報を表します。 |
以上に挙げた主要な型以外で重要な型は次の 4 つです。
SqlSyncStoreMetadataCleanup および SqlCeSyncStoreMetadataCleanup: Sync Framework の変更追跡テーブルから古いメタデータをクリーンアップします。詳細については、「コラボレーション同期のメタデータをクリーンアップする方法 (SQL Server)」を参照してください。
SqlCeSyncStoreSnapshotInitialization: 既存の SQL Server Compact データベース ファイルのスナップショットを生成し、それを使用して、同期対象の別の SQL Server Compact データベースを初期化することができます。詳細については、このトピックの「クライアントの準備」を参照してください。
SqlSyncStoreRestore: SQL Server データベースがバックアップから復元された後、その復元されたデータベースを再度同期するためには、変更追跡メタデータを更新する必要があります。それを可能にする型です。詳細については、「データベースのバックアップと復元方法 (SQL Server)」を参照してください。
スコープについて
注意
ここでは、同期スコープについての補足情報を取り上げます。このセクションをとばして直接「コード例」を参照してもかまいませんが、フィルター選択されたスコープを使用する場合や、1 つのアプリケーションで複数のスコープを使用する予定がある場合は、このセクションに目をとおしておくことをお勧めします。
大切なことは、スコープはテーブルとフィルターの "組み合わせ" であるという点です。たとえば、customer_sales
テーブルにフィルターを適用して、ワシントン州の売上データだけを含む sales-WA
スコープを定義するとします。同じテーブルに対して別のフィルターを定義すれば、また別のスコープが作成されます (sales-OR
など)。フィルターの条件を満たさなくなった行は削除することになりますが、その処理は、Sync Framework が自動的に行うわけではありません。フィルターを定義する場合はこのことに注意してください。たとえば、フィルター選択に使用される列の値がユーザーやアプリケーションによって更新された場合、行が別のスコープに移動します。この行は、新しい所属先スコープに追加されますが、それまでのスコープからも削除されません。この状況はアプリケーションで対処する必要があります。
スコープは互いに独立している場合と、オーバーラップしている場合とがあります。2 つのスコープが同じデータを共有している場合、"2 つのスコープがオーバーラップしている" といいます。たとえば、products
テーブルが sales
スコープに属しており、さらに inventory
スコープにも属している状況が考えられます。スコープがオーバーラップし、なおかつフィルター選択されている状況もありえます。フィルター選択とオーバーラップの両方が起こるシナリオを次に示します。
シナリオ 1:
スコープ 1 は
sales-WA
です。このスコープには、products
、orders
(フィルター:state=WA
)、およびorder_details
(フィルター:state=WA
) が含まれます。スコープ 2 は
sales-OR
です。このスコープには、products
、orders
(フィルター:state=OR
)、およびorder_details
(フィルター:state=OR
) が含まれます。
このシナリオでは、
products
テーブル全体が両方のスコープによって共有されています。orders
テーブルとorder_details
テーブルは両方のスコープに存在しますが、フィルターはオーバーラップしていないため、これらのスコープは、これらのテーブルの行を共有しません。シナリオ 2:
スコープ 1 は
sales-WA
です。このスコープには、products
、orders
(フィルター:state=WA
)、およびorder_details
(フィルター:state=WA
) が含まれます。スコープ 2 は
sales-Northwest
です。このスコープには、products
、orders
(フィルター:state=WA OR state=ID
) およびshippers
が含まれます。
このシナリオでも、
products
テーブル全体が両方のスコープによって共有されています。orders
テーブルは両方のスコープに存在し、フィルターはオーバーラップしています。つまり、両方のスコープがstate=WA
というフィルターを満たした行を共有することになります。shippers
テーブルとorder_details
テーブルはスコープ間で共有されません。
スコープはさまざまな方法で定義できますが、必ず、次の原則に従う必要があります。同期トポロジ内の 2 つのデータベース間で同期されるデータは、1 つのスコープにしか所属できない、ということです。たとえば、先ほどのシナリオ 2 の場合、データベース A とデータベース B はスコープ 1 を同期できます。データベース A とデータベース C はスコープ 2 を同期できます。しかし、データベース A とデータベース B が (スコープ 1 に加え) スコープ 2 を同期することはできません。products
と orders
には、両方のスコープに属している行が存在するためです。
コード例
このセクションのコード例には、これまで説明してきたさまざまなオブジェクトが使用されています。主に次の点を取り上げています。
スコープおよびテーブルの記述
サーバーの準備
クライアントの準備
同期オプションの設定
ノードの同期
以上の各項目についての説明に続けて、完全なコンソール アプリケーションが掲載されています。それぞれの例に、4 ノード トポロジを同期するための補足的なコードを組み合わせたものです。このトポロジは SQL Server サーバー、SQL Server クライアントが 1 つ、SQL Server Compact クライアントが 2 つで構成されています。
スコープおよびテーブルの記述
次のコード例では、filtered_customer
という名前のスコープを記述し、このスコープに 2 つのテーブル Customer
と CustomerContact
を追加します。このテーブルは既にサーバー データベースに存在しているので、GetDescriptionForTable メソッドを使用して、サーバー データベースからスキーマを取得します。Customer
テーブルのすべての列が含まれますが、CustomerContact
テーブルの列は 2 つしか含まれません。
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("filtered_customer");
// Definition for Customer.
DbSyncTableDescription customerDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
// Definition for CustomerContact, including the list of columns to include.
Collection<string> columnsToInclude = new Collection<string>();
columnsToInclude.Add("CustomerId");
columnsToInclude.Add("PhoneType");
DbSyncTableDescription customerContactDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn);
scopeDesc.Tables.Add(customerContactDescription);
Dim scopeDesc As New DbSyncScopeDescription("filtered_customer")
' Definition for Customer.
Dim customerDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
' Definition for CustomerContact, including the list of columns to include.
Dim columnsToInclude As New Collection(Of String)()
columnsToInclude.Add("CustomerId")
columnsToInclude.Add("PhoneType")
Dim customerContactDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn)
scopeDesc.Tables.Add(customerContactDescription)
サーバーの準備
次のコード例は、filtered_customer
スコープ用に準備オブジェクトを作成し、ベース テーブルがサーバー データベースに作成されないように指定します。さらに、同期に関連するオブジェクトはすべて "Sync"
という名前のデータベース スキーマ内で作成されるように指定します。スコープを準備する処理の一環として、Customer
テーブルに対するフィルターを定義します。このフィルターに一致した行だけが同期されます。CustomerContact
テーブルにはフィルターを定義しません。したがって、このテーブルのすべての行が同期されます。準備オプションを定義した後、Apply メソッドを呼び出して、サーバー データベースに変更追跡インフラストラクチャを作成します。さらに、準備スクリプトをファイルに書き込みます。
SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
serverConfig.ObjectSchema = "Sync";
// Specify which column(s) in the Customer table to use for filtering data,
// and the filtering clause to use against the tracking table.
// "[side]" is an alias for the tracking table.
serverConfig.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverConfig.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = 'Retail'";
// Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn);
// Write the configuration script to a file. You can modify
// this script if necessary and run it against the server
// to customize behavior.
File.WriteAllText("SampleConfigScript.txt",
serverConfig.Script("SyncSamplesDb_SqlPeer1"));
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
serverConfig.ObjectSchema = "Sync"
' Specify which column(s) in the Customer table to use for filtering data,
' and the filtering clause to use against the tracking table.
' "[side]" is an alias for the tracking table.
serverConfig.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverConfig.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = 'Retail'"
' Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn)
' Write the configuration script to a file. You can modify
' this script if necessary and run it against the server
' to customize behavior.
File.WriteAllText("SampleConfigScript.txt", serverConfig.Script("SyncSamplesDb_SqlPeer1"))
クライアントの準備
このアプリケーションでは、クライアントは次の 2 つの方法で準備されます。
サーバーまたは別のクライアント データベースから取得したスコープ情報に基づいた SQL Server または SQL Server Compact クライアント データベースの完全初期化。
ユーザー オブジェクトと同期オブジェクトは、SqlSyncDescriptionBuilder オブジェクトと SqlCeSyncDescriptionBuilder オブジェクトによって提供されるスキーマ情報に基づいて、クライアント データベース内に作成されます。最初の同期セッションの一環として、クライアント データベースが同期用に準備されますが、そのとき、すべての行がクライアントにダウンロードされ増分挿入されます。
SQL Server Compact クライアント データベースのスナップショット初期化 (既存のクライアント データベースを使用)。
スナップショット初期化は、クライアント データベースの初期化に要する時間を短縮することを目的とした初期化方法です。いずれかのクライアント データベースを完全初期化を使って初期化した後、他のデータベースは、この 1 つ目のクライアント データベースの "スナップショット" を使用して初期化できます。スナップショットは、テーブル スキーマ、データ (オプション)、および変更追跡インフラストラクチャを備えた特別な SQL Server Compact データベースです。各クライアントには、このスナップショットをコピーすることになります。クライアントの初回同期セッション中、クライアント固有のメタデータが更新され、スナップショットの作成以後に生じたすべての変更がクライアント データベースにダウンロードされます。
注意
スナップショットの生成は、SQL Server Compact データベースのアクティビティがないときにのみ行う必要があります。スナップショットの生成時に同時に行うことができる処理はありません。
次のコード例では、まずサーバーからスコープ情報を取得し、取得したベース テーブルと変更追跡のスキーマを使用して、SQL Server Compact クライアント データベースを準備します。次に、SQL Server Compact クライアント データベースからのスコープ情報に基づいて SQL Server クライアント データベースを準備します。
// Create a SQL Server Compact database and provision it based on scope
// information that is retrieved from the server. Compact databases
// do not support separate schemas, so we prefix the name of all
// synchronization-related objects with "Sync" so that they are easy to
// identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);
DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", null, "Sync", serverConn);
SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.Apply(clientSqlCe1Conn);
// Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
// information that is retrieved from the SQL Server Compact database. We could
// have also retrieved this information from the server.
DbSyncScopeDescription clientSqlDesc = SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply(clientSqlConn);
' Create a SQL Server Compact database and provision it based on scope
' information that is retrieved from the server. Compact databases
' do not support separate schemas, so we prefix the name of all
' synchronization-related objects with "Sync" so that they are easy to
' identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)
Dim clientSqlCe1Desc As DbSyncScopeDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", Nothing, "Sync", serverConn)
Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.Apply(clientSqlCe1Conn)
' Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
' information that is retrieved from the SQL Server Compact database. We could
' have also retrieved this information from the server.
Dim clientSqlDesc As DbSyncScopeDescription = _
SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply(clientSqlConn)
次のコード例では、SyncSampleClient2.sdf
という名前のスナップショットが SyncSampleClient1.sdf
データベースに基づいて作成されます。次に、SyncSampleClient2.sdf
をサーバー データベースに同期します。
// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");
// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
' Create a snapshot from the SQL Server Compact database, which will be used to
' initialize a second Compact database. Again, this database could be provisioned
' by retrieving scope information from another database, but we want to
' demonstrate the use of snapshots, which provide a convenient deployment
' mechanism for Compact databases.
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")
' The new SQL Server Compact client synchronizes with the server, but
' no data is downloaded because the snapshot already contains
' all of the data from the first Compact database.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
同期オプションの設定
次のコード例は、このアプリケーションに含まれる SampleSyncOrchestrator
クラスのコンストラクターです。SqlSyncProvider
も SqlCeSyncProvider
も RelationalSyncProvider
から派生しているため、このコンストラクターは 2 つの RelationalSyncProvider
オブジェクトを受け取ります。このコードでは、どのプロバイダーがローカル プロバイダーであり、リモート プロバイダーであるかを指定します。次に、変更をリモート データベースからローカル データベースにアップロードした後、その逆の方向にダウンロードすることを指定します。
public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
{
this.LocalProvider = localProvider;
this.RemoteProvider = remoteProvider;
this.Direction = SyncDirectionOrder.UploadAndDownload;
}
Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)
Me.LocalProvider = localProvider
Me.RemoteProvider = remoteProvider
Me.Direction = SyncDirectionOrder.UploadAndDownload
End Sub
ノードの同期
次のコード例は、1) サーバーと SQL Server クライアントの間、2) SQL Server クライアントと複数の SQL Server Compact クライアントのいずれかの間、および 3) サーバーと他の SQL Server Compact クライアントの間という、3 つの異なる同期セッションのプロバイダーをインスタンス化します。最初の 2 つのセッションでは 7 つの行が同期されます (CustomerContact
では 4 行すべてが、Customer
ではフィルター条件を満たした 3 行が同期されます)。3 番目のセッションでは、最初の SQL Server Compact データベースからのすべてのデータがスナップショットに既に含まれているので、同期される行はありません。このアプリケーションは、Synchronize()
メソッドから返された統計情報を表示します。
SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;
// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the SQL Server client to the
// first SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");
// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics
' Data is downloaded from the server to the SQL Server client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the SQL Server client to the
' first SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Create a snapshot from the SQL Server Compact database, which will be used to
' initialize a second Compact database. Again, this database could be provisioned
' by retrieving scope information from another database, but we want to
' demonstrate the use of snapshots, which provide a convenient deployment
' mechanism for Compact databases.
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")
' The new SQL Server Compact client synchronizes with the server, but
' no data is downloaded because the snapshot already contains
' all of the data from the first Compact database.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
完全なコード例
次の完全なコード例には、既に説明したコード例に加え、同期の統計情報やイベント情報を表示するためのコードが含まれています。この例では、「データベース プロバイダーの Utility クラスに関するトピック」で説明されている Utility
クラスが必要です。
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using Microsoft.Synchronization.Data.SqlServerCe;
namespace Microsoft.Samples.Synchronization
{
class Program
{
static void Main(string[] args)
{
// Create the connections over which provisioning and synchronization
// are performed. The Utility class handles all functionality that is not
// directly related to synchronization, such as holding connection
// string information and making changes to the server database.
SqlConnection serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
SqlConnection clientSqlConn = new SqlConnection(Utility.ConnStr_SqlSync_Client);
SqlCeConnection clientSqlCe1Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync1);
SqlCeConnection clientSqlCe2Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync2);
// Create a scope named "filtered_customer", and add two tables to the scope.
// GetDescriptionForTable gets the schema of each table, so that tracking
// tables and triggers can be created for that table. For Customer, we add
// the entire table. For CustomerContact, we add only two of the columns.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("filtered_customer");
// Definition for Customer.
DbSyncTableDescription customerDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
// Definition for CustomerContact, including the list of columns to include.
Collection<string> columnsToInclude = new Collection<string>();
columnsToInclude.Add("CustomerId");
columnsToInclude.Add("PhoneType");
DbSyncTableDescription customerContactDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn);
scopeDesc.Tables.Add(customerContactDescription);
// Create a provisioning object for "filtered_customer". We specify that
// base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1),
// and that all synchronization-related objects should be created in a
// database schema named "Sync". If you specify a schema, it must already exist
// in the database.
SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
serverConfig.ObjectSchema = "Sync";
// Specify which column(s) in the Customer table to use for filtering data,
// and the filtering clause to use against the tracking table.
// "[side]" is an alias for the tracking table.
serverConfig.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverConfig.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = 'Retail'";
// Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn);
// Write the configuration script to a file. You can modify
// this script if necessary and run it against the server
// to customize behavior.
File.WriteAllText("SampleConfigScript.txt",
serverConfig.Script("SyncSamplesDb_SqlPeer1"));
// Provision each of the client databases.
// Create a SQL Server Compact database and provision it based on scope
// information that is retrieved from the server. Compact databases
// do not support separate schemas, so we prefix the name of all
// synchronization-related objects with "Sync" so that they are easy to
// identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);
DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", null, "Sync", serverConn);
SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.Apply(clientSqlCe1Conn);
// Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
// information that is retrieved from the SQL Server Compact database. We could
// have also retrieved this information from the server.
DbSyncScopeDescription clientSqlDesc = SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply(clientSqlConn);
// Initial synchronization sessions. 7 rows are synchronized:
// all rows (4) from CustomerContact, and the 3 rows from Customer
// that satisfy the filtering criteria.
SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;
// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the SQL Server client to the
// first SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");
// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");
// Synchronize again. Three changes were made on the server, but
// only two of them applied to rows that are in the "filtered_customer"
// scope. The other row is not synchronized.
// Notice that the order of synchronization is different from the initial
// sessions, but the two changes are propagated to all nodes.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"),
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
serverConn.Close();
serverConn.Dispose();
clientSqlConn.Close();
clientSqlConn.Dispose();
clientSqlCe1Conn.Close();
clientSqlCe1Conn.Dispose();
clientSqlCe2Conn.Close();
clientSqlCe2Conn.Dispose();
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server);
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client);
Console.Write("\nPress any key to exit.");
Console.Read();
}
}
public class SampleSyncOrchestrator : SyncOrchestrator
{
public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
{
this.LocalProvider = localProvider;
this.RemoteProvider = remoteProvider;
this.Direction = SyncDirectionOrder.UploadAndDownload;
}
public void DisplayStats(SyncOperationStatistics syncStatistics, string syncType)
{
Console.WriteLine(String.Empty);
if (syncType == "initial")
{
Console.WriteLine("****** Initial Synchronization ******");
}
else if (syncType == "subsequent")
{
Console.WriteLine("***** Subsequent Synchronization ****");
}
Console.WriteLine("Start Time: " + syncStatistics.SyncStartTime);
Console.WriteLine("Total Changes Uploaded: " + syncStatistics.UploadChangesTotal);
Console.WriteLine("Total Changes Downloaded: " + syncStatistics.DownloadChangesTotal);
Console.WriteLine("Complete Time: " + syncStatistics.SyncEndTime);
Console.WriteLine(String.Empty);
}
}
}
Imports System
Imports System.Collections.ObjectModel
Imports System.IO
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlServerCe
Imports Microsoft.Synchronization
Imports Microsoft.Synchronization.Data
Imports Microsoft.Synchronization.Data.SqlServer
Imports Microsoft.Synchronization.Data.SqlServerCe
Namespace Microsoft.Samples.Synchronization
Class Program
Public Shared Sub Main(ByVal args As String())
' Create the connections over which provisioning and synchronization
' are performed. The Utility class handles all functionality that is not
' directly related to synchronization, such as holding connection
' string information and making changes to the server database.
Dim serverConn As New SqlConnection(Utility.ConnStr_SqlSync_Server)
Dim clientSqlConn As New SqlConnection(Utility.ConnStr_SqlSync_Client)
Dim clientSqlCe1Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync1)
Dim clientSqlCe2Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync2)
' Create a scope named "filtered_customer", and add two tables to the scope.
' GetDescriptionForTable gets the schema of each table, so that tracking
' tables and triggers can be created for that table. For Customer, we add
' the entire table. For CustomerContact, we add only two of the columns.
Dim scopeDesc As New DbSyncScopeDescription("filtered_customer")
' Definition for Customer.
Dim customerDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
' Definition for CustomerContact, including the list of columns to include.
Dim columnsToInclude As New Collection(Of String)()
columnsToInclude.Add("CustomerId")
columnsToInclude.Add("PhoneType")
Dim customerContactDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn)
scopeDesc.Tables.Add(customerContactDescription)
' Create a provisioning object for "filtered_customer". We specify that
' base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1),
' and that all synchronization-related objects should be created in a
' database schema named "Sync". If you specify a schema, it must already exist
' in the database.
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
serverConfig.ObjectSchema = "Sync"
' Specify which column(s) in the Customer table to use for filtering data,
' and the filtering clause to use against the tracking table.
' "[side]" is an alias for the tracking table.
serverConfig.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverConfig.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = 'Retail'"
' Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn)
' Write the configuration script to a file. You can modify
' this script if necessary and run it against the server
' to customize behavior.
File.WriteAllText("SampleConfigScript.txt", serverConfig.Script("SyncSamplesDb_SqlPeer1"))
' Provision each of the client databases.
' Create a SQL Server Compact database and provision it based on scope
' information that is retrieved from the server. Compact databases
' do not support separate schemas, so we prefix the name of all
' synchronization-related objects with "Sync" so that they are easy to
' identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)
Dim clientSqlCe1Desc As DbSyncScopeDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", Nothing, "Sync", serverConn)
Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.Apply(clientSqlCe1Conn)
' Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
' information that is retrieved from the SQL Server Compact database. We could
' have also retrieved this information from the server.
Dim clientSqlDesc As DbSyncScopeDescription = _
SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply(clientSqlConn)
' Initial synchronization sessions. 7 rows are synchronized:
' all rows (4) from CustomerContact, and the 3 rows from Customer
' that satisfy the filtering criteria.
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics
' Data is downloaded from the server to the SQL Server client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the SQL Server client to the
' first SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Create a snapshot from the SQL Server Compact database, which will be used to
' initialize a second Compact database. Again, this database could be provisioned
' by retrieving scope information from another database, but we want to
' demonstrate the use of snapshots, which provide a convenient deployment
' mechanism for Compact databases.
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")
' The new SQL Server Compact client synchronizes with the server, but
' no data is downloaded because the snapshot already contains
' all of the data from the first Compact database.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")
' Synchronize again. Three changes were made on the server, but
' only two of them applied to rows that are in the "filtered_customer"
' scope. The other row is not synchronized.
' Notice that the order of synchronization is different from the initial
' sessions, but the two changes are propagated to all nodes.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
serverConn.Close()
serverConn.Dispose()
clientSqlConn.Close()
clientSqlConn.Dispose()
clientSqlCe1Conn.Close()
clientSqlCe1Conn.Dispose()
clientSqlCe2Conn.Close()
clientSqlCe2Conn.Dispose()
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server)
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client)
Console.Write(vbLf & "Press any key to exit.")
Console.Read()
End Sub
End Class
Public Class SampleSyncOrchestrator
Inherits SyncOrchestrator
Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)
Me.LocalProvider = localProvider
Me.RemoteProvider = remoteProvider
Me.Direction = SyncDirectionOrder.UploadAndDownload
End Sub
Public Sub DisplayStats(ByVal syncStatistics As SyncOperationStatistics, ByVal syncType As String)
Console.WriteLine([String].Empty)
If syncType = "initial" Then
Console.WriteLine("****** Initial Synchronization ******")
ElseIf syncType = "subsequent" Then
Console.WriteLine("***** Subsequent Synchronization ****")
End If
Console.WriteLine("Start Time: " & syncStatistics.SyncStartTime)
Console.WriteLine("Total Changes Uploaded: " & syncStatistics.UploadChangesTotal)
Console.WriteLine("Total Changes Downloaded: " & syncStatistics.DownloadChangesTotal)
Console.WriteLine("Complete Time: " & syncStatistics.SyncEndTime)
Console.WriteLine([String].Empty)
End Sub
End Class
End Namespace