public void Should_not_perform_file_delete_if_it_is_being_synced() { var filename = FileHeader.Canonize("file.bin"); var client = NewAsyncClient(); var rfs = GetFileSystem(); client.UploadAsync("file.bin", new MemoryStream(new byte[] { 1, 2, 3, 4, 5 })).Wait(); rfs.Files.IndicateFileToDelete(filename, null); rfs.Storage.Batch(accessor => accessor.SetConfigurationValue(RavenFileNameHelper.SyncLockNameForFile(filename), LockFileTests.SynchronizationConfig(DateTime.UtcNow))); rfs.Files.CleanupDeletedFilesAsync().Wait(); DeleteFileOperation deleteFile = null; rfs.Storage.Batch(accessor => deleteFile = accessor.GetConfigurationValue <DeleteFileOperation>(RavenFileNameHelper.DeleteOperationConfigNameForFile(RavenFileNameHelper.DeletingFileName(filename)))); Assert.Equal(RavenFileNameHelper.DeletingFileName(filename), deleteFile.CurrentFileName); Assert.Equal(filename, deleteFile.OriginalFileName); }
public void Should_not_delete_downloading_file_if_synchronization_retry_is_being_performed() { const string fileName = "file.bin"; var downloadingFileName = RavenFileNameHelper.DownloadingFileName(fileName); var client = NewAsyncClient(); var rfs = GetFileSystem(); client.UploadAsync(fileName, new RandomStream(1)).Wait(); client.UploadAsync(downloadingFileName, new RandomStream(1)).Wait(); rfs.Files.IndicateFileToDelete(downloadingFileName, null); rfs.Storage.Batch(accessor => accessor.SetConfigurationValue(RavenFileNameHelper.SyncLockNameForFile(fileName), LockFileTests.SynchronizationConfig(DateTime.UtcNow))); rfs.Files.CleanupDeletedFilesAsync().Wait(); DeleteFileOperation deleteFile = null; rfs.Storage.Batch(accessor => deleteFile = accessor.GetConfigurationValue <DeleteFileOperation>(RavenFileNameHelper.DeleteOperationConfigNameForFile(RavenFileNameHelper.DeletingFileName(downloadingFileName)))); Assert.Equal(RavenFileNameHelper.DeletingFileName(downloadingFileName), deleteFile.CurrentFileName); Assert.Equal(downloadingFileName, deleteFile.OriginalFileName); }
public void RegisterFileDeletion(FileHeader file, Etag etag = null) { var operation = new DeleteFileOperation(this, file.Path, etag); IncrementRequestCount(); registeredOperations.Enqueue(operation); }
public void RegisterFileDeletion(string path, Etag etag = null) { var operation = new DeleteFileOperation(this, path, etag); IncrementRequestCount(); registeredOperations.Enqueue(operation); }
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JArray ja = JArray.Load(reader); var result = new List <IRollbackableOperation>(); foreach (var jsonObject in ja) { var unit = default(IRollbackableOperation); switch (jsonObject["Type"].Value <string>()) { case "AppendAllText": unit = new AppendAllTextOperation( jsonObject["path"].Value <string>(), jsonObject["contents"].Value <string>()); break; case "Copy": unit = new CopyOperation( jsonObject["sourceFileName"].Value <string>(), jsonObject["path"].Value <string>(), jsonObject["overwrite"].Value <bool>()); break; case "CreateFile": unit = new CreateFileOperation( jsonObject["path"].Value <string>()); break; case "Delete": unit = new DeleteFileOperation( jsonObject["path"].Value <string>()); break; case "Move": unit = new MoveOperation( jsonObject["sourceFileName"].Value <string>(), jsonObject["destFileName"].Value <string>()); break; case "WriteAllText": unit = new WriteAllTextOperation( jsonObject["path"].Value <string>(), jsonObject["contents"].Value <string>()); break; } serializer.Populate(jsonObject.CreateReader(), unit); result.Add(unit); } return(result); }
public void RegisterFileDeletion(FileHeader file, Etag etag = null) { deletedEntities.Add(file.Directory); var operation = new DeleteFileOperation(this, file.Directory, etag); IncrementRequestCount(); registeredOperations.Enqueue(operation); }
public void RegisterFileDeletion(string path, Etag etag = null) { FileHeader existingEntity; if (etag == null && UseOptimisticConcurrency && entitiesByKey.TryGetValue(path, out existingEntity)) { if (IsDeleted(path) == false) // do not set etag if we already know that file was deleted etag = existingEntity.Etag; } deletedEntities.Add(path); var operation = new DeleteFileOperation(this, path, etag); IncrementRequestCount(); registeredOperations.Enqueue(operation); }
public void Should_create_apropriate_config_after_indicating_file_to_delete() { var client = NewAsyncClient(); var rfs = GetRavenFileSystem(); client.UploadAsync("toDelete.bin", new MemoryStream(new byte[] { 1, 2, 3, 4, 5 })).Wait(); rfs.StorageOperationsTask.IndicateFileToDelete("toDelete.bin"); DeleteFileOperation deleteFile = null; rfs.Storage.Batch(accessor => deleteFile = accessor.GetConfigurationValue <DeleteFileOperation>(RavenFileNameHelper.DeleteOperationConfigNameForFile(RavenFileNameHelper.DeletingFileName("toDelete.bin")))); Assert.Equal(RavenFileNameHelper.DeletingFileName("toDelete.bin"), deleteFile.CurrentFileName); Assert.Equal("toDelete.bin", deleteFile.OriginalFileName); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jo = JObject.Load(reader); var taskType = (FileOperationType)(int)jo["operationType"]; IFileOperation fileOperation; switch (taskType) { case FileOperationType.MoveFile: fileOperation = new MoveFileOperation(); break; case FileOperationType.Update: fileOperation = new UpdateFileOperation(); break; case FileOperationType.Delete: fileOperation = new DeleteFileOperation(); break; case FileOperationType.Download: fileOperation = new DownloadFileOperation(); break; case FileOperationType.DeltaPatch: fileOperation = new DeltaPatchOperation(); break; default: throw new ArgumentOutOfRangeException(); } using (JsonReader jObjectReader = CopyReaderForObject(reader, jo)) serializer.Populate(jObjectReader, fileOperation); return(fileOperation); }
public void IndicateFileToDelete(string fileName) { var deletingFileName = RavenFileNameHelper.DeletingFileName(fileName); var fileExists = true; storage.Batch(accessor => { var existingFileHeader = accessor.ReadFile(fileName); if (existingFileHeader == null) { // do nothing if file does not exist fileExists = false; return; } if (existingFileHeader.Metadata[SynchronizationConstants.RavenDeleteMarker] != null) { // if it is a tombstone drop it accessor.Delete(fileName); fileExists = false; return; } var metadata = new RavenJObject(existingFileHeader.Metadata).WithDeleteMarker(); var renameSucceeded = false; int deleteVersion = 0; do { try { accessor.RenameFile(fileName, deletingFileName); renameSucceeded = true; } catch (FileExistsException) // it means that .deleting file was already existed { var deletingFileHeader = accessor.ReadFile(deletingFileName); if (deletingFileHeader != null && deletingFileHeader.Equals(existingFileHeader)) { fileExists = false; // the same file already marked as deleted no need to do it again return; } // we need to use different name to do a file rename deleteVersion++; deletingFileName = RavenFileNameHelper.DeletingFileName(fileName, deleteVersion); } } while (!renameSucceeded && deleteVersion < 128); if (renameSucceeded) { accessor.UpdateFileMetadata(deletingFileName, metadata); accessor.DecrementFileCount(deletingFileName); Log.Debug(string.Format("File '{0}' was renamed to '{1}' and marked as deleted", fileName, deletingFileName)); var configName = RavenFileNameHelper.DeleteOperationConfigNameForFile(deletingFileName); var operation = new DeleteFileOperation { OriginalFileName = fileName, CurrentFileName = deletingFileName }; accessor.SetConfig(configName, JsonExtensions.ToJObject(operation)); notificationPublisher.Publish(new ConfigurationChangeNotification { Name = configName, Action = ConfigurationChangeAction.Set }); } else { Log.Warn("Could not rename a file '{0}' when a delete operation was performed", fileName); } }); if (fileExists) { search.Delete(fileName); search.Delete(deletingFileName); } }
public void Retry() { if (savedEndpointOperation != null) { switch (Operation) { case PNOperationType.PNAccessManagerAudit: if (savedEndpointOperation is AuditOperation) { AuditOperation endpoint = savedEndpointOperation as AuditOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNAccessManagerGrant: if (savedEndpointOperation is GrantOperation) { GrantOperation endpoint = savedEndpointOperation as GrantOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNAddChannelsToGroupOperation: if (savedEndpointOperation is AddChannelsToChannelGroupOperation) { AddChannelsToChannelGroupOperation endpoint = savedEndpointOperation as AddChannelsToChannelGroupOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNRemoveGroupOperation: if (savedEndpointOperation is DeleteChannelGroupOperation) { DeleteChannelGroupOperation endpoint = savedEndpointOperation as DeleteChannelGroupOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.ChannelGroupAllGet: if (savedEndpointOperation is ListAllChannelGroupOperation) { ListAllChannelGroupOperation endpoint = savedEndpointOperation as ListAllChannelGroupOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.ChannelGroupGet: if (savedEndpointOperation is ListChannelsForChannelGroupOperation) { ListChannelsForChannelGroupOperation endpoint = savedEndpointOperation as ListChannelsForChannelGroupOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNRemoveChannelsFromGroupOperation: if (savedEndpointOperation is RemoveChannelsFromChannelGroupOperation) { RemoveChannelsFromChannelGroupOperation endpoint = savedEndpointOperation as RemoveChannelsFromChannelGroupOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetStateOperation: if (savedEndpointOperation is GetStateOperation) { GetStateOperation endpoint = savedEndpointOperation as GetStateOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNHereNowOperation: if (savedEndpointOperation is HereNowOperation) { HereNowOperation endpoint = savedEndpointOperation as HereNowOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNSetStateOperation: if (savedEndpointOperation is SetStateOperation) { SetStateOperation endpoint = savedEndpointOperation as SetStateOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNWhereNowOperation: if (savedEndpointOperation is WhereNowOperation) { WhereNowOperation endpoint = savedEndpointOperation as WhereNowOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNFireOperation: if (savedEndpointOperation is FireOperation) { FireOperation endpoint = savedEndpointOperation as FireOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNPublishOperation: if (savedEndpointOperation is PublishOperation) { PublishOperation endpoint = savedEndpointOperation as PublishOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PushRegister: if (savedEndpointOperation is AddPushChannelOperation) { AddPushChannelOperation endpoint = savedEndpointOperation as AddPushChannelOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PushGet: if (savedEndpointOperation is AuditPushChannelOperation) { AuditPushChannelOperation endpoint = savedEndpointOperation as AuditPushChannelOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PushUnregister: if (savedEndpointOperation is RemovePushChannelOperation) { RemovePushChannelOperation endpoint = savedEndpointOperation as RemovePushChannelOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNHistoryOperation: if (savedEndpointOperation is HistoryOperation) { HistoryOperation endpoint = savedEndpointOperation as HistoryOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNFetchHistoryOperation: if (savedEndpointOperation is FetchHistoryOperation) { FetchHistoryOperation endpoint = savedEndpointOperation as FetchHistoryOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNMessageCountsOperation: if (savedEndpointOperation is MessageCountsOperation) { MessageCountsOperation endpoint = savedEndpointOperation as MessageCountsOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNTimeOperation: if (savedEndpointOperation is TimeOperation) { TimeOperation endpoint = savedEndpointOperation as TimeOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNSetUuidMetadataOperation: if (savedEndpointOperation is SetUuidMetadataOperation) { SetUuidMetadataOperation endpoint = savedEndpointOperation as SetUuidMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNSetChannelMetadataOperation: if (savedEndpointOperation is SetChannelMetadataOperation) { SetChannelMetadataOperation endpoint = savedEndpointOperation as SetChannelMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNDeleteUuidMetadataOperation: if (savedEndpointOperation is RemoveUuidMetadataOperation) { RemoveUuidMetadataOperation endpoint = savedEndpointOperation as RemoveUuidMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNDeleteChannelMetadataOperation: if (savedEndpointOperation is RemoveChannelMetadataOperation) { RemoveChannelMetadataOperation endpoint = savedEndpointOperation as RemoveChannelMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetUuidMetadataOperation: if (savedEndpointOperation is GetUuidMetadataOperation) { GetUuidMetadataOperation endpoint = savedEndpointOperation as GetUuidMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetChannelMetadataOperation: if (savedEndpointOperation is GetChannelMetadataOperation) { GetChannelMetadataOperation endpoint = savedEndpointOperation as GetChannelMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetAllUuidMetadataOperation: if (savedEndpointOperation is GetAllUuidMetadataOperation) { GetAllUuidMetadataOperation endpoint = savedEndpointOperation as GetAllUuidMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetAllChannelMetadataOperation: if (savedEndpointOperation is GetAllChannelMetadataOperation) { GetAllChannelMetadataOperation endpoint = savedEndpointOperation as GetAllChannelMetadataOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNManageMembershipsOperation: if (savedEndpointOperation is ManageMembershipsOperation) { ManageMembershipsOperation endpoint = savedEndpointOperation as ManageMembershipsOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNSetMembershipsOperation: if (savedEndpointOperation is SetMembershipsOperation) { SetMembershipsOperation endpoint = savedEndpointOperation as SetMembershipsOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNRemoveMembershipsOperation: if (savedEndpointOperation is RemoveMembershipsOperation) { RemoveMembershipsOperation endpoint = savedEndpointOperation as RemoveMembershipsOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNManageChannelMembersOperation: if (savedEndpointOperation is ManageChannelMembersOperation) { ManageChannelMembersOperation endpoint = savedEndpointOperation as ManageChannelMembersOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNSetChannelMembersOperation: if (savedEndpointOperation is SetChannelMembersOperation) { SetChannelMembersOperation endpoint = savedEndpointOperation as SetChannelMembersOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNRemoveChannelMembersOperation: if (savedEndpointOperation is RemoveChannelMembersOperation) { RemoveChannelMembersOperation endpoint = savedEndpointOperation as RemoveChannelMembersOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetMembershipsOperation: if (savedEndpointOperation is GetMembershipsOperation) { GetMembershipsOperation endpoint = savedEndpointOperation as GetMembershipsOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNGetChannelMembersOperation: if (savedEndpointOperation is GetChannelMembersOperation) { GetChannelMembersOperation endpoint = savedEndpointOperation as GetChannelMembersOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNAddMessageActionOperation: if (savedEndpointOperation is AddMessageActionOperation) { AddMessageActionOperation endpoint = savedEndpointOperation as AddMessageActionOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNRemoveMessageActionOperation: if (savedEndpointOperation is RemoveMessageActionOperation) { RemoveMessageActionOperation endpoint = savedEndpointOperation as RemoveMessageActionOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNPublishFileMessageOperation: if (savedEndpointOperation is PublishFileMessageOperation) { PublishFileMessageOperation endpoint = savedEndpointOperation as PublishFileMessageOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNFileUrlOperation: if (savedEndpointOperation is GetFileUrlOperation) { GetFileUrlOperation endpoint = savedEndpointOperation as GetFileUrlOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNListFilesOperation: if (savedEndpointOperation is ListFilesOperation) { ListFilesOperation endpoint = savedEndpointOperation as ListFilesOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNDeleteFileOperation: if (savedEndpointOperation is DeleteFileOperation) { DeleteFileOperation endpoint = savedEndpointOperation as DeleteFileOperation; if (endpoint != null) { endpoint.Retry(); } } break; case PNOperationType.PNDownloadFileOperation: if (savedEndpointOperation is DownloadFileOperation) { DownloadFileOperation endpoint = savedEndpointOperation as DownloadFileOperation; if (endpoint != null) { endpoint.Retry(); } } break; default: break; } } }
public void IndicateFileToDelete(string fileName, Etag etag) { var deletingFileName = RavenFileNameHelper.DeletingFileName(fileName); var fileExists = true; Storage.Batch(accessor => { AssertDeleteOperationNotVetoed(fileName); var existingFile = accessor.ReadFile(fileName); if (existingFile == null) { // do nothing if file does not exist fileExists = false; return; } if (existingFile.Metadata[SynchronizationConstants.RavenDeleteMarker] != null) { // if it is a tombstone drop it accessor.Delete(fileName); fileExists = false; return; } if (etag != null && existingFile.Etag != etag) { throw new ConcurrencyException("Operation attempted on file '" + fileName + "' using a non current etag") { ActualETag = existingFile.Etag, ExpectedETag = etag } } ; var metadata = new RavenJObject(existingFile.Metadata).WithDeleteMarker(); var renameSucceeded = false; int deleteVersion = 0; do { try { accessor.RenameFile(fileName, deletingFileName); renameSucceeded = true; } catch (FileExistsException) // it means that .deleting file was already existed { var deletingFileHeader = accessor.ReadFile(deletingFileName); if (deletingFileHeader != null && deletingFileHeader.Equals(existingFile)) { fileExists = false; // the same file already marked as deleted no need to do it again return; } // we need to use different name to do a file rename deleteVersion++; deletingFileName = RavenFileNameHelper.DeletingFileName(fileName, deleteVersion); } } while (!renameSucceeded && deleteVersion < 128); if (renameSucceeded) { accessor.UpdateFileMetadata(deletingFileName, metadata, null); accessor.DecrementFileCount(deletingFileName); Log.Debug("File '{0}' was renamed to '{1}' and marked as deleted", fileName, deletingFileName); var configName = RavenFileNameHelper.DeleteOperationConfigNameForFile(deletingFileName); var operation = new DeleteFileOperation { OriginalFileName = fileName, CurrentFileName = deletingFileName }; accessor.SetConfig(configName, JsonExtensions.ToJObject(operation)); FileSystem.DeleteTriggers.Apply(trigger => trigger.AfterDelete(fileName)); Publisher.Publish(new ConfigurationChangeNotification { Name = configName, Action = ConfigurationChangeAction.Set }); Publisher.Publish(new FileChangeNotification { File = fileName, Action = FileChangeAction.Delete }); Log.Debug("File '{0}' was deleted", fileName); } else { Log.Warn("Could not rename a file '{0}' when a delete operation was performed", fileName); } }); if (fileExists) { Search.Delete(fileName); Search.Delete(deletingFileName); } }
public void Clone(FileSystemSite source, FileSystemSite destination, FileSystemCloningOptions options) { // Ensure the source and destination directories are directory indicated. var ensuredSource = source.EnsureSiteDirectoryPathIsDirectoryIndicated(this.StringlyTypedPathOperator); var ensuredDestination = destination.EnsureSiteDirectoryPathIsDirectoryIndicated(this.StringlyTypedPathOperator); // Get all source file-system entries. var sourceFileSystemEntries = ensuredSource.FileSystemOperator.EnumerateFileSystemEntries(ensuredSource.DirectoryPath, true) .ToList(); // Get all destination file-system entries. var destinationFileSystemEntries = ensuredDestination.FileSystemOperator.EnumerateFileSystemEntries(ensuredDestination.DirectoryPath, true) .ToList(); // Create relative-path source and destination file-system entries. FileSystemEntry MakeRelativeEntry(string baseDirectoryPath, FileSystemEntry entry) { var sourceBaseDirectoryRelativeEntryPath = this.StringlyTypedPathOperator.GetRelativePath(baseDirectoryPath, entry.Path); var relativeEntry = FileSystemEntry.New(sourceBaseDirectoryRelativeEntryPath, entry.Type, entry.LastModifiedUTC); return(relativeEntry); } var sourceBaseDirectoryRelativePathEntries = sourceFileSystemEntries.Select(entry => MakeRelativeEntry(ensuredSource.DirectoryPath, entry)) .Select(fileSystemEntry => { // Make sure we are using a common path format. var standardPathFileSystemEntry = fileSystemEntry.GetStandardPathFormatEntry(this.StringlyTypedPathOperator); return(standardPathFileSystemEntry); }) .ToList(); var destinationBaseDirectoryRelativePathEntries = destinationFileSystemEntries.Select(entry => MakeRelativeEntry(ensuredDestination.DirectoryPath, entry)) .Select(fileSystemEntry => { // Make sure we are using a common path format. var standardPathFileSystemEntry = fileSystemEntry.GetStandardPathFormatEntry(this.StringlyTypedPathOperator); return(standardPathFileSystemEntry); }) .ToList(); // Write out source and destination data. // Get the file-system cloning difference. var difference = this.FileSystemCloningDifferencer.PerformDifference(sourceBaseDirectoryRelativePathEntries, destinationBaseDirectoryRelativePathEntries, options); // Create a list of operations, using absolute paths. var operations = new List <IFileSystemCloningOperation>(); // Special case: the destination directory does not exist. If so, make sure it is created first to allow files to be copied into it! var destinationDirectoryExists = ensuredDestination.FileSystemOperator.ExistsDirectory(ensuredDestination.DirectoryPath); if (!destinationDirectoryExists) { var createDestinationDirectoryOperation = new CreateDirectoryOperation(ensuredDestination.DirectoryPath); operations.Add(createDestinationDirectoryOperation); } foreach (var directoryToCreate in difference.RelativeDirectoryPathsToCreate) { string destinationDirectoryToCreate = this.StringlyTypedPathOperator.Combine(ensuredDestination.DirectoryPath, directoryToCreate); var createDirectoryOperation = new CreateDirectoryOperation(destinationDirectoryToCreate); operations.Add(createDirectoryOperation); } foreach (var directoryToDelete in difference.RelativeDirectoryPathsToDelete) { string destinationDirectoryToDelete = this.StringlyTypedPathOperator.Combine(ensuredDestination.DirectoryPath, directoryToDelete); var deleteDirectoryOperation = new DeleteDirectoryOperation(destinationDirectoryToDelete); operations.Add(deleteDirectoryOperation); } foreach (var fileToCopy in difference.RelativeFilePathsToCopy) { string sourceFilePath = this.StringlyTypedPathOperator.Combine(ensuredSource.DirectoryPath, fileToCopy); string destinationFilePath = this.StringlyTypedPathOperator.Combine(ensuredDestination.DirectoryPath, fileToCopy); var copyFileOperation = new CopyFileOperation(sourceFilePath, destinationFilePath); operations.Add(copyFileOperation); } foreach (var fileToUpdate in difference.RelativeFilePathsToUpdate) { string sourceFilePath = this.StringlyTypedPathOperator.Combine(ensuredSource.DirectoryPath, fileToUpdate); string destinationFilePath = this.StringlyTypedPathOperator.Combine(ensuredDestination.DirectoryPath, fileToUpdate); var copyFileOperation = new CopyFileOperation(sourceFilePath, destinationFilePath); operations.Add(copyFileOperation); } foreach (var fileToDelete in difference.RelativeFilePathsToDelete) { string destinationFilePath = this.StringlyTypedPathOperator.Combine(ensuredDestination.DirectoryPath, fileToDelete); var deleteFileOperation = new DeleteFileOperation(destinationFilePath); operations.Add(deleteFileOperation); } // Write out and allow approval of the list of operations? // Execute the list of operations. foreach (var operation in operations) { operation.Execute(ensuredSource.FileSystemOperator, ensuredDestination.FileSystemOperator); } }