| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328 |
- //------------------------------------------------------------------------------
- // <copyright file="DataRow.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data {
- using System;
- using System.Collections;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Globalization;
- using System.Xml;
- /// <devdoc>
- /// <para>Represents a row of data in a <see cref='System.Data.DataTable'/>.</para>
- /// </devdoc>
- public class DataRow {
- private readonly DataTable _table;
- private readonly DataColumnCollection _columns;
- internal int oldRecord = -1;
- internal int newRecord = -1;
- internal int tempRecord;
- internal long _rowID = -1;
- internal DataRowAction _action;
- internal bool inChangingEvent;
- internal bool inDeletingEvent;
- internal bool inCascade;
- private DataColumn _lastChangedColumn; // last successfully changed column
- private int _countColumnChange; // number of columns changed during edit mode
-
- private DataError error;
- private object _element;
-
- private int _rbTreeNodeId; // if row is not detached, Id used for computing index in rows collection
- private static int _objectTypeCount; // Bid counter
- internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the DataRow.
- /// </para>
- /// <para>
- /// Constructs a row from the builder. Only for internal usage..
- /// </para>
- /// </devdoc>
- protected internal DataRow (DataRowBuilder builder) {
- tempRecord = builder._record;
- _table = builder._table;
- _columns = _table.Columns;
- }
- internal XmlBoundElement Element {
- get {
- return (XmlBoundElement) _element;
- }
- set {
- _element = value;
- }
- }
- internal DataColumn LastChangedColumn {
- get { // last successfully changed column or if multiple columns changed: null
- if (_countColumnChange != 1) {
- return null;
- }
- return _lastChangedColumn;
- }
- set {
- _countColumnChange++;
- _lastChangedColumn = value;
- }
- }
- internal bool HasPropertyChanged {
- get { return (0 < _countColumnChange); }
- }
- internal int RBTreeNodeId {
- get {
- return _rbTreeNodeId;
- }
- set {
- Bid.Trace("<ds.DataRow.set_RBTreeNodeId|INFO> %d#, value=%d\n", ObjectID, value);
- _rbTreeNodeId = value;
- }
- }
- /// <devdoc>
- /// <para>Gets or sets the custom error description for a row.</para>
- /// </devdoc>
- public string RowError {
- get {
- return(error == null ? String.Empty :error.Text);
- }
- set {
- Bid.Trace("<ds.DataRow.set_RowError|API> %d#, value='%ls'\n", ObjectID, value);
- if (error == null) {
- if (!Common.ADP.IsEmpty(value)) {
- error = new DataError(value);
- }
- RowErrorChanged();
- }
- else if(error.Text != value) {
- error.Text = value;
- RowErrorChanged();
- }
- }
- }
- private void RowErrorChanged() {
- // We don't know wich record was used by view index. try to use both.
- if (oldRecord != -1)
- _table.RecordChanged(oldRecord);
- if (newRecord != -1)
- _table.RecordChanged(newRecord);
- }
- internal long rowID {
- get {
- return _rowID;
- }
- set {
- ResetLastChangedColumn();
- _rowID = value;
- }
- }
- /// <devdoc>
- /// <para>Gets the current state of the row in regards to its relationship to the table.</para>
- /// </devdoc>
- public DataRowState RowState {
- get {
- /*
- if (oldRecord == -1 && newRecord == -1)
- state = DataRowState.Detached; // 2
- else if (oldRecord == newRecord)
- state = DataRowState.Unchanged; // 2
- else if (oldRecord == -1)
- state = DataRowState.Added; // 4
- else if (newRecord == -1)
- state = DataRowState.Deleted; // 4
- else
- state = DataRowState.Modified; // 4
- */
- if (oldRecord == newRecord) {
- if (oldRecord == -1) {
- return DataRowState.Detached; // 2
- }
- if (0 < _columns.ColumnsImplementingIChangeTrackingCount) {
- foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
- object value = this[dc];
- if ((DBNull.Value != value) && ((IChangeTracking)value).IsChanged) {
- return DataRowState.Modified; // 3 + _columns.columnsImplementingIChangeTracking.Count
- }
- }
- }
- return DataRowState.Unchanged; // 3
- }
- else if (oldRecord == -1) {
- return DataRowState.Added; // 2
- }
- else if (newRecord == -1) {
- return DataRowState.Deleted; // 3
- }
- return DataRowState.Modified; // 3
- }
- }
- /// <devdoc>
- /// <para>Gets the <see cref='System.Data.DataTable'/>
- /// for which this row has a schema.</para>
- /// </devdoc>
- public DataTable Table {
- get {
- return _table;
- }
- }
- /// <devdoc>
- /// <para>Gets or sets the data stored in the column specified by index.</para>
- /// </devdoc>
- public object this[int columnIndex] {
- get {
- DataColumn column = _columns[columnIndex];
- int record = GetDefaultRecord();
- _table.recordManager.VerifyRecord(record, this);
- VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
- return column[record];
- }
- set {
- DataColumn column = _columns[columnIndex];
- this[column] = value;
- }
- }
- internal void CheckForLoops(DataRelation rel){
- // don't check for loops in the diffgram
- // because there may be some holes in the rowCollection
- // and index creation may fail. The check will be done
- // after all the loading is done _and_ we are sure there
- // are no holes in the collection.
- if (_table.fInLoadDiffgram || (_table.DataSet != null && _table.DataSet.fInLoadDiffgram))
- return;
- int count = _table.Rows.Count, i = 0;
- // need to optimize this for count > 100
- DataRow parent = this.GetParentRow(rel);
- while (parent != null) {
- if ((parent == this) || (i>count))
- throw ExceptionBuilder.NestedCircular(_table.TableName);
- i++;
- parent = parent.GetParentRow(rel);
- }
- }
- internal int GetNestedParentCount() {
- int count = 0;
- DataRelation[] nestedParentRelations = _table.NestedParentRelations;
- foreach(DataRelation rel in nestedParentRelations) {
- if (rel == null) // don't like this but done for backward code compatability
- continue;
- if (rel.ParentTable == _table) // self-nested table
- this.CheckForLoops(rel);
- DataRow row = this.GetParentRow(rel);
- if (row != null) {
- count++;
- }
- }
- return count ;
- // Rule 1: At all times, only ONE FK "(in a row) can be non-Null
- // we wont allow a row to have multiple parents, as we cant handle it , also in diffgram
- }
- /// <devdoc>
- /// <para>Gets or sets the data stored in the column specified by
- /// name.</para>
- /// </devdoc>
- public object this[string columnName] {
- get {
- DataColumn column = GetDataColumn(columnName);
- int record = GetDefaultRecord();
- _table.recordManager.VerifyRecord(record, this);
- VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
- return column[record];
- }
- set {
- DataColumn column = GetDataColumn(columnName);
- this[column] = value;
- }
- }
- /// <devdoc>
- /// <para>Gets or sets
- /// the data stored in the specified <see cref='System.Data.DataColumn'/>.</para>
- /// </devdoc>
- public object this[DataColumn column] {
- get {
- CheckColumn(column);
- int record = GetDefaultRecord();
- _table.recordManager.VerifyRecord(record, this);
- VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
- return column[record];
- }
- set {
- CheckColumn(column);
- if (inChangingEvent) {
- throw ExceptionBuilder.EditInRowChanging();
- }
- if ((-1 != rowID) && column.ReadOnly) {
- throw ExceptionBuilder.ReadOnly(column.ColumnName);
- }
- // allow users to tailor the proposed value, or throw an exception.
- // note we intentionally do not try/catch this event.
- // note: we also allow user to do anything at this point
- // infinite loops are possible if user calls Item or ItemArray during the event
- DataColumnChangeEventArgs e = null;
- if (_table.NeedColumnChangeEvents) {
- e = new DataColumnChangeEventArgs(this, column, value);
- _table.OnColumnChanging(e);
- }
- if (column.Table != _table) {
- // user removed column from table during OnColumnChanging event
- throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
- }
- if ((-1 != rowID) && column.ReadOnly) {
- // user adds row to table during OnColumnChanging event
- throw ExceptionBuilder.ReadOnly(column.ColumnName);
- }
-
- object proposed = ((null != e) ? e.ProposedValue : value);
- if (null == proposed) {
- if (column.IsValueType) { // WebData 105963
- throw ExceptionBuilder.CannotSetToNull(column);
- }
- proposed = DBNull.Value;
- }
- bool immediate = BeginEditInternal();
- try {
- int record = GetProposedRecordNo();
- _table.recordManager.VerifyRecord(record, this);
- column[record] = proposed;
- }
- catch (Exception e1){
- //
- if (Common.ADP.IsCatchableOrSecurityExceptionType(e1)) {
- if (immediate) {
- Debug.Assert(!inChangingEvent, "how are we in a changing event to cancel?");
- Debug.Assert(-1 != tempRecord, "how no propsed record to cancel?");
- CancelEdit(); // WebData 107154
- }
- }
- throw;
- }
- LastChangedColumn = column;
- // note: we intentionally do not try/catch this event.
- // infinite loops are possible if user calls Item or ItemArray during the event
- if (null != e) {
- _table.OnColumnChanged(e); // user may call CancelEdit or EndEdit
- }
- if (immediate) {
- Debug.Assert(!inChangingEvent, "how are we in a changing event to end?");
- EndEdit();
- }
- }
- }
- /// <devdoc>
- /// <para>Gets the data stored
- /// in the column, specified by index and version of the data to retrieve.</para>
- /// </devdoc>
- public object this[int columnIndex, DataRowVersion version] {
- get {
- DataColumn column = _columns[columnIndex];
- int record = GetRecordFromVersion(version);
- _table.recordManager.VerifyRecord(record, this);
- VerifyValueFromStorage(column, version, column[record]);
- return column[record];
- }
- }
- /// <devdoc>
- /// <para> Gets the specified version of data stored in
- /// the named column.</para>
- /// </devdoc>
- public object this[string columnName, DataRowVersion version] {
- get {
- DataColumn column = GetDataColumn(columnName);
- int record = GetRecordFromVersion(version);
- _table.recordManager.VerifyRecord(record, this);
- VerifyValueFromStorage(column, version, column[record]);
- return column[record];
- }
- }
- /// <devdoc>
- /// <para>Gets the specified version of data stored in the specified <see cref='System.Data.DataColumn'/>.</para>
- /// </devdoc>
- public object this[DataColumn column, DataRowVersion version] {
- get {
- CheckColumn(column);
- int record = GetRecordFromVersion(version);
- _table.recordManager.VerifyRecord(record, this);
- VerifyValueFromStorage(column, version, column[record]);
- return column[record];
- }
- }
- /// <devdoc>
- /// <para>Gets
- /// or sets all of the values for this row through an array.</para>
- /// </devdoc>
- public object[] ItemArray {
- get {
- int record = GetDefaultRecord();
- _table.recordManager.VerifyRecord(record, this);
- object[] values = new object[_columns.Count];
- for (int i = 0; i < values.Length; i++) {
- DataColumn column = _columns[i];
- VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
- values[i] = column[record];
- }
- return values;
- }
- set {
- if (null == value) { // WebData 104372
- throw ExceptionBuilder.ArgumentNull("ItemArray");
- }
- if (_columns.Count < value.Length) {
- throw ExceptionBuilder.ValueArrayLength();
- }
- DataColumnChangeEventArgs e = null;
- if (_table.NeedColumnChangeEvents) {
- e = new DataColumnChangeEventArgs(this);
- }
- bool immediate = BeginEditInternal();
- for (int i = 0; i < value.Length; ++i) {
- // Empty means don't change the row.
- if (null != value[i]) {
- // may throw exception if user removes column from table during event
- DataColumn column = _columns[i];
- if ((-1 != rowID) && column.ReadOnly) {
- throw ExceptionBuilder.ReadOnly(column.ColumnName);
- }
- // allow users to tailor the proposed value, or throw an exception.
- // note: we intentionally do not try/catch this event.
- // note: we also allow user to do anything at this point
- // infinite loops are possible if user calls Item or ItemArray during the event
- if (null != e) {
- e.InitializeColumnChangeEvent(column, value[i]);
- _table.OnColumnChanging(e);
- }
- if (column.Table != _table) {
- // user removed column from table during OnColumnChanging event
- throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
- }
- if ((-1 != rowID) && column.ReadOnly) {
- // user adds row to table during OnColumnChanging event
- throw ExceptionBuilder.ReadOnly(column.ColumnName);
- }
- if (tempRecord == -1) {
- // user affected CancelEdit or EndEdit during OnColumnChanging event of the last value
- BeginEditInternal();
- }
- object proposed = (null != e) ? e.ProposedValue : value[i];
- if (null == proposed) {
- if (column.IsValueType) { // WebData 105963
- throw ExceptionBuilder.CannotSetToNull(column);
- }
- proposed = DBNull.Value;
- }
- try {
- // must get proposed record after each event because user may have
- // called EndEdit(), AcceptChanges(), BeginEdit() during the event
- int record = GetProposedRecordNo();
- _table.recordManager.VerifyRecord(record, this);
- column[record] = proposed;
- }
- catch (Exception e1) {
- //
- if (Common.ADP.IsCatchableOrSecurityExceptionType(e1)) {
- if (immediate) {
- Debug.Assert(!inChangingEvent, "how are we in a changing event to cancel?");
- Debug.Assert(-1 != tempRecord, "how no propsed record to cancel?");
- CancelEdit(); // WebData 107154
- }
- }
- throw;
- }
- LastChangedColumn = column;
- // note: we intentionally do not try/catch this event.
- // infinite loops are possible if user calls Item or ItemArray during the event
- if (null != e) {
- _table.OnColumnChanged(e); // user may call CancelEdit or EndEdit
- }
- }
- }
- // proposed breaking change: if (immediate){ EndEdit(); } because table currently always fires RowChangedEvent
- Debug.Assert(!inChangingEvent, "how are we in a changing event to end?");
- EndEdit();
- }
- }
- /// <devdoc>
- /// <para>Commits all the changes made to this row
- /// since the last time <see cref='System.Data.DataRow.AcceptChanges'/> was called.</para>
- /// </devdoc>
- public void AcceptChanges() {
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<ds.DataRow.AcceptChanges|API> %d#\n", ObjectID);
- try {
- EndEdit();
- if (this.RowState != DataRowState.Detached && this.RowState != DataRowState.Deleted) {
- if (_columns.ColumnsImplementingIChangeTrackingCount > 0) {
- foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
- object value = this[dc];
- if (DBNull.Value != value) {
- IChangeTracking tracking = (IChangeTracking)value;
- if (tracking.IsChanged) {
- tracking.AcceptChanges();
- }
- }
- }
- }
- }
- _table.CommitRow(this);
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- /// <devdoc>
- /// <para>Begins an edit operation on a <see cref='System.Data.DataRow'/>object.</para>
- /// </devdoc>
- [
- EditorBrowsableAttribute(EditorBrowsableState.Advanced),
- ]
- public void BeginEdit() {
- BeginEditInternal();
- }
- private bool BeginEditInternal() {
- if (inChangingEvent) {
- throw ExceptionBuilder.BeginEditInRowChanging();
- }
- if (tempRecord != -1) {
- if (tempRecord < _table.recordManager.LastFreeRecord) {
- return false; // we will not call EndEdit
- }
- else {
- // partial fix for detached row after Table.Clear scenario
- // in debug, it will have asserted earlier, but with this
- // it will go get a new record for editing
- tempRecord = -1;
- }
- // shifted VerifyRecord to first make the correction, then verify
- _table.recordManager.VerifyRecord(tempRecord, this);
- }
- if (oldRecord != -1 && newRecord == -1) {
- throw ExceptionBuilder.DeletedRowInaccessible();
- }
- //
- ResetLastChangedColumn(); // shouldn't have to do this
- tempRecord = _table.NewRecord(newRecord);
- Debug.Assert(-1 != tempRecord, "missing temp record");
- Debug.Assert(0 == _countColumnChange, "unexpected column change count");
- Debug.Assert(null == _lastChangedColumn, "unexpected last column change");
- return true;
- }
- /// <devdoc>
- /// <para>Cancels the current edit on the row.</para>
- /// </devdoc>
- [
- EditorBrowsableAttribute(EditorBrowsableState.Advanced),
- ]
- public void CancelEdit() {
- if (inChangingEvent) {
- throw ExceptionBuilder.CancelEditInRowChanging();
- }
- _table.FreeRecord(ref tempRecord);
- Debug.Assert(-1 == tempRecord, "unexpected temp record");
- ResetLastChangedColumn();
- }
- private void CheckColumn(DataColumn column) {
- if (column == null) {
- throw ExceptionBuilder.ArgumentNull("column");
- }
- if (column.Table != _table) {
- throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
- }
- }
- /// <devdoc>
- /// Throws a RowNotInTableException if row isn't in table.
- /// </devdoc>
- internal void CheckInTable() {
- if (rowID == -1) {
- throw ExceptionBuilder.RowNotInTheTable();
- }
- }
- /// <devdoc>
- /// <para>Deletes the row.</para>
- /// </devdoc>
- public void Delete() {
- if (inDeletingEvent) {
- throw ExceptionBuilder.DeleteInRowDeleting();
- }
- if (newRecord == -1)
- return;
- _table.DeleteRow(this);
- }
- /// <devdoc>
- /// <para>Ends the edit occurring on the row.</para>
- /// </devdoc>
- [
- EditorBrowsableAttribute(EditorBrowsableState.Advanced),
- ]
- public void EndEdit() {
- if (inChangingEvent) {
- throw ExceptionBuilder.EndEditInRowChanging();
- }
- if (newRecord == -1) {
- return; // this is meaningless, detatched row case
- }
- if (tempRecord != -1) {
- try {
- // suppressing the ensure property changed because it's possible that no values have been modified
- _table.SetNewRecord(this, tempRecord, suppressEnsurePropertyChanged: true);
- }
- finally {
- // a constraint violation may be thrown during SetNewRecord
- ResetLastChangedColumn();
- }
- }
- }
- /// <devdoc>
- /// <para>Sets the error description for a column specified by index.</para>
- /// </devdoc>
- public void SetColumnError(int columnIndex, string error) {
- DataColumn column = _columns[columnIndex];
- if (column == null)
- throw ExceptionBuilder.ColumnOutOfRange(columnIndex);
- SetColumnError(column, error);
- }
- /// <devdoc>
- /// <para>Sets
- /// the error description for a column specified by name.</para>
- /// </devdoc>
- public void SetColumnError(string columnName, string error) {
- DataColumn column = GetDataColumn(columnName);
- SetColumnError(column, error);
- }
- /// <devdoc>
- /// <para>Sets the error description for a column specified as a <see cref='System.Data.DataColumn'/>.</para>
- /// </devdoc>
- public void SetColumnError(DataColumn column, string error) {
- CheckColumn(column);
-
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<ds.DataRow.SetColumnError|API> %d#, column=%d, error='%ls'\n", ObjectID, column.ObjectID, error);
- try {
- if (this.error == null) this.error = new DataError();
- if(GetColumnError(column) != error) {
- this.error.SetColumnError(column, error);
- RowErrorChanged();
- }
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- /// <devdoc>
- /// <para>Gets the error description for the column specified
- /// by index.</para>
- /// </devdoc>
- public string GetColumnError(int columnIndex) {
- DataColumn column = _columns[columnIndex];
- return GetColumnError(column);
- }
- /// <devdoc>
- /// <para>Gets the error description for a column, specified by name.</para>
- /// </devdoc>
- public string GetColumnError(string columnName) {
- DataColumn column = GetDataColumn(columnName);
- return GetColumnError(column);
- }
- /// <devdoc>
- /// <para>Gets the error description of
- /// the specified <see cref='System.Data.DataColumn'/>.</para>
- /// </devdoc>
- public string GetColumnError(DataColumn column) {
- CheckColumn(column);
- if (error == null) error = new DataError();
- return error.GetColumnError(column);
- }
- /// <summary>
- /// Clears the errors for the row, including the <see cref='System.Data.DataRow.RowError'/>
- /// and errors set with <see cref='System.Data.DataRow.SetColumnError(DataColumn, string)'/>
- /// </summary>
- public void ClearErrors() {
- if (error != null) {
- error.Clear();
- RowErrorChanged();
- }
- }
- internal void ClearError(DataColumn column) {
- if (error != null) {
- error.Clear(column);
- RowErrorChanged();
- }
- }
- /// <devdoc>
- /// <para>Gets a value indicating whether there are errors in a columns collection.</para>
- /// </devdoc>
- public bool HasErrors {
- get {
- return(error == null ? false : error.HasErrors);
- }
- }
- /// <devdoc>
- /// <para>Gets an array of columns that have errors.</para>
- /// </devdoc>
- public DataColumn[] GetColumnsInError() {
- if (error == null)
- return DataTable.zeroColumns;
- else
- return error.GetColumnsInError();
- }
- public DataRow[] GetChildRows(string relationName) {
- return GetChildRows(_table.ChildRelations[relationName], DataRowVersion.Default);
- }
- public DataRow[] GetChildRows(string relationName, DataRowVersion version) {
- return GetChildRows(_table.ChildRelations[relationName], version);
- }
- /// <devdoc>
- /// <para>Gets the child rows of this <see cref='System.Data.DataRow'/> using the
- /// specified <see cref='System.Data.DataRelation'/>
- /// .</para>
- /// </devdoc>
- public DataRow[] GetChildRows(DataRelation relation) {
- return GetChildRows(relation, DataRowVersion.Default);
- }
- /// <devdoc>
- /// <para>Gets the child rows of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> and the specified <see cref='System.Data.DataRowVersion'/></para>
- /// </devdoc>
- public DataRow[] GetChildRows(DataRelation relation, DataRowVersion version) {
- if (relation == null)
- return _table.NewRowArray(0);
- //if (-1 == rowID)
- // throw ExceptionBuilder.RowNotInTheTable();
- if (relation.DataSet != _table.DataSet)
- throw ExceptionBuilder.RowNotInTheDataSet();
- if (relation.ParentKey.Table != _table)
- throw ExceptionBuilder.RelationForeignTable(relation.ParentTable.TableName, _table.TableName);
- return DataRelation.GetChildRows(relation.ParentKey, relation.ChildKey, this, version);
- }
- internal DataColumn GetDataColumn(string columnName) {
- DataColumn column = _columns[columnName];
- if (null != column) {
- return column;
- }
- throw ExceptionBuilder.ColumnNotInTheTable(columnName, _table.TableName);
- }
- public DataRow GetParentRow(string relationName) {
- return GetParentRow(_table.ParentRelations[relationName], DataRowVersion.Default);
- }
- public DataRow GetParentRow(string relationName, DataRowVersion version) {
- return GetParentRow(_table.ParentRelations[relationName], version);
- }
- /// <devdoc>
- /// <para>Gets the parent row of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> .</para>
- /// </devdoc>
- public DataRow GetParentRow(DataRelation relation) {
- return GetParentRow(relation, DataRowVersion.Default);
- }
- /// <devdoc>
- /// <para>Gets the parent row of this <see cref='System.Data.DataRow'/>
- /// using the specified <see cref='System.Data.DataRelation'/> and <see cref='System.Data.DataRowVersion'/>.</para>
- /// </devdoc>
- public DataRow GetParentRow(DataRelation relation, DataRowVersion version) {
- if (relation == null)
- return null;
- //if (-1 == rowID)
- // throw ExceptionBuilder.RowNotInTheTable();
- if (relation.DataSet != _table.DataSet)
- throw ExceptionBuilder.RelationForeignRow();
- if (relation.ChildKey.Table != _table)
- throw ExceptionBuilder.GetParentRowTableMismatch(relation.ChildTable.TableName, _table.TableName);
- return DataRelation.GetParentRow(relation.ParentKey, relation.ChildKey, this, version);
- }
- // a multiple nested child table's row can have only one non-null FK per row. So table has multiple
- // parents, but a row can have only one parent. Same nested row cannot below to 2 parent rows.
- internal DataRow GetNestedParentRow(DataRowVersion version) {
- // 1) Walk over all FKs and get the non-null. 2) Get the relation. 3) Get the parent Row.
- DataRelation[] nestedParentRelations = _table.NestedParentRelations;
- foreach(DataRelation rel in nestedParentRelations) {
- if (rel == null) // don't like this but done for backward code compatability
- continue;
- if (rel.ParentTable == _table) // self-nested table
- this.CheckForLoops(rel);
- DataRow row = this.GetParentRow(rel, version);
- if (row != null) {
- return row;
- }
- }
- return null;// Rule 1: At all times, only ONE FK "(in a row) can be non-Null
- }
- // No Nested in 1-many
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- public DataRow[] GetParentRows(string relationName) {
- return GetParentRows(_table.ParentRelations[relationName], DataRowVersion.Default);
- }
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- public DataRow[] GetParentRows(string relationName, DataRowVersion version) {
- return GetParentRows(_table.ParentRelations[relationName], version);
- }
- /// <devdoc>
- /// <para>
- /// Gets the parent rows of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> .
- /// </para>
- /// </devdoc>
- public DataRow[] GetParentRows(DataRelation relation) {
- return GetParentRows(relation, DataRowVersion.Default);
- }
- /// <devdoc>
- /// <para>
- /// Gets the parent rows of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> .
- /// </para>
- /// </devdoc>
- public DataRow[] GetParentRows(DataRelation relation, DataRowVersion version) {
- if (relation == null)
- return _table.NewRowArray(0);
- //if (-1 == rowID)
- // throw ExceptionBuilder.RowNotInTheTable();
- if (relation.DataSet != _table.DataSet)
- throw ExceptionBuilder.RowNotInTheDataSet();
- if (relation.ChildKey.Table != _table)
- throw ExceptionBuilder.GetParentRowTableMismatch(relation.ChildTable.TableName, _table.TableName);
- return DataRelation.GetParentRows(relation.ParentKey, relation.ChildKey, this, version);
- }
- internal object[] GetColumnValues(DataColumn[] columns) {
- return GetColumnValues(columns, DataRowVersion.Default);
- }
- internal object[] GetColumnValues(DataColumn[] columns, DataRowVersion version) {
- DataKey key = new DataKey(columns, false); // temporary key, don't copy columns
- return GetKeyValues(key, version);
- }
- internal object[] GetKeyValues(DataKey key) {
- int record = GetDefaultRecord();
- return key.GetKeyValues(record);
- }
- internal object[] GetKeyValues(DataKey key, DataRowVersion version) {
- int record = GetRecordFromVersion(version);
- return key.GetKeyValues(record);
- }
- internal int GetCurrentRecordNo() {
- if (newRecord == -1)
- throw ExceptionBuilder.NoCurrentData();
- return newRecord;
- }
- internal int GetDefaultRecord() {
- if (tempRecord != -1)
- return tempRecord;
- if (newRecord != -1) {
- return newRecord;
- }
- // If row has oldRecord - this is deleted row.
- if (oldRecord == -1)
- throw ExceptionBuilder.RowRemovedFromTheTable();
- else
- throw ExceptionBuilder.DeletedRowInaccessible();
- }
- internal int GetOriginalRecordNo() {
- if (oldRecord == -1)
- throw ExceptionBuilder.NoOriginalData();
- return oldRecord;
- }
- private int GetProposedRecordNo() {
- if (tempRecord == -1)
- throw ExceptionBuilder.NoProposedData();
- return tempRecord;
- }
- internal int GetRecordFromVersion(DataRowVersion version) {
- switch (version) {
- case DataRowVersion.Original:
- return GetOriginalRecordNo();
- case DataRowVersion.Current:
- return GetCurrentRecordNo();
- case DataRowVersion.Proposed:
- return GetProposedRecordNo();
- case DataRowVersion.Default:
- return GetDefaultRecord();
- default:
- throw ExceptionBuilder.InvalidRowVersion();
- }
- }
- internal DataRowVersion GetDefaultRowVersion(DataViewRowState viewState) {
- if (oldRecord == newRecord) {
- if (oldRecord == -1) {
- // should be DataView.addNewRow
- return DataRowVersion.Default;
- }
- Debug.Assert(0 != (DataViewRowState.Unchanged & viewState), "not DataViewRowState.Unchanged");
- return DataRowVersion.Default;
- }
- else if (oldRecord == -1) {
- Debug.Assert(0 != (DataViewRowState.Added & viewState), "not DataViewRowState.Added");
- return DataRowVersion.Default;
- }
- else if (newRecord == -1) {
- Debug.Assert(_action==DataRowAction.Rollback || 0 != (DataViewRowState.Deleted & viewState), "not DataViewRowState.Deleted");
- return DataRowVersion.Original;
- }
- else if (0 != (DataViewRowState.ModifiedCurrent & viewState)) {
- return DataRowVersion.Default;
- }
- Debug.Assert(0 != (DataViewRowState.ModifiedOriginal & viewState), "not DataViewRowState.ModifiedOriginal");
- return DataRowVersion.Original;
- }
- internal DataViewRowState GetRecordState(int record) {
- if (record == -1)
- return DataViewRowState.None;
- if (record == oldRecord && record == newRecord)
- return DataViewRowState.Unchanged;
- if (record == oldRecord)
- return(newRecord != -1) ? DataViewRowState.ModifiedOriginal : DataViewRowState.Deleted;
- if (record == newRecord)
- return(oldRecord != -1) ? DataViewRowState.ModifiedCurrent : DataViewRowState.Added;
- return DataViewRowState.None;
- }
- internal bool HasKeyChanged(DataKey key) {
- return HasKeyChanged(key, DataRowVersion.Current, DataRowVersion.Proposed);
- }
- internal bool HasKeyChanged(DataKey key, DataRowVersion version1, DataRowVersion version2) {
- if (!HasVersion(version1) || !HasVersion(version2))
- return true;
- return !key.RecordsEqual(GetRecordFromVersion(version1), GetRecordFromVersion(version2));
- }
- /// <devdoc>
- /// <para>
- /// Gets a value indicating whether a specified version exists.
- /// </para>
- /// </devdoc>
- public bool HasVersion(DataRowVersion version) {
- switch (version) {
- case DataRowVersion.Original:
- return(oldRecord != -1);
- case DataRowVersion.Current:
- return(newRecord != -1);
- case DataRowVersion.Proposed:
- return(tempRecord != -1);
- case DataRowVersion.Default:
- return(tempRecord != -1 || newRecord != -1);
- default:
- throw ExceptionBuilder.InvalidRowVersion();
- }
- }
- internal bool HasChanges() {
- if (!HasVersion(DataRowVersion.Original) || !HasVersion(DataRowVersion.Current)) {
- return true; // if does not have original, its added row, if does not have current, its deleted row so it has changes
- }
- foreach(DataColumn dc in Table.Columns) {
- if (dc.Compare(oldRecord, newRecord) != 0) {
- return true;
- }
- }
- return false;
- }
- internal bool HaveValuesChanged(DataColumn[] columns) {
- return HaveValuesChanged(columns, DataRowVersion.Current, DataRowVersion.Proposed);
- }
- internal bool HaveValuesChanged(DataColumn[] columns, DataRowVersion version1, DataRowVersion version2) {
- for (int i = 0; i < columns.Length; i++) {
- CheckColumn(columns[i]);
- }
- DataKey key = new DataKey(columns, false); // temporary key, don't copy columns
- return HasKeyChanged(key, version1, version2);
- }
- /// <devdoc>
- /// <para>
- /// Gets
- /// a value indicating whether the column at the specified index contains a
- /// null value.
- /// </para>
- /// </devdoc>
- public bool IsNull(int columnIndex) {
- DataColumn column = _columns[columnIndex];
- int record = GetDefaultRecord();
- return column.IsNull(record);
- }
- /// <devdoc>
- /// <para>
- /// Gets a value indicating whether the named column contains a null value.
- /// </para>
- /// </devdoc>
- public bool IsNull(string columnName) {
- DataColumn column = GetDataColumn(columnName);
- int record = GetDefaultRecord();
- return column.IsNull(record);
- }
- /// <devdoc>
- /// <para>
- /// Gets a value indicating whether the specified <see cref='System.Data.DataColumn'/>
- /// contains a null value.
- /// </para>
- /// </devdoc>
- public bool IsNull(DataColumn column) {
- CheckColumn(column);
- int record = GetDefaultRecord();
- return column.IsNull(record);
- }
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- public bool IsNull(DataColumn column, DataRowVersion version) {
- CheckColumn(column);
- int record = GetRecordFromVersion(version);
- return column.IsNull(record);
- }
- /// <devdoc>
- /// <para>
- /// Rejects all changes made to the row since <see cref='System.Data.DataRow.AcceptChanges'/>
- /// was last called.
- /// </para>
- /// </devdoc>
- public void RejectChanges() {
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<ds.DataRow.RejectChanges|API> %d#\n", ObjectID);
- try {
- if (this.RowState != DataRowState.Detached) {
- if (_columns.ColumnsImplementingIChangeTrackingCount != _columns.ColumnsImplementingIRevertibleChangeTrackingCount) {
- foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
- if (!dc.ImplementsIRevertibleChangeTracking) {
- object value = null;
- if (this.RowState != DataRowState.Deleted)
- value = this[dc];
- else
- value = this[dc, DataRowVersion.Original];
- if (DBNull.Value != value){
- if (((IChangeTracking)value).IsChanged) {
- throw ExceptionBuilder.UDTImplementsIChangeTrackingButnotIRevertible(dc.DataType.AssemblyQualifiedName);
- }
- }
- }
- }
- }
- foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
- object value = null;
- if (this.RowState != DataRowState.Deleted)
- value = this[dc];
- else
- value = this[dc, DataRowVersion.Original];
- if (DBNull.Value != value) {
- IChangeTracking tracking = (IChangeTracking)value;
- if (tracking.IsChanged) {
- ((IRevertibleChangeTracking)value).RejectChanges();
- }
- }
- }
- }
- _table.RollbackRow(this);
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
-
- internal void ResetLastChangedColumn() {
- _lastChangedColumn = null;
- _countColumnChange = 0;
- }
- internal void SetKeyValues(DataKey key, object[] keyValues) {
- bool fFirstCall = true;
- bool immediate = (tempRecord == -1);
- for (int i = 0; i < keyValues.Length; i++) {
- object value = this[key.ColumnsReference[i]];
- if (!value.Equals(keyValues[i])) {
- if (immediate && fFirstCall) {
- fFirstCall = false;
- BeginEditInternal();
- }
- this[key.ColumnsReference[i]] = keyValues[i];
- }
- }
- if (!fFirstCall)
- EndEdit();
- }
- /// <devdoc>
- /// <para>
- /// Sets the specified column's value to a null value.
- /// </para>
- /// </devdoc>
- protected void SetNull(DataColumn column) {
- this[column] = DBNull.Value;
- }
- internal void SetNestedParentRow(DataRow parentRow, bool setNonNested) {
- if (parentRow == null) {
- SetParentRowToDBNull();
- return;
- }
- foreach (DataRelation relation in _table.ParentRelations) {
- if (relation.Nested || setNonNested) {
- if (relation.ParentKey.Table == parentRow._table) {
- object[] parentKeyValues = parentRow.GetKeyValues(relation.ParentKey);
- this.SetKeyValues(relation.ChildKey, parentKeyValues);
- if (relation.Nested) {
- if (parentRow._table == _table)
- this.CheckForLoops(relation);
- else
- this.GetParentRow(relation);
- }
- }
- }
- }
- }
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- public void SetParentRow(DataRow parentRow) {
- SetNestedParentRow(parentRow, true);
- }
- /// <devdoc>
- /// <para>
- /// Sets current row's parent row with specified relation.
- /// </para>
- /// </devdoc>
- public void SetParentRow(DataRow parentRow, DataRelation relation) {
- if (relation == null) {
- SetParentRow(parentRow);
- return;
- }
- if (parentRow == null) {
- SetParentRowToDBNull(relation);
- return;
- }
- //if (-1 == rowID)
- // throw ExceptionBuilder.ChildRowNotInTheTable();
- //if (-1 == parentRow.rowID)
- // throw ExceptionBuilder.ParentRowNotInTheTable();
- if (_table.DataSet != parentRow._table.DataSet)
- throw ExceptionBuilder.ParentRowNotInTheDataSet();
- if (relation.ChildKey.Table != _table)
- throw ExceptionBuilder.SetParentRowTableMismatch(relation.ChildKey.Table.TableName, _table.TableName);
- if (relation.ParentKey.Table != parentRow._table)
- throw ExceptionBuilder.SetParentRowTableMismatch(relation.ParentKey.Table.TableName, parentRow._table.TableName);
- object[] parentKeyValues = parentRow.GetKeyValues(relation.ParentKey);
- this.SetKeyValues(relation.ChildKey, parentKeyValues);
- }
- internal void SetParentRowToDBNull() {
- //if (-1 == rowID)
- // throw ExceptionBuilder.ChildRowNotInTheTable();
- foreach (DataRelation relation in _table.ParentRelations)
- SetParentRowToDBNull(relation);
- }
- internal void SetParentRowToDBNull(DataRelation relation) {
- Debug.Assert(relation != null, "The relation should not be null here.");
- //if (-1 == rowID)
- // throw ExceptionBuilder.ChildRowNotInTheTable();
- if (relation.ChildKey.Table != _table)
- throw ExceptionBuilder.SetParentRowTableMismatch(relation.ChildKey.Table.TableName, _table.TableName);
- object[] parentKeyValues = new object[1];
- parentKeyValues[0] = DBNull.Value;
- this.SetKeyValues(relation.ChildKey, parentKeyValues);
- }
- public void SetAdded(){
- if (this.RowState == DataRowState.Unchanged) {
- _table.SetOldRecord(this, -1);
- }
- else {
- throw ExceptionBuilder.SetAddedAndModifiedCalledOnnonUnchanged();
- }
- }
- public void SetModified(){
- if (this.RowState == DataRowState.Unchanged) {
- tempRecord = _table.NewRecord(newRecord);
- if (tempRecord != -1) {
- // suppressing the ensure property changed because no values have changed
- _table.SetNewRecord(this, tempRecord, suppressEnsurePropertyChanged: true);
- }
- }
- else {
- throw ExceptionBuilder.SetAddedAndModifiedCalledOnnonUnchanged();
- }
- }
- /*
- RecordList contains the empty column storage needed. We need to copy the existing record values into this storage.
- */
- internal int CopyValuesIntoStore(ArrayList storeList, ArrayList nullbitList, int storeIndex) {
- int recordCount = 0;
- if (oldRecord != -1) {//Copy original record for the row in Unchanged, Modified, Deleted state.
- for (int i = 0; i < _columns.Count; i++) {
- _columns[i].CopyValueIntoStore(oldRecord, storeList[i], (BitArray) nullbitList[i], storeIndex);
- }
- recordCount++;
- storeIndex++;
- }
- DataRowState state = RowState;
- if ((DataRowState.Added == state) || (DataRowState.Modified == state)) { //Copy current record for the row in Added, Modified state.
- for (int i = 0; i < _columns.Count; i++) {
- _columns[i].CopyValueIntoStore(newRecord, storeList[i], (BitArray) nullbitList[i], storeIndex);
- }
- recordCount++;
- storeIndex++;
- }
- if (-1 != tempRecord) {//Copy temp record for the row in edit mode.
- for (int i = 0; i < _columns.Count; i++) {
- _columns[i].CopyValueIntoStore(tempRecord, storeList[i], (BitArray)nullbitList[i], storeIndex);
- }
- recordCount++;
- storeIndex++;
- }
- return recordCount;
- }
-
- [Conditional("DEBUG")]
- private void VerifyValueFromStorage(DataColumn column, DataRowVersion version, object valueFromStorage) {
- // Dev11 900390: ignore deleted rows by adding "newRecord != -1" condition - we do not evaluate computed rows if they are deleted
- if (column.DataExpression != null && !inChangingEvent && tempRecord == -1 && newRecord != -1)
- {
- // for unchanged rows, check current if original is asked for.
- // this is because by design, there is only single storage for an unchanged row.
- if (version == DataRowVersion.Original && oldRecord == newRecord) {
- version = DataRowVersion.Current;
- }
- // There are various known issues detected by this assert for non-default versions,
- // for example DevDiv2 bug 73753
- // Since changes consitutute breaking change (either way customer will get another result),
- // we decided not to fix them in Dev 11
- Debug.Assert(valueFromStorage.Equals(column.DataExpression.Evaluate(this, version)),
- "Value from storage does lazily computed expression value");
- }
- }
- }
- public sealed class DataRowBuilder {
- internal readonly DataTable _table;
- internal int _record;
- internal DataRowBuilder(DataTable table, int record) {
- _table = table;
- _record = record;
- }
- }
- }
|