private void SetNewRecordWorker(DataRow row, int proposedRecord, DataRowAction action, bool isInMerge, bool suppressEnsurePropertyChanged, int position, bool fireEvent, out Exception deferredException) { // this is the event workhorse... it will throw the changing/changed events // and update the indexes. Used by change, add, delete, revert. // order of execution is as follows // // 1) set temp record // 2) Check constraints for non-expression columns // 3) Raise RowChanging/RowDeleting with temp record // 4) set the new record in storage // 5) Update indexes with recordStateChanges - this will fire ListChanged & PropertyChanged events on associated views // 6) Evaluate all Expressions (exceptions are deferred)- this will fire ListChanged & PropertyChanged events on associated views // 7) Raise RowChanged/ RowDeleted // 8) Check constraints for expression columns Debug.Assert(row != null, "Row can't be null."); deferredException = null; if (row.tempRecord != proposedRecord) { // $HACK: for performance reasons, EndUpdate calls SetNewRecord with tempRecord == proposedRecord if (!inDataLoad) { row.CheckInTable(); CheckNotModifying(row); } if (proposedRecord == row.newRecord) { if (isInMerge) { Debug.Assert(fireEvent, "SetNewRecord is called with wrong parameter"); RaiseRowChanged(null, row, action); } return; } Debug.Assert(!row.inChangingEvent, "How can this row be in an infinite loop?"); row.tempRecord = proposedRecord; } DataRowChangeEventArgs drcevent = null; try { row._action = action; drcevent = RaiseRowChanging(null, row, action, fireEvent); } catch { row.tempRecord = -1; throw; } finally { row._action = DataRowAction.Nothing; } row.tempRecord = -1; int currentRecord = row.newRecord; // if we're deleting, then the oldRecord value will change, so need to track that if it's distinct from the newRecord. int secondRecord = (proposedRecord != -1 ? proposedRecord : (row.RowState != DataRowState.Unchanged ? row.oldRecord : -1)); if (action == DataRowAction.Add) { //if we come here from insert we do insert the row to collection if (position == -1) Rows.ArrayAdd(row); else Rows.ArrayInsert(row, position); } List<DataRow> cachedRows = null; if ((action == DataRowAction.Delete || action == DataRowAction.Change) && dependentColumns != null && dependentColumns.Count > 0) { // if there are expression columns, need to cache related rows for deletes and updates (key changes) // before indexes are modified. cachedRows = new List<DataRow>(); for (int j = 0; j < ParentRelations.Count; j++) { DataRelation relation = ParentRelations[j]; if (relation.ChildTable != row.Table) { continue; } cachedRows.InsertRange(cachedRows.Count, row.GetParentRows(relation)); } for (int j = 0; j < ChildRelations.Count; j++) { DataRelation relation = ChildRelations[j]; if (relation.ParentTable != row.Table) { continue; } cachedRows.InsertRange(cachedRows.Count, row.GetChildRows(relation)); } } // Dev10 Bug 688779: DataRowView.PropertyChanged are not raised on RejectChanges // if the newRecord is changing, the propertychanged event should be allowed to triggered for ListChangedType.Changed or .Moved // unless the specific condition is known that no data has changed, like DataRow.SetModified() if (!suppressEnsurePropertyChanged && !row.HasPropertyChanged && (row.newRecord != proposedRecord) && (-1 != proposedRecord) // explictly not fixing Dev10 Bug 692044: DataRowView.PropertyChanged are not raised on DataTable.Delete when mixing current and original records in RowStateFilter && (-1 != row.newRecord)) // explictly not fixing parts of Dev10 Bug 697909: when mixing current and original records in RowStateFilter { // DataRow will believe multiple edits occured and // DataView.ListChanged event w/ ListChangedType.ItemChanged will raise DataRowView.PropertyChanged event and // PropertyChangedEventArgs.PropertyName will now be empty string so // WPF will refresh the entire row row.LastChangedColumn = null; row.LastChangedColumn = null; } // Check whether we need to update indexes if (LiveIndexes.Count != 0) { // Dev10 bug #463087: DataTable internal index is currupted: '5' if ((-1 == currentRecord) && (-1 != proposedRecord) && (-1 != row.oldRecord) && (proposedRecord != row.oldRecord)) { // the transition from DataRowState.Deleted -> DataRowState.Modified // with same orginal record but new current record // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event. // for indexes/views listening for both DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent currentRecord = row.oldRecord; } DataViewRowState currentRecordStatePre = row.GetRecordState(currentRecord); DataViewRowState secondRecordStatePre = row.GetRecordState(secondRecord); row.newRecord = proposedRecord; if (proposedRecord != -1) this.recordManager[proposedRecord] = row; DataViewRowState currentRecordStatePost = row.GetRecordState(currentRecord); DataViewRowState secondRecordStatePost = row.GetRecordState(secondRecord); // may raise DataView.ListChanged event RecordStateChanged(currentRecord, currentRecordStatePre, currentRecordStatePost, secondRecord, secondRecordStatePre, secondRecordStatePost); } else { row.newRecord = proposedRecord; if (proposedRecord != -1) this.recordManager[proposedRecord] = row; } // Dev10 Bug 461199 - reset the last changed column here, after all // DataViews have raised their DataRowView.PropertyChanged event row.ResetLastChangedColumn(); // SQLBU 278737: Record manager corruption when reentrant write operations // free the 'currentRecord' only after all the indexes have been updated. // Corruption! { if (currentRecord != row.oldRecord) { FreeRecord(ref currentRecord); } } // RecordStateChanged raises ListChanged event at which time user may do work if (-1 != currentRecord) { if (currentRecord != row.oldRecord) { if ((currentRecord != row.tempRecord) && // Delete, AcceptChanges, BeginEdit (currentRecord != row.newRecord) && // RejectChanges & SetAdded (row == recordManager[currentRecord])) // AcceptChanges, NewRow { FreeRecord(ref currentRecord); } } } if (row.RowState == DataRowState.Detached && row.rowID != -1) { RemoveRow(row, false); } if (dependentColumns != null && dependentColumns.Count > 0) { try { EvaluateExpressions(row, action, cachedRows); } catch (Exception exc) { // For DataRows being added, throwing of exception from expression evaluation is // deferred until after the row has been completely added. if (action != DataRowAction.Add) { throw exc; } else { deferredException = exc; } } } try { if (fireEvent) { RaiseRowChanged(drcevent, row, action); } } catch (Exception e) { // if (!Common.ADP.IsCatchableExceptionType(e)) { throw; } ExceptionBuilder.TraceExceptionWithoutRethrow(e); // ignore the exception } }
internal void SilentlySetValue(DataRow dr, DataColumn dc, DataRowVersion version, object newValue) { // get record for version int record = dr.GetRecordFromVersion(version); bool equalValues = false; if (DataStorage.IsTypeCustomType(dc.DataType) && newValue != dc[record]) { // if UDT storage, need to check if reference changed. See bug 385182 equalValues = false; } else { equalValues = dc.CompareValueTo(record, newValue, true); } // if expression has changed if (!equalValues) { int[] oldIndex = dr.Table.RemoveRecordFromIndexes(dr, version);// conditional, if it exists it will try to remove with no event fired dc.SetValue(record, newValue); int[] newIndex = dr.Table.InsertRecordToIndexes(dr, version);// conditional, it will insert if it qualifies, no event will be fired if (dr.HasVersion(version)) { if (version != DataRowVersion.Original) { dr.Table.RecordChanged(oldIndex, newIndex); } if (dc.dependentColumns != null) { //BugBug - passing in null for cachedRows. This means expression columns as keys does not work when key changes. dc.Table.EvaluateDependentExpressions(dc.dependentColumns, dr, version, null); } } } dr.ResetLastChangedColumn(); }
internal void InsertRow(DataRow row, long proposedID, int pos, bool fireEvent) { Exception deferredException = null; if (row == null) { throw ExceptionBuilder.ArgumentNull("row"); } if (row.Table != this) { throw ExceptionBuilder.RowAlreadyInOtherCollection(); } if (row.rowID != -1) { throw ExceptionBuilder.RowAlreadyInTheCollection(); } row.BeginEdit(); // ensure something's there. int record = row.tempRecord; row.tempRecord = -1; if (proposedID == -1) { proposedID = this.nextRowID; } bool rollbackOnException; if (rollbackOnException = (nextRowID <= proposedID)) { // WebData 109005 nextRowID = checked(proposedID + 1); } try { try { row.rowID = proposedID; // this method may cause DataView.OnListChanged in which another row may be added SetNewRecordWorker(row, record, DataRowAction.Add, false, false, pos, fireEvent, out deferredException); // now we do add the row to collection before OnRowChanged (RaiseRowChanged) } catch { if (rollbackOnException && (nextRowID == proposedID+1)) { nextRowID = proposedID; } row.rowID = -1; row.tempRecord = record; throw; } // since expression evaluation occurred in SetNewRecordWorker, there may have been a problem that // was deferred to this point. If so, throw now since row has already been added. if (deferredException != null) throw deferredException; if (EnforceConstraints && !inLoad ) { // if we are evaluating expression, we need to validate constraints int columnCount = columnCollection.Count; for (int i = 0; i < columnCount; ++i) { DataColumn column = columnCollection[i]; if (column.Computed) { column.CheckColumnConstraint(row, DataRowAction.Add); } } } } finally { row.ResetLastChangedColumn();// if expression is evaluated while adding, before return, we want to clear it } }
internal void SilentlySetValue(DataRow dr, DataColumn dc, DataRowVersion version, object newValue) { int recordFromVersion = dr.GetRecordFromVersion(version); bool flag = false; if (DataStorage.IsTypeCustomType(dc.DataType) && (newValue != dc[recordFromVersion])) { flag = false; } else { flag = dc.CompareValueTo(recordFromVersion, newValue, true); } if (!flag) { int[] oldIndex = dr.Table.RemoveRecordFromIndexes(dr, version); dc.SetValue(recordFromVersion, newValue); int[] newIndex = dr.Table.InsertRecordToIndexes(dr, version); if (dr.HasVersion(version)) { if (version != DataRowVersion.Original) { dr.Table.RecordChanged(oldIndex, newIndex); } if (dc.dependentColumns != null) { dc.Table.EvaluateDependentExpressions(dc.dependentColumns, dr, version, null); } } } dr.ResetLastChangedColumn(); }
private void SetNewRecordWorker(DataRow row, int proposedRecord, DataRowAction action, bool isInMerge, bool suppressEnsurePropertyChanged, int position, bool fireEvent, out Exception deferredException) { deferredException = null; if (row.tempRecord != proposedRecord) { if (!this.inDataLoad) { row.CheckInTable(); this.CheckNotModifying(row); } if (proposedRecord == row.newRecord) { if (isInMerge) { this.RaiseRowChanged(null, row, action); } return; } row.tempRecord = proposedRecord; } DataRowChangeEventArgs args = null; try { row._action = action; args = this.RaiseRowChanging(null, row, action, fireEvent); } catch { row.tempRecord = -1; throw; } finally { row._action = DataRowAction.Nothing; } row.tempRecord = -1; int newRecord = row.newRecord; int record = (proposedRecord != -1) ? proposedRecord : ((row.RowState != DataRowState.Unchanged) ? row.oldRecord : -1); if (action == DataRowAction.Add) { if (position == -1) { this.Rows.ArrayAdd(row); } else { this.Rows.ArrayInsert(row, position); } } List<DataRow> cachedRows = null; if (((action == DataRowAction.Delete) || (action == DataRowAction.Change)) && ((this.dependentColumns != null) && (this.dependentColumns.Count > 0))) { cachedRows = new List<DataRow>(); for (int i = 0; i < this.ParentRelations.Count; i++) { DataRelation relation2 = this.ParentRelations[i]; if (relation2.ChildTable == row.Table) { cachedRows.InsertRange(cachedRows.Count, row.GetParentRows(relation2)); } } for (int j = 0; j < this.ChildRelations.Count; j++) { DataRelation relation = this.ChildRelations[j]; if (relation.ParentTable == row.Table) { cachedRows.InsertRange(cachedRows.Count, row.GetChildRows(relation)); } } } if (((!suppressEnsurePropertyChanged && !row.HasPropertyChanged) && ((row.newRecord != proposedRecord) && (-1 != proposedRecord))) && (-1 != row.newRecord)) { row.LastChangedColumn = null; row.LastChangedColumn = null; } if (this.LiveIndexes.Count != 0) { if (((-1 == newRecord) && (-1 != proposedRecord)) && ((-1 != row.oldRecord) && (proposedRecord != row.oldRecord))) { newRecord = row.oldRecord; } DataViewRowState recordState = row.GetRecordState(newRecord); DataViewRowState state3 = row.GetRecordState(record); row.newRecord = proposedRecord; if (proposedRecord != -1) { this.recordManager[proposedRecord] = row; } DataViewRowState state2 = row.GetRecordState(newRecord); DataViewRowState state = row.GetRecordState(record); this.RecordStateChanged(newRecord, recordState, state2, record, state3, state); } else { row.newRecord = proposedRecord; if (proposedRecord != -1) { this.recordManager[proposedRecord] = row; } } row.ResetLastChangedColumn(); if ((((-1 != newRecord) && (newRecord != row.oldRecord)) && ((newRecord != row.tempRecord) && (newRecord != row.newRecord))) && (row == this.recordManager[newRecord])) { this.FreeRecord(ref newRecord); } if ((row.RowState == DataRowState.Detached) && (row.rowID != -1L)) { this.RemoveRow(row, false); } if ((this.dependentColumns != null) && (this.dependentColumns.Count > 0)) { try { this.EvaluateExpressions(row, action, cachedRows); } catch (Exception exception2) { if (action != DataRowAction.Add) { throw exception2; } deferredException = exception2; } } try { if (fireEvent) { this.RaiseRowChanged(args, row, action); } } catch (Exception exception) { if (!ADP.IsCatchableExceptionType(exception)) { throw; } ExceptionBuilder.TraceExceptionWithoutRethrow(exception); } }
internal void InsertRow(DataRow row, long proposedID, int pos, bool fireEvent) { Exception deferredException = null; bool flag; if (row == null) { throw ExceptionBuilder.ArgumentNull("row"); } if (row.Table != this) { throw ExceptionBuilder.RowAlreadyInOtherCollection(); } if (row.rowID != -1L) { throw ExceptionBuilder.RowAlreadyInTheCollection(); } row.BeginEdit(); int tempRecord = row.tempRecord; row.tempRecord = -1; if (proposedID == -1L) { proposedID = this.nextRowID; } if (flag = this.nextRowID <= proposedID) { this.nextRowID = proposedID + 1L; } try { try { row.rowID = proposedID; this.SetNewRecordWorker(row, tempRecord, DataRowAction.Add, false, false, pos, fireEvent, out deferredException); } catch { if (flag && (this.nextRowID == (proposedID + 1L))) { this.nextRowID = proposedID; } row.rowID = -1L; row.tempRecord = tempRecord; throw; } if (deferredException != null) { throw deferredException; } if (this.EnforceConstraints && !this.inLoad) { int count = this.columnCollection.Count; for (int i = 0; i < count; i++) { DataColumn column = this.columnCollection[i]; if (column.Computed) { column.CheckColumnConstraint(row, DataRowAction.Add); } } } } finally { row.ResetLastChangedColumn(); } }