Sync Frameworkの使い方(SQL Server Compact Edition 3.5編)

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

作業の流れ

作業は以下のような流れになります。

  1. ローカル、リモートそれぞれのデータベースへのConnectionオブジェクトを作る。
  2. ローカル、リモートそれぞれのSyncProviderを作成する。
  3. 新しいスコープ詳細の作成と、データベースに対してそのスコープに対応する同期作業用テーブルの追加を行う
  4. SqlCeSyncProviderオブジェクトを利用してRelationalSyncProviderオブジェクトを作成する。
  5. SyncOrchestratorを作成する。
  6. データの同期を実行する。

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

CC BY-SA 4.0 This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

No Code, No Life.

%d人のブロガーが「いいね」をつけました。