SQL Server Compact Edition 3.5のデータベース間でのSync Frameworkを使ったデータ同期方法について説明します。
トランザクションのあまりないようなアプリケーションで、データの多重化をさせたいような場合に有効なソリューションだと思っています。
参照が必要なアセンブリ
Sync Framework 2.1を使用します。以下のPathはx64の場合です。
- C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.1\Runtime\x86\Microsoft.Synchronization.dll
- C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.1\Runtime\ADO.NET\V3.1\x86\Microsoft.Synchronization.Data.dll
- C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.1\Runtime\ADO.NET\V3.1\x86\Microsoft.Synchronization.Data.SqlServerCe.dll
SQL Sever CEで必要な参照アセンブリは以下の通りです。
- C:\Program Files (x86)\Microsoft SQL Server Compact Edition\v3.5\Desktop\System.Data.SqlServerCe.dll
基本的にVisual Studioを使う場合には、Visual Studioの参照の追加で出てくるリストにあるはずですが、無ければパスで指定してください。
名前空間
Sync Frameworkの名前空間は以下になります。
- using Microsoft.Synchronization
- using Microsoft.Synchronization.Data
- using Microsoft.Synchronization.Data.SqlServerCe
作業の流れ
作業は以下のような流れになります。
- ローカル、リモートそれぞれのデータベースへのConnectionオブジェクトを作る。
- ローカル、リモートそれぞれのSyncProviderを作成する。
- 新しいスコープ詳細の作成と、データベースに対してそのスコープに対応する同期作業用テーブルの追加を行う
- SqlCeSyncProviderオブジェクトを利用してRelationalSyncProviderオブジェクトを作成する。
- SyncOrchestratorを作成する。
- データの同期を実行する。
Connectionオブジェクトの作成
次のようにsdfファイルへのフルパスをDataSourceに指定してConnectionオブジェクトを作成します。
//Connectionオブジェクトを作成する
string con01 = "DataSource=\"C:\\Users\\ishisaka\\Documents\\TestDb1.sdf\"";
SqlCeConnection con1 = new SqlCeConnection(con01);
string con02 = "DataSource=\"C:\\Users\\ishisaka\\Documents\\TestDb2.sdf\"";
SqlCeConnection con2 = new SqlCeConnection(con02);
SyncProviderの作成
今作ったConnectionオブジェクトを使ったSyncProviderを作成します。
string scopeName = "sync_log";//スコープ名の指定
SqlCeSyncProvider provider1 = new SqlCeSyncProvider(scopeName, con1);
SqlCeSyncProvider provider2 = new SqlCeSyncProvider(scopeName, con2);
同期のための準備
同期をさせるために新しいスコープ詳細の作成とそのスコープに対応するテーブルの追加を行います。
//新しいスコープ詳細の作成とそのスコープに対応するテーブルの追加を行う
CreateSyncScheme(provider1);
CreateSyncScheme(provider2);
//新しいスコープ詳細の作成とそのスコープに対応するテーブルの追加を行う
private void CreateSyncScheme(SqlCeSyncProvider provider) {
//同期スコープの設定
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription(provider.ScopeName);
//ProviderからConnectionオブジェクトの取得
SqlCeConnection ceConn = (SqlCeConnection)provider.Connection;
//SqlCeSyncScopeProvisioningの作成
SqlCeSyncScopeProvisioning ceConfig = new SqlCeSyncScopeProvisioning(ceConn);
string scopeName = provider.ScopeName;
//すでに用意されていないのであれば、必要なテーブルを作成する
if (!ceConfig.ScopeExists(scopeName)) {
scopeDesc.Tables.Add(SqlCeSyncDescriptionBuilder.GetDescriptionForTable("Log", ceConn));
ceConfig.PopulateFromScopeDescription(scopeDesc);
//DbSyncScopeDescription オブジェクトによって表される特定のスコープに対応する
//SQL Server Compact データベースの準備する。
ceConfig.Apply();
}
}
Sync Frameworkは同期に必要な情報を保存するため、同期対象となっているデータベースにテーブルをに追加する必要があり、この関数ではその処理を行っています。このテーブルは一度作ればいいので、11行目ですでにそれらのテーブルが存在しているかを確認しています。
RelationalSyncProviderオブジェクトを作成する。
RelationalSyncProviderは同期エージェントに対してデータベース固有の特徴を抽象化して、データベース特定の実装が覆い隠します。
//SqlCeSyncProviderオブジェクトを利用してRelationalSyncProviderオブジェクトを作成する。
RelationalSyncProvider srcProvider = provider1;
RelationalSyncProvider destProvider = provider2;
SyncOrchestratorを作成する
SyncOrchestratorを作成し、必要な設定をします。
//SyncOrchestratorを作成する。
SyncOrchestrator orchestrator = new SyncOrchestrator();
orchestrator.LocalProvider = srcProvider;
orchestrator.RemoteProvider = destProvider;
//データの同期方向を設定する。
orchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
//同期中メッセージ用のイベントを登録する。
orchestrator.SessionProgress += new EventHandler(orchestrator_SessionProgress);
同期を実行する
同期を実行させます。OrchestratorのSynchronizeメソッドを呼び出すことで同期を実行させ、結果をSyncOperationStatisticsとして返します。コード例にはありませんが、SyncOperationStatisticsには同期実行での統計情報が含まれているので、ユーザーに対して必要な情報表示させたり出来ます。
//データの同期を実行する。
SyncOperationStatistics stats = orchestrator.Synchronize();
全体コード
Log.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Data;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServerCe;
namespace SyncFrameworkDemo
{
public class LogData
{
public DateTime Timestamp { get; set; }
public String Message { get; set; }
}
public class Log
{
//ローカルのデータベースにデータを追加する。
public void AddLog(string message) {
string con01 = "DataSource=\"C:\\Users\\ishisaka\\Documents\\TestDb1.sdf\"";
using (SqlCeConnection con1 = new SqlCeConnection(con01)) {
con1.Open();
var queryString = "INSERT INTO Log (timestamp, message) VALUES (@timestamp, @message)";
using (SqlCeCommand cmd = new SqlCeCommand(queryString, con1)) {
SqlCeParameter timestampPara = new SqlCeParameter("@timestamp", SqlDbType.DateTime);
timestampPara.Value = DateTime.Now;
cmd.Parameters.Add(timestampPara);
SqlCeParameter messeagePara = new SqlCeParameter("@message", SqlDbType.NVarChar, 1000);
messeagePara.Value = message;
cmd.Parameters.Add(messeagePara);
cmd.ExecuteNonQuery();
}
}
}
//リモート側のデータベースからデータを取得する。
public List GetLogFromRemote() {
List logData = new List();
string con02 = "DataSource=\"C:\\Users\\ishisaka\\Documents\\TestDb2.sdf\"";
using (SqlCeConnection con2 = new SqlCeConnection(con02)) {
con2.Open();
var queryString = "SELECT * FROM Log";
using (SqlCeCommand cmd = new SqlCeCommand(queryString, con2)) {
SqlCeDataReader dr = cmd.ExecuteReader();
while (dr.Read()) {
int timestampOrdinal = dr.GetOrdinal("timestamp");
int messeageOrdinal = dr.GetOrdinal("message");
LogData data = new LogData();
data.Timestamp = dr.GetDateTime(timestampOrdinal);
data.Message = dr.GetString(messeageOrdinal);
logData.Add(data);
}
}
}
return logData;
}
//SQL CEのデータベース間でデータを同期する。
public void DbSync() {
//Connectionオブジェクトを作成する
string con01 = "DataSource=\"C:\\Users\\ishisaka\\Documents\\TestDb1.sdf\"";
SqlCeConnection con1 = new SqlCeConnection(con01);
string con02 = "DataSource=\"C:\\Users\\ishisaka\\Documents\\TestDb2.sdf\"";
SqlCeConnection con2 = new SqlCeConnection(con02);
//Providerの作成
string scopeName = "sync_log";//スコープ名の指定
SqlCeSyncProvider provider1 = new SqlCeSyncProvider(scopeName, con1);
SqlCeSyncProvider provider2 = new SqlCeSyncProvider(scopeName, con2);
//新しいスコープ詳細の作成とそのスコープに対応するテーブルの追加を行う
CreateSyncScheme(provider1);
CreateSyncScheme(provider2);
//SqlCeSyncProviderオブジェクトを利用してRelationalSyncProviderオブジェクトを作成する。
RelationalSyncProvider srcProvider = provider1;
RelationalSyncProvider destProvider = provider2;
//SyncOrchestratorを作成する。
SyncOrchestrator orchestrator = new SyncOrchestrator();
orchestrator.LocalProvider = srcProvider;
orchestrator.RemoteProvider = destProvider;
//データの同期方向を設定する。
orchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
//同期中メッセージ用のイベントを登録する。
orchestrator.SessionProgress += new EventHandler(orchestrator_SessionProgress);
//データの同期を実行する。
SyncOperationStatistics stats = orchestrator.Synchronize();
srcProvider.Dispose();
destProvider.Dispose();
provider1.Dispose();
provider2.Dispose();
con2.Close();
con1.Close();
}
//同期の状態を表示する
void orchestrator_SessionProgress(object sender, SyncStagedProgressEventArgs e) {
string providerPosition;
if (e.ReportingProvider == SyncProviderPosition.Local) {
providerPosition = "ローカル";
}
else if (e.ReportingProvider == SyncProviderPosition.Remote) {
providerPosition = "リモート";
}
else {
providerPosition = "不明";
}
if (e.Stage == SessionProgressStage.ChangeDetection) {
Console.WriteLine("{0} 同期元の確認 {1} %", providerPosition, e.CompletedWork / e.TotalWork * 100);
}
if (e.Stage == SessionProgressStage.ChangeEnumeration) {
Console.WriteLine("{0} 同期元の変更の抽出 {1} %", providerPosition, e.CompletedWork / e.TotalWork * 100);
}
if (e.Stage == SessionProgressStage.ChangeEnumeration) {
Console.WriteLine("{0} 同期先への変更の適用 {1} %", providerPosition, e.CompletedWork / e.TotalWork * 100);
}
}
//新しいスコープ詳細の作成とそのスコープに対応するテーブルの追加を行う
private void CreateSyncScheme(SqlCeSyncProvider provider) {
//同期スコープの設定
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription(provider.ScopeName);
//ProviderからConnectionオブジェクトの取得
SqlCeConnection ceConn = (SqlCeConnection)provider.Connection;
//SqlCeSyncScopeProvisioningの作成
SqlCeSyncScopeProvisioning ceConfig = new SqlCeSyncScopeProvisioning(ceConn);
string scopeName = provider.ScopeName;
//すでに用意されていないのであれば、必要なテーブルを作成する
if (!ceConfig.ScopeExists(scopeName)) {
scopeDesc.Tables.Add(SqlCeSyncDescriptionBuilder.GetDescriptionForTable("Log", ceConn));
ceConfig.PopulateFromScopeDescription(scopeDesc);
//DbSyncScopeDescription オブジェクトによって表される特定のスコープに対応する
//SQL Server Compact データベースの準備する。
ceConfig.Apply();
}
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SyncFrameworkDemo
{
class Program
{
static void Main(string[] args) {
//テスト用クラスのインスタンスを作る
Log log = new Log();
//ローカルのデータベースにデータを追加する。
log.AddLog("Test");
//同期させる
log.DbSync();
//リモートのデータベースが同期したか確認する。
List data = log.GetLogFromRemote();
data.ForEach(d => { Console.WriteLine("{0}, {1}", d.Timestamp, d.Message); });
Console.ReadLine();
}
}
}
コードダウンロード
Visual Studio 2010のプロジェクトととなっています。