Как синхронизировать файлы с помощью управляемого кода

В этом разделе показано, как использовать управляемый язык (например, C# или Visual Basic) для создания приложений, синхронизирующих файлы и вложенные папки с помощью службы синхронизации файлов Sync Framework.

Примеры в этом разделе уделяют особое внимание следующим типам Sync Framework:

Основные сведения о синхронизации файлов

Платформа Sync Framework реализует службу синхронизации, позволяющую синхронизировать файлы и вложенные папки файловой системы. Эта служба предоставляет доступ к настраиваемым параметрам, обеспечивающим более тонкое управление элементами и процессом синхронизации. Для синхронизации файлов в двух папках приложение выполняет ряд простых шагов.

  1. Создает объект FileSyncProvider, представляющий каждую из папок.

  2. Передает два поставщика объекту SyncOrchestrator и указывает один из них в качестве поставщика источника, а другой — в качестве поставщика назначения.

  3. Вызывает Synchronize, чтобы начать сеанс синхронизации.

Дополнительные сведения о синхронизации файлов см. в разделе Синхронизация файлов.

Пример

Пример кода, приведенный в этом разделе, взят из приложения командной строки, предназначенного для синхронизации двух каталогов вместе с содержащимися в них файлами и вложенными папками. В примере кода показаны следующие задачи.

  • Как задать параметры синхронизации.

  • Как выполнить явное обнаружение изменения для реплики.

  • Как задать фильтр, управляющий набором элементов, включаемых в синхронизацию.

  • Как обрабатывать конфликты, которые могут возникнуть в процессе синхронизации.

  • Как синхронизировать две реплики.

После рассмотрения примеров кода в конце приведен полный код приложения, который можно скомпилировать и запустить на выполнение.

Задание параметров синхронизации

Объект FileSyncOptions позволяет определить ряд параметров синхронизации файлов, в том числе задать способ обнаружения изменений и необходимость удаления элементов или их перемещения в корзину в процессе синхронизации. В следующем примере кода определены четыре параметра, три из которых связаны с операциями удаления элементов. Параметр ExplicitDetectChanges указывает на то, что платформа Sync Framework не будет выполнять обнаружение изменений, если приложение явным образом не вызвало метод DetectChanges. Объяснение приведено в разделе «Осуществление обнаружения изменений».

FileSyncOptions options = FileSyncOptions.ExplicitDetectChanges |
         FileSyncOptions.RecycleDeletedFiles | FileSyncOptions.RecyclePreviousFileOnUpdates | 
         FileSyncOptions.RecycleConflictLoserFiles;
Dim options As FileSyncOptions = _
    FileSyncOptions.ExplicitDetectChanges Or FileSyncOptions.RecycleDeletedFiles _
    Or FileSyncOptions.RecyclePreviousFileOnUpdates _
    Or FileSyncOptions.RecycleConflictLoserFiles

Осуществление обнаружения изменений

По умолчанию платформа Sync Framework выполняет обнаружение изменений в обеих репликах при каждом вызове метода Synchronize. Обнаружение изменений дает возможность платформе Sync Framework определить, какие элементы должны быть отправлены от источника к назначению, а какие из этих элементов конфликтуют (если есть). Параметр ExplicitDetectChanges позволяет управлять способом обнаружения изменений. В следующем примере кода обнаружение изменений вызывается для каждой реплики еще до вызова метода Synchronize. Этот пример иллюстрирует метод DetectChanges, преимущество которого заключается в том, что требует одного прохода обнаружения изменений, а не двух, как при выполнении двунаправленной синхронизации, показанной ниже в этом приложении.

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();
    }
    finally
    {
        // Release resources.
        if (provider != null)
            provider.Dispose();
    }
}
DetectChangesOnFileSystemReplica(replica1RootPath, filter, options)
DetectChangesOnFileSystemReplica(replica2RootPath, filter, options)
Public Shared Sub DetectChangesOnFileSystemReplica(ByVal replicaRootPath As String, ByVal filter As FileSyncScopeFilter, _
                                                   ByVal options As FileSyncOptions)
    Dim provider As FileSyncProvider = Nothing

    Try
        provider = New FileSyncProvider(replicaRootPath, filter, options)
        provider.DetectChanges()
    Finally
        ' Release resources. 
        If provider IsNot Nothing Then
            provider.Dispose()
        End If
    End Try
End Sub

Определение статического фильтра

Для исключения файлов по имени (в том числе с использованием шаблонов) и по атрибуту можно задавать статические фильтры. Кроме этого, статические фильтры позволяют целиком исключать содержимое вложенных папок. Также можно задать явный список имен файлов, включаемых в синхронизацию (в том числе с использованием шаблонов). Для включения в область синхронизации файлы или папки должны пройти все фильтры. Например, если все файлы, имеющие расширение TXT, должны быть исключены из области, а файл MyFile.txt указан в списке файлов, предназначенных для явного включения в область, то файл MyFile.txt все равно будет исключен.

В следующем примере кода объект FileSyncScopeFilter используется для создания фильтра, исключающего все файлы с расширением LNK. Фильтр не имеет никакой связи с создающим его поставщиком. Чтобы подключить фильтр к поставщику, он должен быть передан одному из конструкторов класса FileSyncProvider или присвоен свойству ScopeFilter. В рассматриваемом образце приложения эта передача осуществляется в методе DetectChangesOnFileSystemReplica(), поскольку данный фильтр применим только для обнаружения изменений. Поскольку фильтр не зависит от поставщика, то в каждом сеансе синхронизации должен быть создан только один фильтр; поставщики не должны использовать другие фильтры, так как это может привести к расхождению данных.

FileSyncScopeFilter filter = new FileSyncScopeFilter();
filter.FileNameExcludes.Add("*.lnk");
Dim filter As New FileSyncScopeFilter()
filter.FileNameExcludes.Add("*.lnk")

Исключать файлы в процессе синхронизации можно не только с помощью статических фильтров, но при обработке событий, вызываемых поставщиком. Дополнительные сведения см. в разделе Выбор файлов для синхронизации.

Обработка конфликтов

Платформа Sync Framework обнаруживает и разрешает конфликты параллелизма и конфликты ограничений для файлов и папок. Конфликт параллелизма возникает в том случае, если один и тот же элемент изменился в обеих репликах после проведения последнего сеанса синхронизации между этими репликами. Конфликт ограничения возникает при добавлении на обеих репликах файла или папки с одним и тем же именем. Конфликты разрешаются путем сохранения файла или папки с последним по времени изменением или путем удаления (или перемещения) файла или папки с более старым изменением. Применительно к файлам предусмотрена также возможность задать, что победителем конфликта всегда является источник или назначение (независимо от того, где было сделано первое изменение). В следующем примере кода регистрируются обработчики событий ItemConflicting и ItemConstraint, которые доступны через объект SyncCallbacks. Вызываемые методы разрешают все конфликты в пользу источника и выводят данные на консоль.

Dim destinationCallbacks As SyncCallbacks = destinationProvider.DestinationCallbacks
AddHandler destinationCallbacks.ItemConflicting, AddressOf OnItemConflicting
AddHandler destinationCallbacks.ItemConstraint, AddressOf OnItemConstraint
SyncCallbacks destinationCallbacks = destinationProvider.DestinationCallbacks;
destinationCallbacks.ItemConflicting += new EventHandler<ItemConflictingEventArgs>(OnItemConflicting);
destinationCallbacks.ItemConstraint += new EventHandler<ItemConstraintEventArgs>(OnItemConstraint);
public static void OnItemConflicting(object sender, ItemConflictingEventArgs args)
{
    args.SetResolutionAction(ConflictResolutionAction.SourceWins);
    Console.WriteLine("-- Concurrency conflict detected for item " + args.DestinationChange.ItemId.ToString());
}

public static void OnItemConstraint(object sender, ItemConstraintEventArgs args)
{
    args.SetResolutionAction(ConstraintConflictResolutionAction.SourceWins);
    Console.WriteLine("-- Constraint conflict detected for item " + args.DestinationChange.ItemId.ToString());
}
Public Shared Sub OnItemConflicting(ByVal sender As Object, ByVal args As ItemConflictingEventArgs)
    args.SetResolutionAction(ConflictResolutionAction.SourceWins)
    Console.WriteLine("-- Concurrency conflict detected for item " & args.DestinationChange.ItemId.ToString())
End Sub

Public Shared Sub OnItemConstraint(ByVal sender As Object, ByVal args As ItemConstraintEventArgs)
    args.SetResolutionAction(ConstraintConflictResolutionAction.SourceWins)
    Console.WriteLine("-- Constraint conflict detected for item " & args.DestinationChange.ItemId.ToString())
End Sub

Синхронизация двух реплик

После задания параметров и фильтров приложение синхронизирует эти две реплики, создавая экземпляр объекта SyncOrchestrator и вызывая метод Synchronize. В следующем примере кода показано, как указать поставщика для каждой реплики, задать параметры, зарегистрировать обработчики событий, указать направление синхронизации Upload и вызвать метод Synchronize. Для выполнения двунаправленной синхронизации реплик этот метод вызывается дважды.

SyncFileSystemReplicasOneWay(replica1RootPath, replica2RootPath, null, options);
SyncFileSystemReplicasOneWay(replica2RootPath, replica1RootPath, null, options);
public static void SyncFileSystemReplicasOneWay(
        string sourceReplicaRootPath, string destinationReplicaRootPath,
        FileSyncScopeFilter filter, FileSyncOptions options)
{
    FileSyncProvider sourceProvider = null;
    FileSyncProvider destinationProvider = null;

    try
    {
        // Instantiate source and destination providers, with a null filter (the filter
        // was specified in DetectChangesOnFileSystemReplica()), and options for both.
        sourceProvider = new FileSyncProvider(
            sourceReplicaRootPath, filter, options);
        destinationProvider = new FileSyncProvider(
            destinationReplicaRootPath, filter, options);

        // Register event handlers so that we can write information
        // to the console.
        destinationProvider.AppliedChange +=
            new EventHandler<AppliedChangeEventArgs>(OnAppliedChange);
        destinationProvider.SkippedChange +=
            new EventHandler<SkippedChangeEventArgs>(OnSkippedChange);

        // Use SyncCallbacks for conflicting items.
        SyncCallbacks destinationCallbacks = destinationProvider.DestinationCallbacks;
        destinationCallbacks.ItemConflicting += new EventHandler<ItemConflictingEventArgs>(OnItemConflicting);
        destinationCallbacks.ItemConstraint += new EventHandler<ItemConstraintEventArgs>(OnItemConstraint);

        SyncOrchestrator agent = new SyncOrchestrator();
        agent.LocalProvider = sourceProvider;
        agent.RemoteProvider = destinationProvider;
        agent.Direction = SyncDirectionOrder.Upload; // Upload changes from the source to the destination.

        Console.WriteLine("Synchronizing changes to replica: " +
            destinationProvider.RootDirectoryPath);
        agent.Synchronize();
    }
    finally
    {
        // Release resources.
        if (sourceProvider != null) sourceProvider.Dispose();
        if (destinationProvider != null) destinationProvider.Dispose();
    }
}
SyncFileSystemReplicasOneWay(replica1RootPath, replica2RootPath, Nothing, options)
SyncFileSystemReplicasOneWay(replica2RootPath, replica1RootPath, Nothing, options)
Public Shared Sub SyncFileSystemReplicasOneWay(ByVal sourceReplicaRootPath As String, _
                                               ByVal destinationReplicaRootPath As String, ByVal filter As FileSyncScopeFilter, _
                                               ByVal options As FileSyncOptions)

    Dim sourceProvider As FileSyncProvider = Nothing
    Dim destinationProvider As FileSyncProvider = Nothing

    Try
        ' Instantiate source and destination providers, with a null filter (the filter
        ' was specified in DetectChangesOnFileSystemReplica()), and options for both.
        sourceProvider = New FileSyncProvider(sourceReplicaRootPath, filter, options)
        destinationProvider = New FileSyncProvider(destinationReplicaRootPath, filter, options)

        ' Register event handlers so that we can write information 
        ' to the console. 
        AddHandler destinationProvider.AppliedChange, AddressOf OnAppliedChange
        AddHandler destinationProvider.SkippedChange, AddressOf OnSkippedChange

        ' Use SyncCallbacks for conflicting items.
        Dim destinationCallbacks As SyncCallbacks = destinationProvider.DestinationCallbacks
        AddHandler destinationCallbacks.ItemConflicting, AddressOf OnItemConflicting
        AddHandler destinationCallbacks.ItemConstraint, AddressOf OnItemConstraint

        Dim agent As New SyncOrchestrator()
        agent.LocalProvider = sourceProvider
        agent.RemoteProvider = destinationProvider
        agent.Direction = SyncDirectionOrder.Upload
        ' Upload changes from the source to the destination. 
        Console.WriteLine("Synchronizing changes to replica: " & destinationProvider.RootDirectoryPath)
        agent.Synchronize()
    Finally
        ' Release resources. 
        If sourceProvider IsNot Nothing Then
            sourceProvider.Dispose()
        End If
        If destinationProvider IsNot Nothing Then
            destinationProvider.Dispose()
        End If
    End Try
End Sub

Полный пример кода

Ниже приведен полный код для данного примера. Предыдущие примеры в этом разделе взяты именно из этого фрагмента кода. Чтобы запустить этот код на выполнение, выполните следующие действия.

  • Создайте проект приложения командной строки и добавьте код к проекту.

  • Добавьте ссылки на сборки Microsoft.Synchronzation.dll и Microsoft.Synchronzation.Files.dll.

  • Выполните сборку проекта, создав исполняемый файл.

  • Запустите исполняемый файл на выполнение из командной строки, чтобы провести синхронизацию файлов и подкаталогов между двумя репликами: MyExeName.exe \path\to\directoryA \path\to\directoryB.

using System;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Files;

namespace Microsoft.Samples.Synchronization
{
    class Program
    {
        public 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: MyExecutableName.exe [valid directory path 1] [valid directory path 2]");
                return;
            }

            string replica1RootPath = args[0];
            string replica2RootPath = args[1];

            try
            {
                // Set options for the synchronization session. In this case, options specify
                // that the application will explicitly call FileSyncProvider.DetectChanges, and
                // that items should be moved to the Recycle Bin instead of being permanently deleted.
                FileSyncOptions options = FileSyncOptions.ExplicitDetectChanges |
                         FileSyncOptions.RecycleDeletedFiles | FileSyncOptions.RecyclePreviousFileOnUpdates | 
                         FileSyncOptions.RecycleConflictLoserFiles;

                // Create a filter that excludes all *.lnk files. The same filter should be used 
                // by both providers.
                FileSyncScopeFilter filter = new FileSyncScopeFilter();
                filter.FileNameExcludes.Add("*.lnk");

                // Explicitly detect changes on both replicas before syncyhronization occurs.
                // This avoids two change detection passes for the bidirectional synchronization 
                // that we will perform.
                DetectChangesOnFileSystemReplica(
                    replica1RootPath, filter, options);
                DetectChangesOnFileSystemReplica(
                    replica2RootPath, filter, options);

                // Synchronize the replicas in both directions. In the first session replica 1 is
                // the source, and in the second session replica 2 is the source. The third parameter
                // (the filter value) is null because the filter is specified in DetectChangesOnFileSystemReplica().
                SyncFileSystemReplicasOneWay(replica1RootPath, replica2RootPath, null, options);
                SyncFileSystemReplicasOneWay(replica2RootPath, replica1RootPath, null, options);
            }
            catch (Exception e)
            {
                Console.WriteLine("\nException from File Sync Provider:\n" + e.ToString());
            }
        }

        // Create a provider, and detect changes on the replica that the provider
        // represents.
        public static void DetectChangesOnFileSystemReplica(
                string replicaRootPath,
                FileSyncScopeFilter filter, FileSyncOptions options)
        {
            FileSyncProvider provider = null;

            try
            {
                provider = new FileSyncProvider(replicaRootPath, filter, options);
                provider.DetectChanges();
            }
            finally
            {
                // Release resources.
                if (provider != null)
                    provider.Dispose();
            }
        }

        public static void SyncFileSystemReplicasOneWay(
                string sourceReplicaRootPath, string destinationReplicaRootPath,
                FileSyncScopeFilter filter, FileSyncOptions options)
        {
            FileSyncProvider sourceProvider = null;
            FileSyncProvider destinationProvider = null;

            try
            {
                // Instantiate source and destination providers, with a null filter (the filter
                // was specified in DetectChangesOnFileSystemReplica()), and options for both.
                sourceProvider = new FileSyncProvider(
                    sourceReplicaRootPath, filter, options);
                destinationProvider = new FileSyncProvider(
                    destinationReplicaRootPath, filter, options);

                // Register event handlers so that we can write information
                // to the console.
                destinationProvider.AppliedChange +=
                    new EventHandler<AppliedChangeEventArgs>(OnAppliedChange);
                destinationProvider.SkippedChange +=
                    new EventHandler<SkippedChangeEventArgs>(OnSkippedChange);

                // Use SyncCallbacks for conflicting items.
                SyncCallbacks destinationCallbacks = destinationProvider.DestinationCallbacks;
                destinationCallbacks.ItemConflicting += new EventHandler<ItemConflictingEventArgs>(OnItemConflicting);
                destinationCallbacks.ItemConstraint += new EventHandler<ItemConstraintEventArgs>(OnItemConstraint);

                SyncOrchestrator agent = new SyncOrchestrator();
                agent.LocalProvider = sourceProvider;
                agent.RemoteProvider = destinationProvider;
                agent.Direction = SyncDirectionOrder.Upload; // Upload changes from the source to the destination.

                Console.WriteLine("Synchronizing changes to replica: " +
                    destinationProvider.RootDirectoryPath);
                agent.Synchronize();
            }
            finally
            {
                // Release resources.
                if (sourceProvider != null) sourceProvider.Dispose();
                if (destinationProvider != null) destinationProvider.Dispose();
            }
        }

        // Provide information about files that were affected by the synchronization session.
        public static void OnAppliedChange(object sender, AppliedChangeEventArgs args)
        {
            switch (args.ChangeType)
            {
                case ChangeType.Create:
                    Console.WriteLine("-- Applied CREATE for file " + args.NewFilePath);
                    break;
                case ChangeType.Delete:
                    Console.WriteLine("-- Applied DELETE for file " + args.OldFilePath);
                    break;
                case ChangeType.Update:
                    Console.WriteLine("-- Applied OVERWRITE for file " + args.OldFilePath);
                    break;
                case ChangeType.Rename:
                    Console.WriteLine("-- Applied RENAME for file " + args.OldFilePath +
                                      " as " + args.NewFilePath);
                    break;
            }
        }

        // Provide error information for any changes that were skipped.
        public static void OnSkippedChange(object sender, SkippedChangeEventArgs args)
        {
            Console.WriteLine("-- Skipped applying " + args.ChangeType.ToString().ToUpper()
                  + " for " + (!string.IsNullOrEmpty(args.CurrentFilePath) ?
                                args.CurrentFilePath : args.NewFilePath) + " due to error");

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

        // By default, conflicts are resolved in favor of the last writer. In this example,
        // the change from the source in the first session (replica 1), will always
        // win the conflict.
        public static void OnItemConflicting(object sender, ItemConflictingEventArgs args)
        {
            args.SetResolutionAction(ConflictResolutionAction.SourceWins);
            Console.WriteLine("-- Concurrency conflict detected for item " + args.DestinationChange.ItemId.ToString());
        }

        public static void OnItemConstraint(object sender, ItemConstraintEventArgs args)
        {
            args.SetResolutionAction(ConstraintConflictResolutionAction.SourceWins);
            Console.WriteLine("-- Constraint conflict detected for item " + args.DestinationChange.ItemId.ToString());
        }
    }
}

В примере на Visual Basic показано, как явным образом задать в коде значение атрибута MTAThread для метода Main(). Служба синхронизации файлов требует, чтобы приложения использовали модель многопотокового подразделения (MTA).

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

Namespace Microsoft.Samples.Synchronization

    Class Program

        ' File synchronization provider requires applications to use the multithreaded apartment (MTA) 
        ' threading model. This is specified by using the MTAThread attribute.
        <MTAThreadAttribute()> _
        Public Shared Sub Main(ByVal args As String())
            If args.Length < 2 OrElse String.IsNullOrEmpty(args(0)) OrElse String.IsNullOrEmpty(args(1)) OrElse Not Directory.Exists(args(0)) OrElse Not Directory.Exists(args(1)) Then
                Console.WriteLine("Usage: MyExecutableName.exe [valid directory path 1] [valid directory path 2]")
                Exit Sub
            End If

            Dim replica1RootPath As String = args(0)
            Dim replica2RootPath As String = args(1)

            Try
                ' Set options for the synchronization session. In this case, options specify 
                ' that the application will explicitly call FileSyncProvider.DetectChanges, and 
                ' that items should be moved to the Recycle Bin instead of being permanently deleted. 
                Dim options As FileSyncOptions = _
                    FileSyncOptions.ExplicitDetectChanges Or FileSyncOptions.RecycleDeletedFiles _
                    Or FileSyncOptions.RecyclePreviousFileOnUpdates _
                    Or FileSyncOptions.RecycleConflictLoserFiles

                ' Create a filter that excludes all *.lnk files. The same filter should be used 
                ' by both providers.
                Dim filter As New FileSyncScopeFilter()
                filter.FileNameExcludes.Add("*.lnk")

                ' Explicitly detect changes on both replicas before syncyhronization occurs. 
                ' This avoids two change detection passes for the bidirectional synchronization 
                ' that we will perform. 
                DetectChangesOnFileSystemReplica(replica1RootPath, filter, options)
                DetectChangesOnFileSystemReplica(replica2RootPath, filter, options)

                ' Synchronize the replicas in both directions. In the first session replica 1 is 
                ' the source, and in the second session replica 2 is the source. The third parameter
                ' (the filter value) is null because the filter is specified in DetectChangesOnFileSystemReplica().
                SyncFileSystemReplicasOneWay(replica1RootPath, replica2RootPath, Nothing, options)
                SyncFileSystemReplicasOneWay(replica2RootPath, replica1RootPath, Nothing, options)
            Catch e As Exception
                Console.WriteLine(vbLf & "Exception from File Sync Provider:" & vbLf & e.ToString())
            End Try
        End Sub

        ' Create a provider, and detect changes on the replica that the provider 
        ' represents. 
        Public Shared Sub DetectChangesOnFileSystemReplica(ByVal replicaRootPath As String, ByVal filter As FileSyncScopeFilter, _
                                                           ByVal options As FileSyncOptions)
            Dim provider As FileSyncProvider = Nothing

            Try
                provider = New FileSyncProvider(replicaRootPath, filter, options)
                provider.DetectChanges()
            Finally
                ' Release resources. 
                If provider IsNot Nothing Then
                    provider.Dispose()
                End If
            End Try
        End Sub

        Public Shared Sub SyncFileSystemReplicasOneWay(ByVal sourceReplicaRootPath As String, _
                                                       ByVal destinationReplicaRootPath As String, ByVal filter As FileSyncScopeFilter, _
                                                       ByVal options As FileSyncOptions)

            Dim sourceProvider As FileSyncProvider = Nothing
            Dim destinationProvider As FileSyncProvider = Nothing

            Try
                ' Instantiate source and destination providers, with a null filter (the filter
                ' was specified in DetectChangesOnFileSystemReplica()), and options for both.
                sourceProvider = New FileSyncProvider(sourceReplicaRootPath, filter, options)
                destinationProvider = New FileSyncProvider(destinationReplicaRootPath, filter, options)

                ' Register event handlers so that we can write information 
                ' to the console. 
                AddHandler destinationProvider.AppliedChange, AddressOf OnAppliedChange
                AddHandler destinationProvider.SkippedChange, AddressOf OnSkippedChange

                ' Use SyncCallbacks for conflicting items.
                Dim destinationCallbacks As SyncCallbacks = destinationProvider.DestinationCallbacks
                AddHandler destinationCallbacks.ItemConflicting, AddressOf OnItemConflicting
                AddHandler destinationCallbacks.ItemConstraint, AddressOf OnItemConstraint

                Dim agent As New SyncOrchestrator()
                agent.LocalProvider = sourceProvider
                agent.RemoteProvider = destinationProvider
                agent.Direction = SyncDirectionOrder.Upload
                ' Upload changes from the source to the destination. 
                Console.WriteLine("Synchronizing changes to replica: " & destinationProvider.RootDirectoryPath)
                agent.Synchronize()
            Finally
                ' Release resources. 
                If sourceProvider IsNot Nothing Then
                    sourceProvider.Dispose()
                End If
                If destinationProvider IsNot Nothing Then
                    destinationProvider.Dispose()
                End If
            End Try
        End Sub

        ' Provide information about files that were affected by the synchronization session. 
        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)
                    Exit Select
                Case ChangeType.Delete
                    Console.WriteLine("-- Applied DELETE for file " & args.OldFilePath)
                    Exit Select
                Case ChangeType.Update
                    Console.WriteLine("-- Applied OVERWRITE for file " & args.OldFilePath)
                    Exit Select
                Case ChangeType.Rename
                    Console.WriteLine(("-- Applied RENAME for file " & args.OldFilePath & " as ") & args.NewFilePath)
                    Exit Select
            End Select
        End Sub

        ' Provide error information for any changes that were skipped. 
        Public Shared Sub OnSkippedChange(ByVal sender As Object, ByVal args As SkippedChangeEventArgs)
            Console.WriteLine(("-- Skipped applying " & args.ChangeType.ToString().ToUpper() & " for ") & (If(Not String.IsNullOrEmpty(args.CurrentFilePath), args.CurrentFilePath, args.NewFilePath)) & " due to error")

            If args.Exception IsNot Nothing Then
                Console.WriteLine(" [" & args.Exception.Message & "]")
            End If
        End Sub

        ' By default, conflicts are resolved in favor of the last writer. In this example, 
        ' the change from the source in the first session (replica 1), will always 
        ' win the conflict. 
        Public Shared Sub OnItemConflicting(ByVal sender As Object, ByVal args As ItemConflictingEventArgs)
            args.SetResolutionAction(ConflictResolutionAction.SourceWins)
            Console.WriteLine("-- Concurrency conflict detected for item " & args.DestinationChange.ItemId.ToString())
        End Sub

        Public Shared Sub OnItemConstraint(ByVal sender As Object, ByVal args As ItemConstraintEventArgs)
            args.SetResolutionAction(ConstraintConflictResolutionAction.SourceWins)
            Console.WriteLine("-- Constraint conflict detected for item " & args.DestinationChange.ItemId.ToString())
        End Sub
    End Class
End Namespace

См. также

Справочник

Microsoft.Synchronization.Files

Основные положения

Синхронизация файлов