/// <summary> /// Method called when CancelChanges is called on the c. This method is used so that the /// ResolveInternal method can be called on the conflict, which helps avoid a dead lock on the /// SaveSyncLock on the c. /// </summary> internal void RejectChangesInternal() { lock (syncRoot) { if (this.offlineConflict == null) { FillEntityFromSnapshot(this); original = null; } else { this.offlineConflict.Resolve(SyncConflictResolutionAction.AcceptStoreEntity); } } }
/// <summary> /// Creates a snapshot of an entity. /// </summary> private void CreateSnapshot() { if (EntityState == OfflineEntityState.Unmodified || EntityState == OfflineEntityState.Saved) { if (_original == null && _trackChanges) { lock (_syncRoot) { if (_original == null && _trackChanges) { OfflineEntitySnapshot snapshot = GetSnapshotFromEntity(this); _original = snapshot; } } } } }
/// <summary> /// Copies the properties from the entity to the snapshot /// </summary> /// <param name="entity">Entity from which to copy properties</param> private OfflineEntitySnapshot GetSnapshotFromEntity(OfflineEntity entity) { OfflineEntitySnapshot snapshot = new OfflineEntitySnapshot(); IEnumerable <PropertyInfo> properties = GetEntityProperties(); // Copy data properties foreach (PropertyInfo property in properties) { object val = property.GetMethod.Invoke(entity, null); snapshot.Properties[property.Name] = val; } snapshot.TickCount = entity.TickCount; snapshot.EntityState = entity.EntityState; snapshot.Metadata = entity.ServiceMetadata.Clone(); return(snapshot); }
/// <summary> /// Updates an entity from sync. /// </summary> /// <param name="entity">Entity with changes to update</param> /// <returns>Whether or not the entity is already modified</returns> internal void UpdateFromSync(IsolatedStorageOfflineEntity entity) { lock (_syncRoot) { if (this.EntityState == OfflineEntityState.Modified) { OfflineEntitySnapshot snapshot = GetSnapshotFromEntity(entity); snapshot.IsTombstone = entity.IsTombstone; snapshot.TickCount = this.TickCount + 1; this._original = snapshot; } else { _trackChanges = false; // Update properties CopyEntityToThis(entity); _trackChanges = true; } } }
/// <summary> /// Called when the c is submitting items. It will throw if the item cannot be accepted. /// </summary> internal void AcceptChanges() { if (state != OfflineEntityState.Modified) { // Since this is only called by the c, this should never happen throw new InvalidOperationException("Entity is not modified"); } lock (syncRoot) { if (original != null) { if (TickCount != original.TickCount) { throw new InvalidOperationException("Snapshot has changed since the item was last submitted"); } } state = OfflineEntityState.Saved; original = null; } }
/// <summary> /// Reverts all changes made to the entity since the last time it was Submitted and restores /// it to its original state. If the entity has a store conflict, it will be treated as though /// the conflict is resolved with the AcceptStoreEntity resolution. /// </summary> public void RejectChanges() { lock (syncRoot) { if (this.offlineConflict == null) { if (state != OfflineEntityState.Modified) { throw new InvalidOperationException("Cannot reject changes to unmodified entity"); } if (entityMetadata.IsTombstone) { throw new InvalidOperationException( "Tombstone changes can only be rejected by calling CancelChanges on the c"); } if (original == null) { throw new InvalidOperationException( "Added items can only be rejected by calling CancelChanges on the c"); } if (original.IsTombstone) { throw new InvalidOperationException( "The item snapshot is a tombstone, so the change can only be rejected by calling CancelChanges on the c"); } FillEntityFromSnapshot(this); original = null; } else { this.offlineConflict.Resolve(SyncConflictResolutionAction.AcceptStoreEntity); } } }
/// <summary> /// Copies the properties from the entity to the snapshot /// </summary> /// <param name="entity">Entity from which to copy properties</param> private OfflineEntitySnapshot GetSnapshotFromEntity(OfflineEntity entity) { OfflineEntitySnapshot snapshot = new OfflineEntitySnapshot(); IEnumerable<PropertyInfo> properties = GetEntityProperties(); // Copy data properties foreach (PropertyInfo property in properties) { object val = property.GetMethod.Invoke(entity, null); snapshot.Properties[property.Name] = val; } snapshot.TickCount = entity.TickCount; snapshot.EntityState = entity.EntityState; snapshot.Metadata = entity.ServiceMetadata.Clone(); return snapshot; }
/// <summary> /// Creates a snapshot of an entity. /// </summary> private void CreateSnapshot() { if (EntityState == OfflineEntityState.Unmodified || EntityState == OfflineEntityState.Saved) { if (original == null && trackChanges) { lock (syncRoot) { if (original == null && trackChanges) { OfflineEntitySnapshot snapshot = GetSnapshotFromEntity(this); original = snapshot; } } } } }
/// <summary> /// Updates an entity from sync. /// </summary> /// <param name="entity">Entity with changes to update</param> /// <returns>Whether or not the entity is already modified</returns> internal void UpdateFromSync(OfflineEntity entity) { lock (syncRoot) { if (this.EntityState == OfflineEntityState.Modified) { OfflineEntitySnapshot snapshot = GetSnapshotFromEntity(entity); snapshot.IsTombstone = entity.IsTombstone; snapshot.TickCount = this.TickCount + 1; this.original = snapshot; } else { trackChanges = false; // Update properties CopyEntityToThis(entity); trackChanges = true; } } }
/// <summary> /// Called when the c is submitting items. It will throw if the item cannot be accepted. /// </summary> internal void AcceptChanges() { if (state != OfflineEntityState.Modified) { // Since this is only called by the c, this should never happen throw new InvalidOperationException("Entity is not modified"); } lock (syncRoot) { if (original != null) { if (TickCount != original.TickCount) { throw new InvalidOperationException("Snapshot has changed since the item was last submitted"); } } state = OfflineEntityState.Saved; original = null; } }
/// <summary> /// Method called when CancelChanges is called on the c. This method is used so that the /// ResolveInternal method can be called on the conflict, which helps avoid a dead lock on the /// SaveSyncLock on the c. /// </summary> internal void RejectChangesInternal() { lock (syncRoot) { if (this.offlineConflict == null) { FillEntityFromSnapshot(this); original = null; } else { this.offlineConflict.Resolve(SyncConflictResolutionAction.AcceptStoreEntity); } } }
/// <summary> /// Reverts all changes made to the entity since the last time it was Submitted and restores /// it to its original state. If the entity has a store conflict, it will be treated as though /// the conflict is resolved with the AcceptStoreEntity resolution. /// </summary> public void RejectChanges() { lock (syncRoot) { if (this.offlineConflict == null) { if (state != OfflineEntityState.Modified) throw new InvalidOperationException("Cannot reject changes to unmodified entity"); if (entityMetadata.IsTombstone) throw new InvalidOperationException( "Tombstone changes can only be rejected by calling CancelChanges on the c"); if (original == null) throw new InvalidOperationException( "Added items can only be rejected by calling CancelChanges on the c"); if (original.IsTombstone) throw new InvalidOperationException( "The item snapshot is a tombstone, so the change can only be rejected by calling CancelChanges on the c"); FillEntityFromSnapshot(this); original = null; } else { this.offlineConflict.Resolve(SyncConflictResolutionAction.AcceptStoreEntity); } } }