Synchronizing File Synchronization Provider with Other Providers

This topic describes how to create a managed application that enables a file synchronization provider to synchronize with another Sync Framework provider. In this case the other provider is a simple provider, but you could also use a standard custom provider. For more information about simple providers, see Implementing a Simple Custom Provider.

There are two main requirements for the application:

  • The application must implement IFileDataRetriever as the data transfer interface between the two providers.

  • The simple provider must use the same format for IDs that file synchronization provider uses: a 16-byte GUID for replica IDs; A 16 byte GUID that is preceded by an 8-byte prefix for item IDs; and a 4-byte integer for change unit IDs.

Managed Code Examples

The examples in this section show some of the code that is relevant to synchronizing a file synchronization provider and a simple provider, with a focus on the ID and transfer interface requirements. To view this code in the context of a complete application, see the "Sync 101 - Synchronizing a File Synchronization Provider with a Simple Provider" application that is available on Code Gallery.

The following code example shows how the definition of the IdFormats property for the simple provider matches the requirements stated earlier.

public override SyncIdFormatGroup IdFormats
        SyncIdFormatGroup idFormats = new SyncIdFormatGroup();
        idFormats.ItemIdFormat.Length = 24;
        idFormats.ItemIdFormat.IsVariableLength = false;
        idFormats.ReplicaIdFormat.Length = 16;
        idFormats.ReplicaIdFormat.IsVariableLength = false;
        idFormats.ChangeUnitIdFormat.Length = 4;
        idFormats.ChangeUnitIdFormat.IsVariableLength = false;

        return idFormats;

The following code examples show two of the methods that are required for simple providers: LoadChangeData and InsertItem. Much of the code in these methods is specific to implementing a simple provider, but there are two areas to note:

  • LoadChangeData is used to load data that has been enumerated from the local store (the store that is serviced by the simple provider). This method returns data as a SimpleFileDataRetriever object, which is the sample's implementation of IFileDataRetriever.

    public override object LoadChangeData(ItemFieldDictionary keyAndExpectedVersion,
        IEnumerable<SyncId> changeUnitsToLoad,
        RecoverableErrorReportingContext recoverableErrorReportingContext)
        // Figure out which item is being asked for
        string localRelativePath;
        long expectedLMT;
        ParseDictionary(keyAndExpectedVersion, out localRelativePath, out expectedLMT);
        string localPath = Path.Combine(this.rootFolder, localRelativePath);
        string currentVersion = File.GetLastWriteTimeUtc(localPath).Ticks.ToString();
        // Check if it changed --- race condition!
        if (File.GetLastWriteTimeUtc(localPath).Ticks != expectedLMT)
                new RecoverableErrorData(null));
            return null;
        // Return
        return new SimpleFileDataRetriever(localRelativePath, null, localPath, File.GetAttributes(localPath));
  • InsertItem is used to insert data into the local store from the remote store (the store that is serviced by the file synchronization provider). This method casts the item data that it receives into an IFileDataRetriever object. The InsertItem method also casts the data.

    public override void InsertItem(object itemData,
        IEnumerable<SyncId> changeUnitsToCreate,
        RecoverableErrorReportingContext recoverableErrorReportingContext,
        out ItemFieldDictionary keyAndUpdatedVersion,
        out bool commitKnowledgeAfterThisItem)
        // Figure out where to create it
        IFileDataRetriever fileData = (IFileDataRetriever)itemData;
        string localPath = Path.Combine(this.rootFolder, Path.Combine(fileData.RelativeDirectoryPath, fileData.FileData.Name));
        // Check if it is already there --- name collision
        if (File.Exists(localPath))
                ConstructDictionary(Path.Combine(fileData.RelativeDirectoryPath, fileData.FileData.Name)));
            keyAndUpdatedVersion = null;
            commitKnowledgeAfterThisItem = false;
        // Create it
        File.Copy(fileData.AbsoluteSourceFilePath, localPath);
        // Return particulars to Simple Provider framework
        keyAndUpdatedVersion = ConstructDictionary(
            Path.Combine(fileData.RelativeDirectoryPath, fileData.FileData.Name),
        commitKnowledgeAfterThisItem = false;

The following code example creates the SimpleFileDataRetriever class, which uses AbsoluteSourceFilePath and RelativeDirectoryPath to identify file locations, and FileData and FileStream to transfer the actual data.

class SimpleFileDataRetriever : IFileDataRetriever, IDisposable
    private string _relativeLocalFilePath;
    private Stream _sourceStream;
    private string _absoluteSourceFilePath;
    private FileAttributes _attributes;

    public SimpleFileDataRetriever(string relativeLocalFilePath, Stream sourceStream, string absoluteSourceFilePath, FileAttributes attributes)
        this._relativeLocalFilePath = relativeLocalFilePath;
        this._sourceStream = sourceStream;
        this._attributes = attributes;
        this._absoluteSourceFilePath = absoluteSourceFilePath;

    #region IFileDataRetriever Members

    // If the local store has no concept of absolute file path then return a NotImplementedException here.  
    // The FSP will instead use the stream for file copying.
    // If implemented, return absolute local path including file name.
    public string AbsoluteSourceFilePath
            return this._absoluteSourceFilePath; 

    public FileData FileData
            FileInfo fi = new FileInfo(_absoluteSourceFilePath);

            //For the relative path on FileData, provide relative path including file name
            return new FileData(

    public System.IO.Stream FileStream
            if (this._sourceStream == null)
                this._sourceStream = new FileStream(this._absoluteSourceFilePath, FileMode.Open);

            return _sourceStream; 

    // Must return the relative path without the filename
    public string RelativeDirectoryPath
            return Path.GetDirectoryName(_relativeLocalFilePath);


    #region IDisposable Members

    public void Dispose()
        if (this._sourceStream != null)


The following code example synchronizes the two providers. The synchronization process is the same as if you synchronized two file synchronization providers or two simple providers: implementing the IFileDataRetriever interface and using the appropriate ID formats ensures that data is transferred in the correct way.

static void DoBidirectionalSync(string pathA, Guid replicaA, string pathB, Guid replicaB)
    SyncOperationStatistics stats;
    MySimpleFileProvider providerA = new MySimpleFileProvider(replicaA, pathA);
    FileSyncProvider providerB = new FileSyncProvider(replicaB, pathB);

    //Set the custom provider's conflict resolution policy to custom in order to show 
    //how to perform complex resolution actions.
    providerA.Configuration.ConflictResolutionPolicy = ConflictResolutionPolicy.ApplicationDefined;

    //Register callbacks so that we can handle conflicts if they are detected, and other events.

    //Synchronize the two providers that are specified.           
    Console.WriteLine("Sync {0} and {1}...", pathA, pathB);
    SyncOrchestrator agent = new SyncOrchestrator();

    //To avoid writing conflict resolution logic in your matching provider it is good practice to always sync from custom provider
    //to FSP provider first.  That way the FSP will handle all the conflicts itself.  Here we do the opposite to show our custom
    //constraint conflict resolution.
    agent.Direction = SyncDirectionOrder.UploadAndDownload;

    agent.LocalProvider = providerB;
    agent.RemoteProvider = providerA;
    stats = agent.Synchronize();

    //Display the statistics from the SyncOperationStatistics object that is returned 
    //by Synchronize().
    Console.WriteLine("Download Applied:\t {0}", stats.DownloadChangesApplied);
    Console.WriteLine("Download Failed:\t {0}", stats.DownloadChangesFailed);
    Console.WriteLine("Download Total:\t\t {0}", stats.DownloadChangesTotal);
    Console.WriteLine("Upload Total:\t\t {0}", stats.UploadChangesApplied);
    Console.WriteLine("Upload Total:\t\t {0}", stats.UploadChangesFailed);
    Console.WriteLine("Upload Total:\t\t {0}", stats.UploadChangesTotal);

See Also