如何使用托管代码同步文件
本主题说明如何使用托管语言(如 C# 或 Visual Basic)创建一个应用程序,该应用程序使用 Sync Framework 文件同步提供程序来同步文件和子文件夹。
本主题中的示例着重介绍以下 Sync Framework 类型:
了解文件同步
Sync Framework 实现一个同步提供程序,可以使用该提供程序同步文件系统中某文件夹所包含的文件和子文件夹。此提供程序公开了若干可配置设置,可以进一步加强针对同步的确切方式以及要同步的项的控制。若要在两个文件夹之间同步文件,应用程序需完成以下基本步骤:
创建一个 FileSyncProvider 对象来表示各个文件夹。
将两个提供程序传递给 SyncOrchestrator 对象,并将其一指定为源提供程序,另一个指定为目标提供程序。
调用 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 的扩展名为 .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 示例中,代码显式设置 Main()
方法上的 MTAThread
属性。文件同步提供程序要求应用程序使用多线程单元 (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