Sync Frameworkの使い方(ファイル編)

Sync Framework の名前空間

  • Microsoft.Synchronization
  • Microsoft.Synchronization.Files

参照アセンブリ

  • C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.0\Runtime\x86\Microsoft.Synchronization.dll
  • C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.0\Runtime\x86\Microsoft.Synchronization.Files.dll

コードサンプル

以下のソースコード例は基本的にMSDNのサンプルと同じものです。

機能的にはコマンドライン引数でとった二つのフォルダを同期させます。

同期方法オプションの設定

フォルダ間で競合等が発生したり、同期元にそのファイルがないといった場合に同期先で削除されるファイルを完全に削除してしまうのか、ゴミ箱に移動するのかのオプションや、明示的にどう起用メタデータの更新を行うかのオプションを設定します。


//オプションの設定:
//ExplicitDetectChanges:アプリケーションが明示的に DetectChanges  を呼び出さない限り、
//Sync Framework は変更の検出を行わない設定
//その他はファイル競合等でファイルやフォルダが削除になった場合にそれらを削除せずにゴミ箱に入れるという設定
FileSyncOptions options = FileSyncOptions.ExplicitDetectChanges |
    FileSyncOptions.RecycleDeletedFiles | FileSyncOptions.RecyclePreviousFileOnUpdates |
    FileSyncOptions.RecycleConflictLoserFiles;
FileSyncScopeFilter filter = new FileSyncScopeFilter();

対象/除外ファイルの設定

同期に使用する、もしくは除外するファイルの種類を指定することができます。

以下のコードでは除外側の設定ですが、FileNameIncludeプロパティに対して設定することで対象とするファイル(種類)だけを設定することもできます。

//除外フィルタ設定。ここではショートカットファイルを除外している。
filter.FileNameExcludes.Add("*.lnk");

メタデータの更新

FileSyncOptions.ExplicitDetectChangesオプションが指定されているので、明示的にコードでメタデータの更新をする必要があります。


//変更検出の実行
//ExplicitDetectChangesオプションを指定しているので、変更検出を同期の直前にしておく必要がある。
DetectChangesOnFileSystemReplica(replica1RootPath, filter, options);
DetectChangesOnFileSystemReplica(replica2RootPath, filter, options);
public static void DetectChangesOnFileSystemReplica(
    string replicaRootPath, 
    FileSyncScopeFilter filter, 
    FileSyncOptions options) {

    FileSyncProvider provider = null;

    try {
        //同期元のプロバイダを作成して、変更を検出する。
        provider = new FileSyncProvider(replicaRootPath, filter, options);
        provider.DetectChanges();
    }

    catch {
        //リソースのリリース
        if (provider != null) {
            provider.Dispose();
        }
    }
}

同期の実行

実際に同期処理を実行します。

以下の例では同期元と同期先で衝突や同時実行による競合が起きた場合の処理方法をオプション指定しないで、イベントにしてコンソール表示するようにしています。実際にオプション設定する方法についてはコード内のコメントを参考にしてください。


public static void SyncFileSystemReplicaOneWay(
    string sourceReplicaRootpath, 
    string destinationReplicaRootPath, 
    FileSyncScopeFilter filter, 
    FileSyncOptions options) {

    FileSyncProvider sourceProvider = null;
    FileSyncProvider destinationProvider = null;

    try {
        sourceProvider = new FileSyncProvider(sourceReplicaRootpath, filter, options);
        destinationProvider = new FileSyncProvider(destinationReplicaRootPath, filter, options);

        //衝突時のオプション設定。この設定を行わない場合デフォルトでApplicationDefinedが設定される
        //衝突制約の競合の解決方法。この場合は同期元レプリカで行われた変更が常に優先される。
        //sourceProvider.Configuration.CollisionConflictResolutionPolicy = CollisionConflictResolutionPolicy.SourceWins;
        //destinationProvider.Configuration.CollisionConflictResolutionPolicy = CollisionConflictResolutionPolicy.SourceWins;
        //同時実行時の競合処理のオプション。この設定を行わない場合デフォルトでApplicationDefinedが設定される
        //同時実行の競合の解決方法。この場合は同期元が常に優先される。
        //sourceProvider.Configuration.ConflictResolutionPolicy = ConflictResolutionPolicy.SourceWins;
        //destinationProvider.Configuration.ConflictResolutionPolicy = ConflictResolutionPolicy.SourceWins;

        //コンソールに情報をかけるようにイベントハンドラを設定する
        destinationProvider.AppliedChange += new EventHandler(OnAppliedChange);
        destinationProvider.SkippedChange += new EventHandler(OnSkippedChange);

        //衝突や競合発生した場合のイベント設定。
        SyncCallbacks destinationCallBacks = destinationProvider.DestinationCallbacks;
        destinationCallBacks.ItemConflicting += new EventHandler(OnItemConflicting);
        destinationCallBacks.ItemConstraint += new EventHandler(OnItemConstraint);
        //オーケストレーター(Agent)の作成
        SyncOrchestrator agent = new SyncOrchestrator();
        agent.LocalProvider = sourceProvider;
        agent.RemoteProvider = destinationProvider;
        agent.Direction = SyncDirectionOrder.Upload; //Uploadはソースからディスティネーションへの変更
        Console.WriteLine("Synchronizing changes to replica: " + destinationProvider.RootDirectoryPath);
        //同期実行
        agent.Synchronize();
    }
    finally {
        //プロバイダは適切にDisposeする。
        if (sourceProvider != null) {
            sourceProvider.Dispose();
        }
        if (destinationProvider != null) {
            destinationProvider.Dispose();
        }
    }
}

競合の解決に関しては以下の文書が参考になります。

全体ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Files;

namespace FolderSyncSample
{
    class Program
    {
        static void Main(string[] args) {
            if (args.Length < 2 || 
                string.IsNullOrEmpty(args[0]) || string.IsNullOrEmpty(args[1]) || 
                ! Directory.Exists(args[0]) || ! Directory.Exists(args[1]) ) {
                Console.WriteLine("Usage: FolderSyncSample.exe [valid directory path 1] [valid directory path 2]");
                return;
            }
            string replica1RootPath = args[0];
            string replica2RootPath = args[1];

            try {
                //オプションの設定:
                //ExplicitDetectChanges:アプリケーションが明示的に DetectChanges  を呼び出さない限り、
                //Sync Framework は変更の検出を行わない設定
                //その他はファイル競合等でファイルやフォルダが削除になった場合にそれらを削除せずにゴミ箱に入れるという設定
                FileSyncOptions options = FileSyncOptions.ExplicitDetectChanges |
                    FileSyncOptions.RecycleDeletedFiles | FileSyncOptions.RecyclePreviousFileOnUpdates |
                    FileSyncOptions.RecycleConflictLoserFiles;
                FileSyncScopeFilter filter = new FileSyncScopeFilter();
                //除外フィルタ設定。ここではショートカットファイルを除外している。
                filter.FileNameExcludes.Add("*.lnk");
                //変更検出の実行
                //ExplicitDetectChangesオプションを指定しているので、変更検出を同期の直前にしておく必要がある。
                DetectChangesOnFileSystemReplica(replica1RootPath, filter, options);
                DetectChangesOnFileSystemReplica(replica2RootPath, filter, options);
                //同期実行
                //フィルタがnullでいいのは、上で同期対象のメタデータが更新されていて、同期実行中に
                //メタデータ更新は行われないので、フィルタを指定する必要がない。
                SyncFileSystemReplicaOneWay(replica1RootPath, replica2RootPath, null, options);
                SyncFileSystemReplicaOneWay(replica2RootPath, replica1RootPath, null, options);
                return;
            }
            catch (Exception e) {
                Console.WriteLine("\nException from File Sync Provider:\n" + e.ToString());
                return;
            }
        }
        
        /// 
        /// replicaRootPathで指定された同期元フォルダの変更を検出してメタデータを更新する。
        /// 
        public static void DetectChangesOnFileSystemReplica(
            string replicaRootPath, 
            FileSyncScopeFilter filter, 
            FileSyncOptions options) {

            FileSyncProvider provider = null;

            try {
                //同期元のプロバイダを作成して、変更を検出する。
                provider = new FileSyncProvider(replicaRootPath, filter, options);
                provider.DetectChanges();
            }
            catch {
                //リソースのリリース
                if (provider != null) {
                    provider.Dispose();
                }
            }
        }



        /// 
        /// 単方向の同期を実行する。
        /// 
        public static void SyncFileSystemReplicaOneWay(
            string sourceReplicaRootpath, 
            string destinationReplicaRootPath, 
            FileSyncScopeFilter filter, 
            FileSyncOptions options) {
            FileSyncProvider sourceProvider = null;
            FileSyncProvider destinationProvider = null;

            try {
                sourceProvider = new FileSyncProvider(sourceReplicaRootpath, filter, options);
                destinationProvider = new FileSyncProvider(destinationReplicaRootPath, filter, options);

                //衝突時のオプション設定。この設定を行わない場合デフォルトでApplicationDefinedが設定される
                //衝突制約の競合の解決方法。この場合は同期元レプリカで行われた変更が常に優先される。
                //sourceProvider.Configuration.CollisionConflictResolutionPolicy = CollisionConflictResolutionPolicy.SourceWins;
                //destinationProvider.Configuration.CollisionConflictResolutionPolicy = CollisionConflictResolutionPolicy.SourceWins;
                //同時実行時の競合処理のオプション。この設定を行わない場合デフォルトでApplicationDefinedが設定される
                //同時実行の競合の解決方法。この場合は同期元が常に優先される。
                //sourceProvider.Configuration.ConflictResolutionPolicy = ConflictResolutionPolicy.SourceWins;
                //destinationProvider.Configuration.ConflictResolutionPolicy = ConflictResolutionPolicy.SourceWins;

                //コンソールに情報をかけるようにイベントハンドラを設定する
                destinationProvider.AppliedChange += new EventHandler(OnAppliedChange);
                destinationProvider.SkippedChange += new EventHandler(OnSkippedChange);

                //衝突や競合発生した場合のイベント設定。
                SyncCallbacks destinationCallBacks = destinationProvider.DestinationCallbacks;
                destinationCallBacks.ItemConflicting += new EventHandler(OnItemConflicting);
                destinationCallBacks.ItemConstraint += new EventHandler(OnItemConstraint);

                //オーケストレーター(Agent)の作成

                SyncOrchestrator agent = new SyncOrchestrator();
                agent.LocalProvider = sourceProvider;
                agent.RemoteProvider = destinationProvider;
                agent.Direction = SyncDirectionOrder.Upload; //Uploadはソースからディスティネーションへの変更
                Console.WriteLine("Synchronizing changes to replica: " + destinationProvider.RootDirectoryPath);
                //同期実行
                agent.Synchronize();
            }
            finally {
                //プロバイダは適切にDisposeする。
                if (sourceProvider != null) {
                    sourceProvider.Dispose();
                }
                if (destinationProvider != null) {
                    destinationProvider.Dispose();
                }
            }
        }

        /// 
        /// 衝突競合の解決方法がプロバイダのConfigurationプロパティで適切に設定されていない、
        /// もしくは ApplicationDefined に設定されている場合の衝突競合や、衝突以外のすべて
        /// の制約の競合について、制約の競合が報告されると発生するイベントをログとして表示する。
        /// 
        static void OnItemConstraint(object sender, ItemConstraintEventArgs e) {
            e.SetResolutionAction(ConstraintConflictResolutionAction.SourceWins);
            Console.WriteLine("-- Concurrency conflict detected for item " + e.DestinationChange.ItemId.ToString());
        }

        /// 
        /// 同時実行による競合が起きた場合の解決方法がプロバイダのConfigurationプロパティで適切に
        /// 設定されていない、もしくは ApplicationDefined に設定されている場合に競合が検出され、
        /// セッションでその競合の解決方法が ApplicationDefined に設定されている場合に、
        /// ItemConflicting が発生するイベントをログとして表示します。
        /// 
        static void OnItemConflicting(object sender, ItemConflictingEventArgs e) {
            e.SetResolutionAction(ConflictResolutionAction.SourceWins);
            Console.WriteLine("-- Constraint conflict detected for item " + e.DestinationChange.ItemId.ToString());
        }

        /// 
        /// 同期先フォルダで何らかのファイル操作があった場合にログをコンソールに表示します。
        /// 
        /// イベント呼び出し元オブジェクトです。
        /// 変更の種類、新しいファイル パス、および変更が適用される前のファイル パスを提供します。
        private static void OnAppliedChange(object sender, AppliedChangeEventArgs e) {
            switch (e.ChangeType) {
                case ChangeType.Create:
                    Console.WriteLine("-- Applied CREATE for file " + e.NewFilePath);
                    break;
                case ChangeType.Delete:
                    Console.WriteLine("-- Applied DELETE for file " + e.OldFilePath);
                    break;
                case ChangeType.Update:
                    Console.WriteLine("-- Applied OVERWRITE for file " + e.OldFilePath);
                    break;
                case ChangeType.Rename:
                    Console.WriteLine("-- Applied RENAME for file " + e.OldFilePath +
                                      " as " + e.NewFilePath);
                    break;
            }
        }

        /// 
        /// 同期先フォルダで変更がスキップされた場合にログをコンソール出力します。
        /// 
        /// 変更がスキップされなかった場合のファイル パス、およびスキップの理由を提供します。
        static void OnSkippedChange(object sender, SkippedChangeEventArgs e) {
            Console.WriteLine("-- Skipped applying " + e.ChangeType.ToString().ToUpper()
                  + " for " + (!string.IsNullOrEmpty(e.CurrentFilePath) ?
                                e.CurrentFilePath : e.NewFilePath) + " due to error");

            if (e.Exception != null)
                Console.WriteLine("   [" + e.Exception.Message + "]");
        }   
    }
}

参考・参照文献

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

No Code, No Life.

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