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のプロジェクトととなっています。