Sync Services for File Systemsに触ってみる

Introducing Microsoft Sync Framework: Sync Services for File Systemsの Sample Code を元にちょっと触ってみた。
for File System に関しては対象にした2つのフォルダ間のファイルを簡単に同期できるライブラリといった感じ、for ADO.NET にはまだ触ってないけれど、SQLServer などのスキーマ情報やデータをレプリカデータベースに同期するのを助けるためのライブラリみたいですね。


Option Strict On
Option Explicit On

Imports System.IO
Imports Microsoft.Synchronization
Imports Microsoft.Synchronization.Files

Public Class Program

Public Shared Sub Main()
Const replica1RootPath As String = "C:\TEMP\Replica1"
Const replica2RootPath As String = "C:\TEMP\Replica2"
Const idFileName As String = "SyncFile.ID"


' オプションの設定 どのときにファイルを同期させるか
Dim options = FileSyncOptions.ExplicitDetectChanges Or _
FileSyncOptions.RecycleDeletes Or _
FileSyncOptions.RecycleOverwrites Or _
FileSyncOptions.CompareFileStreams

' IDファイルは除く
Dim filter = New FileSyncScopeFilter()
filter.FileNameExcludes.Add(idFileName)

' Replica1 フォルダ情報登録
Dim replica1ID = GetReplicaId(Path.Combine(replica1RootPath, idFileName)) ' 該当フォルダに SyncFile 作成
RegisterReplicateFolder(replica1ID, replica1RootPath, filter, options) ' 該当フォルダに etafile 作成

' Replica2 フォルダ情報登録
Dim replica2ID = GetReplicaId(Path.Combine(replica2RootPath, idFileName))
RegisterReplicateFolder(replica2ID, replica2RootPath, filter, options)

' Replica1 → Replica2 への同期
CreateSnapshot(replica1ID, replica2ID, replica1RootPath, replica2RootPath, filter, options)

' Replica2 → Replica1 への同期
CreateSnapshot(replica2ID, replica1ID, replica2RootPath, replica1RootPath, filter, options)

End Sub

''' <summary>
''' 同期情報の登録
''' </summary>
''' <param name="replicaId"></param>
''' <param name="replicaRootPath"></param>
''' <param name="filter"></param>
''' <param name="options"></param>
Public Shared Sub RegisterReplicateFolder(ByVal replicaId As SyncId, _
ByVal replicaRootPath As String, _
ByVal filter As FileSyncScopeFilter, _
ByVal options As FileSyncOptions)

Using provider = New FileSyncProvider(replicaId, replicaRootPath, filter, options)
provider.DetectChanges()
End Using
End Sub

''' <summary>
''' SyncId を作成して返す。
''' </summary>
''' <param name="idFilePath">SyncFileへのパス</param>
''' <returns>SyncId</returns>
Public Shared Function GetReplicaId(ByVal idFilePath As String) As SyncId
If File.Exists(idFilePath) Then
Using sr = File.OpenText(idFilePath)
' SyncFile からフォルダの GUID 取得する。
Dim readGuid = sr.ReadLine()
If Not String.IsNullOrEmpty(readGuid) Then
Return New SyncId(New Guid(readGuid))
End If
End Using
End If

' GUID が読み込めない場合は新しく作る
Using idFile = File.Open(idFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)
Using sw = New StreamWriter(idFile)
Dim newSyncId = New SyncId(Guid.NewGuid())
sw.WriteLine(newSyncId.GetGuidId().ToString("D"))
Return newSyncId
End Using
End Using
End Function

''' <summary>
''' ファイルのレプリケーションを行う
''' </summary>
''' <param name="sourceReplicaId">同期元のSyncID</param>
''' <param name="destinationReplicaId">同期先のSyncID</param>
''' <param name="sourceReplicaRootPath">同期元のフォルダ</param>
''' <param name="destinationReplicaRootPath">同期先のフォルダ</param>
''' <param name="filter">対象外ファイル一覧</param>
''' <param name="options">同期オプション</param>
Public Shared Sub CreateSnapshot(ByVal sourceReplicaId As SyncId, _
ByVal destinationReplicaId As SyncId, _
ByVal sourceReplicaRootPath As String, _
ByVal destinationReplicaRootPath As String, _
ByVal filter As FileSyncScopeFilter, _
ByVal options As FileSyncOptions)

Using sourceProvider = New FileSyncProvider(sourceReplicaId, sourceReplicaRootPath, filter, options)
Using destinationProvider = New FileSyncProvider(destinationReplicaId, destinationReplicaRootPath, filter, options)
'
AddHandler destinationProvider.AppliedChange, AddressOf OnAppliedChange
AddHandler destinationProvider.SkippedChange, AddressOf OnSkippedChange

Dim agent = New SyncAgent()
agent.LocalProvider = sourceProvider
agent.RemoteProvider = destinationProvider
agent.Direction = SyncDirection.Upload

Console.WriteLine("Synchronizing changes to replica: " + destinationProvider.RootDirectoryPath)
agent.Synchronize()
End Using
End Using
End Sub

''' <summary>
''' ファイル同期時のイベントハンドラ
''' </summary>
Public Shared Sub OnAppliedChange(ByVal sender As Object, ByVal args As AppliedChangeEventArgs)
Select Case args.ChangeType
Case ChangeType.Create
Console.WriteLine("-- Applied CREATE for file " + args.NewFilePath)
Case ChangeType.Delete
Console.WriteLine("-- Applied DELETE for file " + args.OldFilePath)
Case ChangeType.Overwrite
Console.WriteLine("-- Applied OVERWRITE for file " + args.OldFilePath)
Case ChangeType.Rename
Console.WriteLine("-- Applied RENAME for file " + args.OldFilePath + " as " + args.NewFilePath)
Case Else

End Select
End Sub

''' <summary>
''' ファイルスキップ時のイベントハンドラ
''' </summary>
Public Shared Sub OnSkippedChange(ByVal sender As Object, ByVal args As SkippedChangeEventArgs)
Console.WriteLine(String.Format("-- Skipped applying {0} for {1} ", _
args.ChangeType.ToString().ToUpper(), _
If(String.IsNullOrEmpty(args.CurrentFilePath), _
args.NewFilePath + " due to error", _
args.CurrentFilePath())))
If args.Exception IsNot Nothing Then
' エラーが発生した場合ここでリカバリ処理を行う。
Console.WriteLine(" [" & args.Exception.Message & "]")
End If
End Sub
End Class