internal void UpdateWithLocalInfo(ConflictHandling conflictHandling, Guid correlationId) { Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) Name={0}, LocalFolder={1}, Type={2}", Name, LocalFolder, Type); if (Type == ItemType.File) { var mod = File.GetLastWriteTimeUtc(Path.Combine(LocalFolder, Name)); if (mod == LastModified) { // means something went wrong on initial sync, so reset status to updatedlocal if (SharePointId == -1) { Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) Possibly error: LastWriteTime=LastModified but SharePointId=-1"); Status = ItemStatus.UpdatedLocal; } else { Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) Nothing to do"); } return; } if (mod > LastModified) { Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) File is greater than metadata LastModified. Status={0}", Status); if (Status == ItemStatus.UpdatedRemote || Status == ItemStatus.Conflict) { switch (conflictHandling) { case ConflictHandling.ManualConflictHandling: ConflictData = new ConflictData() { LocalLastModified = mod, RemoteLastModified = LastModified }; Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) Set status to 'Conflict'"); Status = ItemStatus.Conflict; return; case ConflictHandling.OverwriteLocalChanges: Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) Set status to 'UpdatedRemote'"); Status = ItemStatus.UpdatedRemote; return; case ConflictHandling.OverwriteRemoteChanges: break; } } Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) Set status to 'UpdatedLocal' and LastModified to {0}", mod); LastModified = mod; Status = ItemStatus.UpdatedLocal; } else { Logger.LogDebug(correlationId, Id, "(UpdateWithLocalInfo) File is older than metadata LastModified. Status set to 'UpdatedRemote'"); Status = ItemStatus.UpdatedRemote; } } }
private void buttonTest_Click(object sender, RoutedEventArgs e) { ConflictHandling ch = new ConflictHandling(obj1, obj2); ch.analyzeObjects(); ch.printConflicts(); }
public void DeleteItem(string tableName, TableItem tableItem, ConflictHandling conflictHandling) { var genericTableEntity = GenericTableEntity.HydrateFrom(tableItem); if (ShouldForceOverwrite(conflictHandling, genericTableEntity)) { genericTableEntity.ETag = "*"; } Action <MemoryStorageAccount> action = tables => tables.GetTable(tableName).GetPartition(tableItem.PartitionKey).Delete(genericTableEntity); _pendingActions.Enqueue(new TableAction(action, tableItem.PartitionKey, tableItem.RowKey, tableName)); }
public void DeleteItem(string tableName, TableItem tableItem, ConflictHandling conflictHandling) { var genericTableEntity = GenericTableEntity.HydrateFrom(tableItem); if (ShouldForceOverwrite(conflictHandling, genericTableEntity)) { genericTableEntity.ETag = "*"; } var operation = TableOperation.Delete(genericTableEntity); _operations.Enqueue(new ExecutableTableOperation(tableName, operation, TableOperationType.Delete, tableItem.PartitionKey, tableItem.RowKey)); }
/// <summary> /// Remove the instance from table storage /// </summary> /// <param name="tableName">Name of the table</param> /// <param name="instance">the instance to delete</param> /// <param name="conflictHandling">Method for handling ETag conflicts</param> public void Delete(string tableName, dynamic instance, ConflictHandling conflictHandling) { TableItem tableItem = TableItem.Create(instance, _reservedPropertyBehavior); if (tableItem.ETag == null) { Delete(tableName, tableItem.PartitionKey, tableItem.RowKey); } else { _context.DeleteItem(tableName, tableItem, conflictHandling); } }
/// <summary> /// Merge the entity /// </summary> /// <param name="tableName">Name of the table</param> /// <param name="instance">the instance to merge</param> /// <param name="conflictHandling">Method for handling ETag conflicts</param> public void Merge(string tableName, dynamic instance, ConflictHandling conflictHandling) { _context.Merge(tableName, TableItem.Create(instance, _reservedPropertyBehavior), conflictHandling); }
/// <summary> /// Update the entity with specified partition key and row key in table storage /// </summary> /// <param name="tableName">Name of the table</param> /// <param name="instance">the instance to update</param> /// <param name="partitionKey">The partition key to use when storing the entity</param> /// <param name="rowKey">The row key to use when storing the entity</param> /// <param name="conflictHandling">Method for handling ETag conflicts</param> public void Update(string tableName, dynamic instance, string partitionKey, string rowKey, ConflictHandling conflictHandling) { _context.Update(tableName, TableItem.Create(instance, partitionKey, rowKey, _reservedPropertyBehavior), conflictHandling); }
internal void UpdateWithRemoteInfo(int sharePointId, int etag, DateTime lastModified, ConflictHandling conflictHandling, Guid correlationId) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) SharePointId={0}, ETag={1}, Name={2}, LocalFolder={3}, Type={4}, CurrentStatus={5}", sharePointId, etag, Name, LocalFolder, Type, Status); if (Type == ItemType.File) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) SharePointId old={0}, SharePointId new={1}", SharePointId, sharePointId); SharePointId = sharePointId; if (ETag == -1) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Current ETag=-1, new ETag={0}", etag); ETag = etag; if (lastModified > LastModified) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) RemoteLastModified is greater than current LastModified. Status={0}", Status); if (Status == ItemStatus.UpdatedLocal || Status == ItemStatus.Conflict) { switch (conflictHandling) { case ConflictHandling.ManualConflictHandling: ConflictData = new ConflictData() { LocalLastModified = LastModified, RemoteLastModified = lastModified }; Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Set status to 'Conflict'"); Status = ItemStatus.Conflict; return; case ConflictHandling.OverwriteRemoteChanges: Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Set status to 'UpdatedLocal'"); Status = ItemStatus.UpdatedLocal; return; case ConflictHandling.OverwriteLocalChanges: break; } } Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Set status to 'UpdatedRemote' and LastModified to {0}", lastModified); LastModified = lastModified; Status = ItemStatus.UpdatedRemote; return; } if (lastModified < LastModified) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) RemoteLastModified is less than current LasModified. Set status to 'UpdatedLocal'"); Status = ItemStatus.UpdatedLocal; return; } Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) LastModified dates are equal"); Status = ItemStatus.Unchanged; return; } if (File.Exists(Path.Combine(LocalFolder, Name) + ".spsync")) { Status = ItemStatus.Unchanged; return; } if (ETag < etag) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Current ETag is less than remote etag. Status={0}", Status); if (Status == ItemStatus.UpdatedLocal || Status == ItemStatus.Conflict) { switch (conflictHandling) { case ConflictHandling.ManualConflictHandling: ConflictData = new ConflictData() { LocalLastModified = LastModified, RemoteLastModified = lastModified }; Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Set status to 'Conflict'"); Status = ItemStatus.Conflict; return; case ConflictHandling.OverwriteRemoteChanges: Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Set status to 'UpdatedLocal'"); Status = ItemStatus.UpdatedLocal; return; case ConflictHandling.OverwriteLocalChanges: break; } } Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Set status to 'UpdatedRemote' and ETag to {0}", etag); ETag = etag; Status = ItemStatus.UpdatedRemote; return; } if (ETag > etag) { Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) Current ETag is greater than remote etag. Set Status to 'UpdatedLocal'"); Status = ItemStatus.UpdatedLocal; return; } Logger.LogDebug(correlationId, Id, "(UpdateWithRemoteInfo) ETags are equal."); } }
private int SyncMetadataStore(bool doNotSave, ConflictHandling conflictHandling, out bool moreChangesFound, bool rescanLocalFiles = true) { var sumWatch = Stopwatch.StartNew(); var correlationId = Guid.NewGuid(); //reset item status for all items except the ones with errors _metadataStore.Items.Where(p => p.Status != ItemStatus.Conflict).ToList().ForEach(p => { p.Status = ItemStatus.Unchanged; p.HasError = false; }); var watch = Stopwatch.StartNew(); var remoteFileList = _sharePointManager.GetChangedFiles(_metadataStore, (percent, currentFile) => { OnSyncProgress(percent, ProgressStatus.Analyzing, $"Processing remote changes... '{currentFile}'"); }); watch.Stop(); var syncToRemote = (_configuration.Direction == SyncDirection.LocalToRemote || _configuration.Direction == SyncDirection.Both); var syncToLocal = _configuration.Direction == SyncDirection.RemoteToLocal || _configuration.Direction == SyncDirection.Both; var searchOption = SearchOption.AllDirectories; if (rescanLocalFiles) { OnSyncProgress(0, ProgressStatus.Analyzing, "Processing local changes"); #region Iterate local files/folders watch = Stopwatch.StartNew(); Parallel.ForEach(Directory.EnumerateDirectories(_localFolder, "*", searchOption), localFolder => { if (!syncToRemote) { return; } if (!_configuration.ShouldFileSync(localFolder)) { return; } if (File.GetAttributes(localFolder).HasFlag(FileAttributes.Hidden)) { return; } if (Path.GetDirectoryName(localFolder) == MetadataStore.STOREFOLDER) { return; } var item = _metadataStore.GetByFileName(localFolder); if (item == null) { _metadataStore.Add(new MetadataItem(localFolder, ItemType.Folder)); } else { item.UpdateWithLocalInfo(conflictHandling, correlationId); if (item.Status == ItemStatus.Conflict) { item.Status = OnItemConflict(item); } } }); watch.Stop(); watch = Stopwatch.StartNew(); // update store for local files Parallel.ForEach(Directory.EnumerateFiles(_localFolder, "*.*", searchOption), localFile => //foreach (var localFile in Directory.EnumerateFiles(_localFolder, "*.*", searchOption)) { if (!syncToRemote) { return; } if (!_configuration.ShouldFileSync(localFile)) { return; } var localExtension = Path.GetExtension(localFile); if (localExtension == ".spsync") { return; } if (File.GetAttributes(localFile).HasFlag(FileAttributes.Hidden)) { return; } if (Directory.GetParent(localFile).Name == MetadataStore.STOREFOLDER) { return; } var item = _metadataStore.GetByFileName(localFile); if (item == null) { _metadataStore.Add(new MetadataItem(localFile, ItemType.File)); } else { item.UpdateWithLocalInfo(conflictHandling, correlationId); if (item.Status == ItemStatus.Conflict) { item.Status = OnItemConflict(item); } } }); watch.Stop(); #endregion } #region Iterate remote files/folders // update store for remote files/folders foreach (var remoteItem in remoteFileList) { if (!syncToLocal) { continue; } var localFile = new DirectoryInfo(Path.Combine(_localFolder, remoteItem.FullFileName)).FullName; if (!_configuration.ShouldFileSync(localFile)) { continue; } string fn = localFile; if (remoteItem.Type == ItemType.Folder) { fn = Path.Combine(localFile, remoteItem.Name); } var item = _metadataStore.GetByItemId(remoteItem.Id); if (remoteItem.ChangeType == Microsoft.SharePoint.Client.ChangeType.Add) { // new if (item == null) { _metadataStore.Add(new MetadataItem(remoteItem.Id, remoteItem.ETag, remoteItem.Name, Path.GetDirectoryName(localFile), remoteItem.LastModified, remoteItem.Type)); } // remote and local change else { item.UpdateWithRemoteInfo(remoteItem.Id, remoteItem.ETag, remoteItem.LastModified, conflictHandling, correlationId); if (item.Status == ItemStatus.Conflict) { item.Status = OnItemConflict(item); } } } if (remoteItem.ChangeType == Microsoft.SharePoint.Client.ChangeType.DeleteObject) { if (item != null) { item.Status = ItemStatus.DeletedRemote; } } if (remoteItem.ChangeType == Microsoft.SharePoint.Client.ChangeType.Rename) { if (item == null) { _metadataStore.Add(new MetadataItem(remoteItem.Id, remoteItem.ETag, remoteItem.Name, Path.GetDirectoryName(localFile), remoteItem.LastModified, remoteItem.Type)); } else { if (item.Name != remoteItem.Name) { item.NewNameAfterRename = remoteItem.Name; item.Status = ItemStatus.RenamedRemote; if (item.Type == ItemType.Folder) { foreach (var itemInFolder in _metadataStore.Items.Where(p => p.LocalFile.Contains(item.LocalFile))) { if (itemInFolder.Id == item.Id) { continue; } var newFolder = _localFolder + remoteItem.FullFileName.Substring(1); itemInFolder.LocalFolder = itemInFolder.LocalFolder.Replace(item.LocalFile, newFolder); itemInFolder.HasError = true; } } } else { item.Status = ItemStatus.Unchanged; } } } if (remoteItem.ChangeType == Microsoft.SharePoint.Client.ChangeType.Update) { // new if (item == null) { _metadataStore.Add(new MetadataItem(remoteItem.Id, remoteItem.ETag, remoteItem.Name, Path.GetDirectoryName(localFile), remoteItem.LastModified, remoteItem.Type)); } else { item.UpdateWithRemoteInfo(remoteItem.Id, remoteItem.ETag, remoteItem.LastModified, conflictHandling, correlationId); if (item.Status == ItemStatus.Conflict) { item.Status = OnItemConflict(item); } } } } #endregion #region Check for deleted files/folders var itemsToDelete = new List <Guid>(); // update store: files foreach (var item in _metadataStore.Items.Where(p => p.Status == ItemStatus.Unchanged && p.Type == ItemType.File && !p.HasError)) { var path = item.LocalFile; if (!_configuration.ShouldFileSync(path)) { continue; } if (!File.Exists(path) && !File.Exists(path + ".spsync")) { item.Status = ItemStatus.DeletedLocal; } //if (remoteFileList.Count(p => p.Id == item.SharePointId) < 1) //{ // if (item.Status == ItemStatus.DeletedLocal) // { // itemsToDelete.Add(item.Id); // } // item.Status = ItemStatus.DeletedRemote; //} } // update store: folders foreach (var item in _metadataStore.Items.Where(p => p.Status == ItemStatus.Unchanged && p.Type == ItemType.Folder && !p.HasError)) { var relFile = item.LocalFile.Replace(_localFolder, string.Empty).TrimStart('.', '\\'); var path = item.LocalFile; if (!_configuration.ShouldFileSync(path)) { continue; } if (!Directory.Exists(path)) { item.Status = ItemStatus.DeletedLocal; // delete all dependend items _metadataStore.Items.Where(p => p.LocalFolder == item.LocalFile).ToList().ForEach(p => { if (!itemsToDelete.Contains(p.Id)) { itemsToDelete.Add(p.Id); } }); } //if (remoteFileList.Count(p => p.FullFileName.Replace(_localFolder, string.Empty).TrimStart('.', '\\') + p.Name == relFile) < 1) //{ // if (item.Status == ItemStatus.DeletedLocal) // { // if (!itemsToDelete.Contains(item.Id)) // itemsToDelete.Add(item.Id); // } // item.Status = ItemStatus.DeletedRemote; //} } #endregion itemsToDelete.ForEach(p => _metadataStore.Delete(p)); var countChanged = _metadataStore.Items.Count(p => p.Status != ItemStatus.Unchanged); _metadataStore.Items.Where(p => p.Status != ItemStatus.Unchanged).ToList().ForEach(p => { Logger.LogDebug(correlationId, p.Id, "(Result) Item Name={0}, Status={1}, HasError={2}, LastError={3}", p.Name, p.Status, p.HasError, p.LastError); }); // reset error flag //_metadataStore.Items.Where(p => p.HasError && p.Status != ItemStatus.Unchanged).ToList().ForEach(p => p.HasError = false); if (!doNotSave) { _metadataStore.Save(); } sumWatch.Stop(); moreChangesFound = remoteFileList.Count > 0; return(countChanged); }
private static bool ShouldForceOverwrite(ConflictHandling conflictHandling, GenericTableEntity genericTableEntity) { return(string.IsNullOrEmpty(genericTableEntity.ETag) || conflictHandling.Equals(ConflictHandling.Overwrite)); }