Beispiel #1
0
        /// <summary>
        /// This is an extension of the Sync101 sample showing how to write a provider which implements
        /// the RCA ("remote change application") pattern. Implementing the RCA pattern allows us
        /// to synchronize with providers on remote machines. In this sample we use WCF as our transport
        /// layer.
        ///
        /// Please note that this sample is most useful with breakpoints in MyTestProgram.cs to find out HOW synchronization using the
        /// Microsoft Sync Framework works. This sample is not designed to be a boot-strapper like the NTFS providers for native and managed...
        /// </summary>
        /// <param name="args"></param>
        public static void Main(string[] args)
        {
            const string DATA = "data";

            //start clean
            CleanUpProvider(providerNameA);
            CleanUpProvider(providerNameB);
            CleanUpProvider(providerNameC);

            //Initialize the stores
            // We create all the stores on the same machine in this sample app. However we will
            // sync with B and C using the remote change application pattern over WCF as if they
            // were on a remote machine.
            // For a real deployment, B and C would be created on remote machines.
            MySimpleDataStore storeA = new MySimpleDataStore();
            MySimpleDataStore storeB = new MySimpleDataStore();
            MySimpleDataStore storeC = new MySimpleDataStore();

            //Create items on each store using a global ID and the data of the item (Note, in order to verify results against
            //a baseline we are using hardcoded global ids for the items. In a real application CreateItem should be called without
            //the Guid parameter, which would generate a new Guid id and return it.
            Guid[] itemIds = new Guid[7];
            itemIds[1] = new Guid("11111111-1111-1111-1111-111111111111");
            itemIds[2] = new Guid("22222222-2222-2222-2222-222222222222");
            itemIds[3] = new Guid("33333333-3333-3333-3333-333333333333");
            itemIds[4] = new Guid("44444444-4444-4444-4444-444444444444");
            itemIds[5] = new Guid("55555555-5555-5555-5555-555555555555");
            itemIds[6] = new Guid("66666666-6666-6666-6666-666666666666");

            storeA.CreateItem(new ItemData(DATA, "1"), itemIds[1]);
            storeA.CreateItem(new ItemData(DATA, "2"), itemIds[2]);
            storeB.CreateItem(new ItemData(DATA, "3"), itemIds[3]);
            storeB.CreateItem(new ItemData(DATA, "4"), itemIds[4]);
            storeC.CreateItem(new ItemData(DATA, "5"), itemIds[5]);
            storeC.CreateItem(new ItemData(DATA, "6"), itemIds[6]);

            // Save the stores to disk (this is a demo app, in a real deployment,
            // stores B and C would be on different machines and would not be
            // accessed directly by the local app).
            storeA.WriteStoreToFile(folderPathForDataAndMetadata, providerNameA);
            storeB.WriteStoreToFile(folderPathForDataAndMetadata, providerNameB);
            storeC.WriteStoreToFile(folderPathForDataAndMetadata, providerNameC);

            //Show the contents of the stores, prior to ANY any synchronization...
            Console.WriteLine("Show the contents of the stores, prior to any synchronization...");
            Console.WriteLine(new MySyncProvider(folderPathForDataAndMetadata, providerNameA).ToString());
            Console.WriteLine(storeA.ToString());
            Console.WriteLine(new MySyncProvider(folderPathForDataAndMetadata, providerNameB).ToString());
            Console.WriteLine(storeB.ToString());
            Console.WriteLine(new MySyncProvider(folderPathForDataAndMetadata, providerNameC).ToString());
            Console.WriteLine(storeC.ToString());

            //Sync providers A and provider B
            DoBidirectionalSync(providerNameA, providerNameB);

            //Create an update-update conflict on item 1 ...
            Console.WriteLine("A and B are going to create a conflict on item 1. A writes \"UpdateA\" and B writes \"UpdateB\"");
            Console.WriteLine("We do Download first then Upload.");
            Console.WriteLine("Since the policy is DestinationWins, we expect UpdateA to win during the Download and the resolution to be uploaded back to B.");

            storeA = MySimpleDataStore.ReadStoreFromFile(folderPathForDataAndMetadata, providerNameA);
            storeB = MySimpleDataStore.ReadStoreFromFile(folderPathForDataAndMetadata, providerNameB);
            storeA.UpdateItem(itemIds[1], new ItemData(DATA, "UpdateA"));
            storeB.UpdateItem(itemIds[1], new ItemData(DATA, "UpdateB"));
            storeA.WriteStoreToFile(folderPathForDataAndMetadata, providerNameA);
            storeB.WriteStoreToFile(folderPathForDataAndMetadata, providerNameB);

            //Sync providers A and provider B
            //We do Download first then Upload. Since the policy is DestinationWins,
            //we expect UpdateA to win during the Download and be uploaded back to B.
            DoBidirectionalSync(providerNameA, providerNameB);

            //Delete an item on B and show that the delete propagates
            Console.WriteLine("Deleting item '4' on B");
            storeB = MySimpleDataStore.ReadStoreFromFile(folderPathForDataAndMetadata, providerNameB);
            storeB.DeleteItem(itemIds[4]);
            storeB.WriteStoreToFile(folderPathForDataAndMetadata, providerNameB);

            //Sync B and C
            DoBidirectionalSync(providerNameB, providerNameC);

            //Close the sync loop by syncing A and C
            Console.WriteLine("Closing the \"sync loop\" by syncing A and C...");
            DoBidirectionalSync(providerNameA, providerNameC);

            //Delete item 2 on B
            Console.WriteLine("\nDeleting item '2' on B.");
            storeB = MySimpleDataStore.ReadStoreFromFile(folderPathForDataAndMetadata, providerNameB);
            storeB.DeleteItem(itemIds[2]);
            storeB.WriteStoreToFile(folderPathForDataAndMetadata, providerNameB);

            //Cleanup Tombstones on B
            Console.WriteLine("\nClean up Tombstones on B.");
            new RemoteProviderProxy(
                folderPathForDataAndMetadata,
                providerNameB,
                endpointConfigurationName).CleanupTombstones(TimeSpan.Zero);

            // Sync B and C
            DoBidirectionalSync(providerNameB, providerNameC);

            //Close the sync loop by syncing A and C
            DoBidirectionalSync(providerNameA, providerNameC);

            List <string> arguments = new List <string>(args);

            if (arguments.Contains("-q") != true)
            {
                Console.WriteLine("Sync has finished...");
                Console.ReadLine();
            }
        }
        //Save the item, taking the appropriate action for the 'change' and the data from the item (in 'context')
        public void SaveItemChange(SaveChangeAction saveChangeAction, ItemChange change, SaveChangeContext context)
        {
            ulong        timeStamp = 0;
            ItemMetadata item      = null;
            ItemData     data      = null;

            switch (saveChangeAction)
            {
            case SaveChangeAction.Create:
                //Do duplicate detection here
                item = _metadata.FindItemMetadataById(change.ItemId);
                if (null != item)
                {
                    throw new Exception("SaveItemChange must not have Create action for existing items.");
                }
                item = _metadata.CreateItemMetadata(change.ItemId, change.CreationVersion);
                item.ChangeVersion = change.ChangeVersion;
                data = new ItemData((ItemData)context.ChangeData);
                //We are using the same id for both the local and global item id.
                _store.CreateItem(data, change.ItemId.GetGuidId());
                SaveItemMetadata(item, _store.Get(change.ItemId.GetGuidId()).TimeStamp);
                break;

            case SaveChangeAction.UpdateVersionAndData:
            case SaveChangeAction.UpdateVersionOnly:
                item = _metadata.FindItemMetadataById(change.ItemId);
                if (null == item)
                {
                    throw new Exception("Item Not Found in Store!?");
                }

                item.ChangeVersion = change.ChangeVersion;
                if (saveChangeAction == SaveChangeAction.UpdateVersionOnly)
                {
                    SaveItemMetadata(item);
                }
                else      //Also update the data and the timestamp.
                {
                    data      = new ItemData((ItemData)context.ChangeData);
                    timeStamp = _store.UpdateItem(item.GlobalId.GetGuidId(), data);
                    SaveItemMetadata(item, timeStamp);
                }
                break;

            case SaveChangeAction.DeleteAndStoreTombstone:
                item = _metadata.FindItemMetadataById(change.ItemId);
                if (null == item)
                {
                    item = _metadata.CreateItemMetadata(change.ItemId, change.CreationVersion);
                }

                if (change.ChangeKind == ChangeKind.Deleted)
                {
                    item.MarkAsDeleted(change.ChangeVersion);
                }
                else
                {
                    // This should never happen in Sync Framework V1.0
                    throw new Exception("Invalid ChangeType");
                }

                item.ChangeVersion = change.ChangeVersion;
                SaveItemMetadata(item, 0);      // set timestamp to 0 for tombstones
                _store.DeleteItem(item.GlobalId.GetGuidId());
                break;

            //Merge the changes! (Take the data from the local item + the remote item),noting to update the tick count to propagate the resolution!
            case SaveChangeAction.UpdateVersionAndMergeData:
                item = _metadata.FindItemMetadataById(change.ItemId);

                if (null == item)
                {
                    throw new Exception("Item Not Found in Store!?");
                }
                if (item.IsDeleted != true)
                {
                    //Note - you must update the change version to propagate the resolution!
                    item.ChangeVersion = new SyncVersion(0, _metadata.GetNextTickCount());

                    //Combine the conflicting data...
                    ItemData mergedData = (_store.Get(item.GlobalId.GetGuidId())).Merge((ItemData)context.ChangeData);
                    timeStamp = _store.UpdateItem(item.GlobalId.GetGuidId(), mergedData);
                    SaveItemMetadata(item, timeStamp);
                }
                break;

            case SaveChangeAction.DeleteAndRemoveTombstone:
                item = _metadata.FindItemMetadataById(change.ItemId);
                if (item != null)
                {
                    List <SyncId> ids = new List <SyncId>();
                    ids.Add(item.GlobalId);
                    _metadata.RemoveItemMetadata(ids);
                }
                _store.DeleteItem(change.ItemId.GetGuidId());
                break;
            }
        }