Esempio n. 1
0
        /// <summary>
        /// Commits the current changes since the last checkpoint to the ChangeLog, making it available for Rollback as a unit.
        /// </summary>
        public void Checkpoint(string name)
        {
            lock (SyncRoot)
            {
                // Get changes
                var changes = Data.GetChanges();

                // Do nothing when no changes
                if (changes == null)
                {
                    return;
                }

                // Do not check constraints on changes (may be orphaned)
                changes.EnforceConstraints = false;

                // Truncate existing log (remove any roll-forwards entries)
                if (_logIndex < _changeLog.Count - 1)
                {
                    while (_changeLog.Count > (_logIndex + 1))
                    {
                        _changeLog.RemoveAt(_changeLog.Count - 1);
                    }
                }

                // Add change log entry
                var logEntry = new DataSetChangeLogEntry(DateTime.UtcNow, name, changes);
                _changeLog.Add(logEntry);
                _logIndex++;

                // Commit changes
                Data.AcceptChanges();
            }
        }
        /// <summary>
        /// Rolls the DataSet forwards by the specified number of checkpoint "steps".
        /// </summary>
        public void Redo(int steps)
        {
            lock (SyncRoot)
            {
                // Validate request
                if ((steps < 1) || (steps > (_changeLog.Count - _logIndex)))
                {
                    throw new InvalidOperationException();
                }

                // Disable constraints during update (if enabled)
                bool enforceConstraints = Data.EnforceConstraints;
                if (enforceConstraints)
                {
                    Data.EnforceConstraints = false;
                }

                // Undo any outstanding changes before Checkpoint
                Data.RejectChanges();

                // Rollforward the specified number of steps...
                while (steps-- > 0)
                {
                    // Get change log entry
                    DataSetChangeLogEntry logEntry = _changeLog[_logIndex + 1];
                    DataSet changes = logEntry.Changes;

                    // Re-apply changes to DataSet
                    foreach (DataTable changedTable in changes.Tables)
                    {
                        foreach (DataRow changedRow in changedTable.Rows)
                        {
                            // Re-apply action
                            switch (changedRow.RowState)
                            {
                            case DataRowState.Added:
                            {
                                // Re-apply INSERT...

                                // Insert the changed row
                                Data.Tables[changedTable.TableName].Rows.Add(changedRow.ItemArray);
                                break;
                            }

                            case DataRowState.Modified:
                            {
                                // Re-apply UPDATE...

                                // Find the original row
                                string    primaryKeyStatement = GetPrimaryKeyFilterExpression(changedRow);
                                DataRow[] existingRows        = Data.Tables[changedTable.TableName].Select(primaryKeyStatement);
                                if (existingRows.Length == 0)
                                {
                                    throw new InvalidOperationException("Rollback could not find original row in DataSet, needed to roll-back UPDATE.");
                                }
                                DataRow row = existingRows[0];

                                // Update row values
                                row.ItemArray = changedRow.ItemArray;
                                break;
                            }

                            case DataRowState.Deleted:
                            {
                                // Re-apply DELETE...

                                // Temporarily un-delete change row to allow access to key information
                                changedRow.RejectChanges();

                                // Find the original row
                                string    primaryKeyStatement = GetPrimaryKeyFilterExpression(changedRow);
                                DataRow[] existingRows        = Data.Tables[changedTable.TableName].Select(primaryKeyStatement);
                                if (existingRows.Length == 0)
                                {
                                    throw new InvalidOperationException("Rollback could not find original row in DataSet, needed to roll-back INSERT.");
                                }
                                DataRow row = existingRows[0];

                                // Restore change row to original state
                                changedRow.Delete();

                                // Delete the row
                                row.Delete();
                                break;
                            }
                            }
                        }
                    }

                    // Commit changes
                    Data.AcceptChanges();

                    // Move change log pointer forwards
                    _logIndex++;
                }

                // Restore constraints after update (if previously enabled)
                if (enforceConstraints)
                {
                    Data.EnforceConstraints = true;
                }
            }
        }
        /// <summary>
        /// Rolls-back the DataSet by the specified number of checkpoint "steps".
        /// </summary>
        public void Undo(int steps)
        {
            lock (SyncRoot)
            {
                // Validate request
                if ((steps < 1) || (steps > (_logIndex + 1)))
                {
                    throw new InvalidOperationException();
                }

                // Disable constraints during update (if enabled)
                bool enforceConstraints = Data.EnforceConstraints;
                if (enforceConstraints)
                {
                    Data.EnforceConstraints = false;
                }

                // Undo any outstanding changes before Checkpoint
                Data.RejectChanges();

                // Rollback the specified number of steps...
                while (steps-- > 0)
                {
                    // Get change log entry
                    DataSetChangeLogEntry logEntry = _changeLog[_logIndex];
                    DataSet changes = logEntry.Changes;

                    // Reverse changes to DataSet
                    foreach (DataTable changedTable in changes.Tables)
                    {
                        foreach (DataRow changedRow in changedTable.Rows)
                        {
                            // Reverse action
                            switch (changedRow.RowState)
                            {
                            case DataRowState.Added:
                            {
                                // Reverse INSERT...

                                // Find the original row
                                string    primaryKeyStatement = GetPrimaryKeyFilterExpression(changedRow);
                                DataRow[] existingRows        = Data.Tables[changedTable.TableName].Select(primaryKeyStatement);
                                if (existingRows.Length == 0)
                                {
                                    throw new InvalidOperationException("Rollback could not find original row in DataSet, needed to roll-back INSERT.");
                                }
                                DataRow row = existingRows[0];

                                // Delete the row
                                row.Delete();
                                break;
                            }

                            case DataRowState.Modified:
                            {
                                // Reverse UPDATE...

                                // Find the original row
                                string    primaryKeyStatement = GetPrimaryKeyFilterExpression(changedRow);
                                DataRow[] existingRows        = Data.Tables[changedTable.TableName].Select(primaryKeyStatement);
                                if (existingRows.Length == 0)
                                {
                                    throw new InvalidOperationException("Rollback could not find original row in DataSet, needed to roll-back UPDATE.");
                                }
                                DataRow row = existingRows[0];

                                // Restore the original row values
                                for (int i = 0; i < changedTable.Columns.Count; i++)
                                {
                                    row[i] = changedRow[i, DataRowVersion.Original];
                                }
                                break;
                            }

                            case DataRowState.Deleted:
                            {
                                // Reverse DELETE...

                                // Insert the original row
                                DataTable table = Data.Tables[changedTable.TableName];
                                DataRow   row   = table.NewRow();
                                for (int i = 0; i < changedTable.Columns.Count; i++)
                                {
                                    row[i] = changedRow[i, DataRowVersion.Original];
                                }
                                table.Rows.Add(row);
                                break;
                            }
                            }
                        }
                    }

                    // Commit changes
                    Data.AcceptChanges();

                    // Move change log pointer backwards (do not remove forward entries until next Checkpoint, to allow roll-forward until that time)
                    _logIndex--;
                }

                // Restore constraints after update (if previously enabled)
                if (enforceConstraints)
                {
                    Data.EnforceConstraints = true;
                }
            }
        }