DataRow.cs 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DataRow.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data {
  9. using System;
  10. using System.Collections;
  11. using System.ComponentModel;
  12. using System.Diagnostics;
  13. using System.Globalization;
  14. using System.Xml;
  15. /// <devdoc>
  16. /// <para>Represents a row of data in a <see cref='System.Data.DataTable'/>.</para>
  17. /// </devdoc>
  18. public class DataRow {
  19. private readonly DataTable _table;
  20. private readonly DataColumnCollection _columns;
  21. internal int oldRecord = -1;
  22. internal int newRecord = -1;
  23. internal int tempRecord;
  24. internal long _rowID = -1;
  25. internal DataRowAction _action;
  26. internal bool inChangingEvent;
  27. internal bool inDeletingEvent;
  28. internal bool inCascade;
  29. private DataColumn _lastChangedColumn; // last successfully changed column
  30. private int _countColumnChange; // number of columns changed during edit mode
  31. private DataError error;
  32. private object _element;
  33. private int _rbTreeNodeId; // if row is not detached, Id used for computing index in rows collection
  34. private static int _objectTypeCount; // Bid counter
  35. internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  36. /// <devdoc>
  37. /// <para>
  38. /// Initializes a new instance of the DataRow.
  39. /// </para>
  40. /// <para>
  41. /// Constructs a row from the builder. Only for internal usage..
  42. /// </para>
  43. /// </devdoc>
  44. protected internal DataRow (DataRowBuilder builder) {
  45. tempRecord = builder._record;
  46. _table = builder._table;
  47. _columns = _table.Columns;
  48. }
  49. internal XmlBoundElement Element {
  50. get {
  51. return (XmlBoundElement) _element;
  52. }
  53. set {
  54. _element = value;
  55. }
  56. }
  57. internal DataColumn LastChangedColumn {
  58. get { // last successfully changed column or if multiple columns changed: null
  59. if (_countColumnChange != 1) {
  60. return null;
  61. }
  62. return _lastChangedColumn;
  63. }
  64. set {
  65. _countColumnChange++;
  66. _lastChangedColumn = value;
  67. }
  68. }
  69. internal bool HasPropertyChanged {
  70. get { return (0 < _countColumnChange); }
  71. }
  72. internal int RBTreeNodeId {
  73. get {
  74. return _rbTreeNodeId;
  75. }
  76. set {
  77. Bid.Trace("<ds.DataRow.set_RBTreeNodeId|INFO> %d#, value=%d\n", ObjectID, value);
  78. _rbTreeNodeId = value;
  79. }
  80. }
  81. /// <devdoc>
  82. /// <para>Gets or sets the custom error description for a row.</para>
  83. /// </devdoc>
  84. public string RowError {
  85. get {
  86. return(error == null ? String.Empty :error.Text);
  87. }
  88. set {
  89. Bid.Trace("<ds.DataRow.set_RowError|API> %d#, value='%ls'\n", ObjectID, value);
  90. if (error == null) {
  91. if (!Common.ADP.IsEmpty(value)) {
  92. error = new DataError(value);
  93. }
  94. RowErrorChanged();
  95. }
  96. else if(error.Text != value) {
  97. error.Text = value;
  98. RowErrorChanged();
  99. }
  100. }
  101. }
  102. private void RowErrorChanged() {
  103. // We don't know wich record was used by view index. try to use both.
  104. if (oldRecord != -1)
  105. _table.RecordChanged(oldRecord);
  106. if (newRecord != -1)
  107. _table.RecordChanged(newRecord);
  108. }
  109. internal long rowID {
  110. get {
  111. return _rowID;
  112. }
  113. set {
  114. ResetLastChangedColumn();
  115. _rowID = value;
  116. }
  117. }
  118. /// <devdoc>
  119. /// <para>Gets the current state of the row in regards to its relationship to the table.</para>
  120. /// </devdoc>
  121. public DataRowState RowState {
  122. get {
  123. /*
  124. if (oldRecord == -1 && newRecord == -1)
  125. state = DataRowState.Detached; // 2
  126. else if (oldRecord == newRecord)
  127. state = DataRowState.Unchanged; // 2
  128. else if (oldRecord == -1)
  129. state = DataRowState.Added; // 4
  130. else if (newRecord == -1)
  131. state = DataRowState.Deleted; // 4
  132. else
  133. state = DataRowState.Modified; // 4
  134. */
  135. if (oldRecord == newRecord) {
  136. if (oldRecord == -1) {
  137. return DataRowState.Detached; // 2
  138. }
  139. if (0 < _columns.ColumnsImplementingIChangeTrackingCount) {
  140. foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
  141. object value = this[dc];
  142. if ((DBNull.Value != value) && ((IChangeTracking)value).IsChanged) {
  143. return DataRowState.Modified; // 3 + _columns.columnsImplementingIChangeTracking.Count
  144. }
  145. }
  146. }
  147. return DataRowState.Unchanged; // 3
  148. }
  149. else if (oldRecord == -1) {
  150. return DataRowState.Added; // 2
  151. }
  152. else if (newRecord == -1) {
  153. return DataRowState.Deleted; // 3
  154. }
  155. return DataRowState.Modified; // 3
  156. }
  157. }
  158. /// <devdoc>
  159. /// <para>Gets the <see cref='System.Data.DataTable'/>
  160. /// for which this row has a schema.</para>
  161. /// </devdoc>
  162. public DataTable Table {
  163. get {
  164. return _table;
  165. }
  166. }
  167. /// <devdoc>
  168. /// <para>Gets or sets the data stored in the column specified by index.</para>
  169. /// </devdoc>
  170. public object this[int columnIndex] {
  171. get {
  172. DataColumn column = _columns[columnIndex];
  173. int record = GetDefaultRecord();
  174. _table.recordManager.VerifyRecord(record, this);
  175. VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
  176. return column[record];
  177. }
  178. set {
  179. DataColumn column = _columns[columnIndex];
  180. this[column] = value;
  181. }
  182. }
  183. internal void CheckForLoops(DataRelation rel){
  184. // don't check for loops in the diffgram
  185. // because there may be some holes in the rowCollection
  186. // and index creation may fail. The check will be done
  187. // after all the loading is done _and_ we are sure there
  188. // are no holes in the collection.
  189. if (_table.fInLoadDiffgram || (_table.DataSet != null && _table.DataSet.fInLoadDiffgram))
  190. return;
  191. int count = _table.Rows.Count, i = 0;
  192. // need to optimize this for count > 100
  193. DataRow parent = this.GetParentRow(rel);
  194. while (parent != null) {
  195. if ((parent == this) || (i>count))
  196. throw ExceptionBuilder.NestedCircular(_table.TableName);
  197. i++;
  198. parent = parent.GetParentRow(rel);
  199. }
  200. }
  201. internal int GetNestedParentCount() {
  202. int count = 0;
  203. DataRelation[] nestedParentRelations = _table.NestedParentRelations;
  204. foreach(DataRelation rel in nestedParentRelations) {
  205. if (rel == null) // don't like this but done for backward code compatability
  206. continue;
  207. if (rel.ParentTable == _table) // self-nested table
  208. this.CheckForLoops(rel);
  209. DataRow row = this.GetParentRow(rel);
  210. if (row != null) {
  211. count++;
  212. }
  213. }
  214. return count ;
  215. // Rule 1: At all times, only ONE FK "(in a row) can be non-Null
  216. // we wont allow a row to have multiple parents, as we cant handle it , also in diffgram
  217. }
  218. /// <devdoc>
  219. /// <para>Gets or sets the data stored in the column specified by
  220. /// name.</para>
  221. /// </devdoc>
  222. public object this[string columnName] {
  223. get {
  224. DataColumn column = GetDataColumn(columnName);
  225. int record = GetDefaultRecord();
  226. _table.recordManager.VerifyRecord(record, this);
  227. VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
  228. return column[record];
  229. }
  230. set {
  231. DataColumn column = GetDataColumn(columnName);
  232. this[column] = value;
  233. }
  234. }
  235. /// <devdoc>
  236. /// <para>Gets or sets
  237. /// the data stored in the specified <see cref='System.Data.DataColumn'/>.</para>
  238. /// </devdoc>
  239. public object this[DataColumn column] {
  240. get {
  241. CheckColumn(column);
  242. int record = GetDefaultRecord();
  243. _table.recordManager.VerifyRecord(record, this);
  244. VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
  245. return column[record];
  246. }
  247. set {
  248. CheckColumn(column);
  249. if (inChangingEvent) {
  250. throw ExceptionBuilder.EditInRowChanging();
  251. }
  252. if ((-1 != rowID) && column.ReadOnly) {
  253. throw ExceptionBuilder.ReadOnly(column.ColumnName);
  254. }
  255. // allow users to tailor the proposed value, or throw an exception.
  256. // note we intentionally do not try/catch this event.
  257. // note: we also allow user to do anything at this point
  258. // infinite loops are possible if user calls Item or ItemArray during the event
  259. DataColumnChangeEventArgs e = null;
  260. if (_table.NeedColumnChangeEvents) {
  261. e = new DataColumnChangeEventArgs(this, column, value);
  262. _table.OnColumnChanging(e);
  263. }
  264. if (column.Table != _table) {
  265. // user removed column from table during OnColumnChanging event
  266. throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
  267. }
  268. if ((-1 != rowID) && column.ReadOnly) {
  269. // user adds row to table during OnColumnChanging event
  270. throw ExceptionBuilder.ReadOnly(column.ColumnName);
  271. }
  272. object proposed = ((null != e) ? e.ProposedValue : value);
  273. if (null == proposed) {
  274. if (column.IsValueType) { // WebData 105963
  275. throw ExceptionBuilder.CannotSetToNull(column);
  276. }
  277. proposed = DBNull.Value;
  278. }
  279. bool immediate = BeginEditInternal();
  280. try {
  281. int record = GetProposedRecordNo();
  282. _table.recordManager.VerifyRecord(record, this);
  283. column[record] = proposed;
  284. }
  285. catch (Exception e1){
  286. //
  287. if (Common.ADP.IsCatchableOrSecurityExceptionType(e1)) {
  288. if (immediate) {
  289. Debug.Assert(!inChangingEvent, "how are we in a changing event to cancel?");
  290. Debug.Assert(-1 != tempRecord, "how no propsed record to cancel?");
  291. CancelEdit(); // WebData 107154
  292. }
  293. }
  294. throw;
  295. }
  296. LastChangedColumn = column;
  297. // note: we intentionally do not try/catch this event.
  298. // infinite loops are possible if user calls Item or ItemArray during the event
  299. if (null != e) {
  300. _table.OnColumnChanged(e); // user may call CancelEdit or EndEdit
  301. }
  302. if (immediate) {
  303. Debug.Assert(!inChangingEvent, "how are we in a changing event to end?");
  304. EndEdit();
  305. }
  306. }
  307. }
  308. /// <devdoc>
  309. /// <para>Gets the data stored
  310. /// in the column, specified by index and version of the data to retrieve.</para>
  311. /// </devdoc>
  312. public object this[int columnIndex, DataRowVersion version] {
  313. get {
  314. DataColumn column = _columns[columnIndex];
  315. int record = GetRecordFromVersion(version);
  316. _table.recordManager.VerifyRecord(record, this);
  317. VerifyValueFromStorage(column, version, column[record]);
  318. return column[record];
  319. }
  320. }
  321. /// <devdoc>
  322. /// <para> Gets the specified version of data stored in
  323. /// the named column.</para>
  324. /// </devdoc>
  325. public object this[string columnName, DataRowVersion version] {
  326. get {
  327. DataColumn column = GetDataColumn(columnName);
  328. int record = GetRecordFromVersion(version);
  329. _table.recordManager.VerifyRecord(record, this);
  330. VerifyValueFromStorage(column, version, column[record]);
  331. return column[record];
  332. }
  333. }
  334. /// <devdoc>
  335. /// <para>Gets the specified version of data stored in the specified <see cref='System.Data.DataColumn'/>.</para>
  336. /// </devdoc>
  337. public object this[DataColumn column, DataRowVersion version] {
  338. get {
  339. CheckColumn(column);
  340. int record = GetRecordFromVersion(version);
  341. _table.recordManager.VerifyRecord(record, this);
  342. VerifyValueFromStorage(column, version, column[record]);
  343. return column[record];
  344. }
  345. }
  346. /// <devdoc>
  347. /// <para>Gets
  348. /// or sets all of the values for this row through an array.</para>
  349. /// </devdoc>
  350. public object[] ItemArray {
  351. get {
  352. int record = GetDefaultRecord();
  353. _table.recordManager.VerifyRecord(record, this);
  354. object[] values = new object[_columns.Count];
  355. for (int i = 0; i < values.Length; i++) {
  356. DataColumn column = _columns[i];
  357. VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
  358. values[i] = column[record];
  359. }
  360. return values;
  361. }
  362. set {
  363. if (null == value) { // WebData 104372
  364. throw ExceptionBuilder.ArgumentNull("ItemArray");
  365. }
  366. if (_columns.Count < value.Length) {
  367. throw ExceptionBuilder.ValueArrayLength();
  368. }
  369. DataColumnChangeEventArgs e = null;
  370. if (_table.NeedColumnChangeEvents) {
  371. e = new DataColumnChangeEventArgs(this);
  372. }
  373. bool immediate = BeginEditInternal();
  374. for (int i = 0; i < value.Length; ++i) {
  375. // Empty means don't change the row.
  376. if (null != value[i]) {
  377. // may throw exception if user removes column from table during event
  378. DataColumn column = _columns[i];
  379. if ((-1 != rowID) && column.ReadOnly) {
  380. throw ExceptionBuilder.ReadOnly(column.ColumnName);
  381. }
  382. // allow users to tailor the proposed value, or throw an exception.
  383. // note: we intentionally do not try/catch this event.
  384. // note: we also allow user to do anything at this point
  385. // infinite loops are possible if user calls Item or ItemArray during the event
  386. if (null != e) {
  387. e.InitializeColumnChangeEvent(column, value[i]);
  388. _table.OnColumnChanging(e);
  389. }
  390. if (column.Table != _table) {
  391. // user removed column from table during OnColumnChanging event
  392. throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
  393. }
  394. if ((-1 != rowID) && column.ReadOnly) {
  395. // user adds row to table during OnColumnChanging event
  396. throw ExceptionBuilder.ReadOnly(column.ColumnName);
  397. }
  398. if (tempRecord == -1) {
  399. // user affected CancelEdit or EndEdit during OnColumnChanging event of the last value
  400. BeginEditInternal();
  401. }
  402. object proposed = (null != e) ? e.ProposedValue : value[i];
  403. if (null == proposed) {
  404. if (column.IsValueType) { // WebData 105963
  405. throw ExceptionBuilder.CannotSetToNull(column);
  406. }
  407. proposed = DBNull.Value;
  408. }
  409. try {
  410. // must get proposed record after each event because user may have
  411. // called EndEdit(), AcceptChanges(), BeginEdit() during the event
  412. int record = GetProposedRecordNo();
  413. _table.recordManager.VerifyRecord(record, this);
  414. column[record] = proposed;
  415. }
  416. catch (Exception e1) {
  417. //
  418. if (Common.ADP.IsCatchableOrSecurityExceptionType(e1)) {
  419. if (immediate) {
  420. Debug.Assert(!inChangingEvent, "how are we in a changing event to cancel?");
  421. Debug.Assert(-1 != tempRecord, "how no propsed record to cancel?");
  422. CancelEdit(); // WebData 107154
  423. }
  424. }
  425. throw;
  426. }
  427. LastChangedColumn = column;
  428. // note: we intentionally do not try/catch this event.
  429. // infinite loops are possible if user calls Item or ItemArray during the event
  430. if (null != e) {
  431. _table.OnColumnChanged(e); // user may call CancelEdit or EndEdit
  432. }
  433. }
  434. }
  435. // proposed breaking change: if (immediate){ EndEdit(); } because table currently always fires RowChangedEvent
  436. Debug.Assert(!inChangingEvent, "how are we in a changing event to end?");
  437. EndEdit();
  438. }
  439. }
  440. /// <devdoc>
  441. /// <para>Commits all the changes made to this row
  442. /// since the last time <see cref='System.Data.DataRow.AcceptChanges'/> was called.</para>
  443. /// </devdoc>
  444. public void AcceptChanges() {
  445. IntPtr hscp;
  446. Bid.ScopeEnter(out hscp, "<ds.DataRow.AcceptChanges|API> %d#\n", ObjectID);
  447. try {
  448. EndEdit();
  449. if (this.RowState != DataRowState.Detached && this.RowState != DataRowState.Deleted) {
  450. if (_columns.ColumnsImplementingIChangeTrackingCount > 0) {
  451. foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
  452. object value = this[dc];
  453. if (DBNull.Value != value) {
  454. IChangeTracking tracking = (IChangeTracking)value;
  455. if (tracking.IsChanged) {
  456. tracking.AcceptChanges();
  457. }
  458. }
  459. }
  460. }
  461. }
  462. _table.CommitRow(this);
  463. }
  464. finally {
  465. Bid.ScopeLeave(ref hscp);
  466. }
  467. }
  468. /// <devdoc>
  469. /// <para>Begins an edit operation on a <see cref='System.Data.DataRow'/>object.</para>
  470. /// </devdoc>
  471. [
  472. EditorBrowsableAttribute(EditorBrowsableState.Advanced),
  473. ]
  474. public void BeginEdit() {
  475. BeginEditInternal();
  476. }
  477. private bool BeginEditInternal() {
  478. if (inChangingEvent) {
  479. throw ExceptionBuilder.BeginEditInRowChanging();
  480. }
  481. if (tempRecord != -1) {
  482. if (tempRecord < _table.recordManager.LastFreeRecord) {
  483. return false; // we will not call EndEdit
  484. }
  485. else {
  486. // partial fix for detached row after Table.Clear scenario
  487. // in debug, it will have asserted earlier, but with this
  488. // it will go get a new record for editing
  489. tempRecord = -1;
  490. }
  491. // shifted VerifyRecord to first make the correction, then verify
  492. _table.recordManager.VerifyRecord(tempRecord, this);
  493. }
  494. if (oldRecord != -1 && newRecord == -1) {
  495. throw ExceptionBuilder.DeletedRowInaccessible();
  496. }
  497. //
  498. ResetLastChangedColumn(); // shouldn't have to do this
  499. tempRecord = _table.NewRecord(newRecord);
  500. Debug.Assert(-1 != tempRecord, "missing temp record");
  501. Debug.Assert(0 == _countColumnChange, "unexpected column change count");
  502. Debug.Assert(null == _lastChangedColumn, "unexpected last column change");
  503. return true;
  504. }
  505. /// <devdoc>
  506. /// <para>Cancels the current edit on the row.</para>
  507. /// </devdoc>
  508. [
  509. EditorBrowsableAttribute(EditorBrowsableState.Advanced),
  510. ]
  511. public void CancelEdit() {
  512. if (inChangingEvent) {
  513. throw ExceptionBuilder.CancelEditInRowChanging();
  514. }
  515. _table.FreeRecord(ref tempRecord);
  516. Debug.Assert(-1 == tempRecord, "unexpected temp record");
  517. ResetLastChangedColumn();
  518. }
  519. private void CheckColumn(DataColumn column) {
  520. if (column == null) {
  521. throw ExceptionBuilder.ArgumentNull("column");
  522. }
  523. if (column.Table != _table) {
  524. throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
  525. }
  526. }
  527. /// <devdoc>
  528. /// Throws a RowNotInTableException if row isn't in table.
  529. /// </devdoc>
  530. internal void CheckInTable() {
  531. if (rowID == -1) {
  532. throw ExceptionBuilder.RowNotInTheTable();
  533. }
  534. }
  535. /// <devdoc>
  536. /// <para>Deletes the row.</para>
  537. /// </devdoc>
  538. public void Delete() {
  539. if (inDeletingEvent) {
  540. throw ExceptionBuilder.DeleteInRowDeleting();
  541. }
  542. if (newRecord == -1)
  543. return;
  544. _table.DeleteRow(this);
  545. }
  546. /// <devdoc>
  547. /// <para>Ends the edit occurring on the row.</para>
  548. /// </devdoc>
  549. [
  550. EditorBrowsableAttribute(EditorBrowsableState.Advanced),
  551. ]
  552. public void EndEdit() {
  553. if (inChangingEvent) {
  554. throw ExceptionBuilder.EndEditInRowChanging();
  555. }
  556. if (newRecord == -1) {
  557. return; // this is meaningless, detatched row case
  558. }
  559. if (tempRecord != -1) {
  560. try {
  561. // suppressing the ensure property changed because it's possible that no values have been modified
  562. _table.SetNewRecord(this, tempRecord, suppressEnsurePropertyChanged: true);
  563. }
  564. finally {
  565. // a constraint violation may be thrown during SetNewRecord
  566. ResetLastChangedColumn();
  567. }
  568. }
  569. }
  570. /// <devdoc>
  571. /// <para>Sets the error description for a column specified by index.</para>
  572. /// </devdoc>
  573. public void SetColumnError(int columnIndex, string error) {
  574. DataColumn column = _columns[columnIndex];
  575. if (column == null)
  576. throw ExceptionBuilder.ColumnOutOfRange(columnIndex);
  577. SetColumnError(column, error);
  578. }
  579. /// <devdoc>
  580. /// <para>Sets
  581. /// the error description for a column specified by name.</para>
  582. /// </devdoc>
  583. public void SetColumnError(string columnName, string error) {
  584. DataColumn column = GetDataColumn(columnName);
  585. SetColumnError(column, error);
  586. }
  587. /// <devdoc>
  588. /// <para>Sets the error description for a column specified as a <see cref='System.Data.DataColumn'/>.</para>
  589. /// </devdoc>
  590. public void SetColumnError(DataColumn column, string error) {
  591. CheckColumn(column);
  592. IntPtr hscp;
  593. Bid.ScopeEnter(out hscp, "<ds.DataRow.SetColumnError|API> %d#, column=%d, error='%ls'\n", ObjectID, column.ObjectID, error);
  594. try {
  595. if (this.error == null) this.error = new DataError();
  596. if(GetColumnError(column) != error) {
  597. this.error.SetColumnError(column, error);
  598. RowErrorChanged();
  599. }
  600. }
  601. finally {
  602. Bid.ScopeLeave(ref hscp);
  603. }
  604. }
  605. /// <devdoc>
  606. /// <para>Gets the error description for the column specified
  607. /// by index.</para>
  608. /// </devdoc>
  609. public string GetColumnError(int columnIndex) {
  610. DataColumn column = _columns[columnIndex];
  611. return GetColumnError(column);
  612. }
  613. /// <devdoc>
  614. /// <para>Gets the error description for a column, specified by name.</para>
  615. /// </devdoc>
  616. public string GetColumnError(string columnName) {
  617. DataColumn column = GetDataColumn(columnName);
  618. return GetColumnError(column);
  619. }
  620. /// <devdoc>
  621. /// <para>Gets the error description of
  622. /// the specified <see cref='System.Data.DataColumn'/>.</para>
  623. /// </devdoc>
  624. public string GetColumnError(DataColumn column) {
  625. CheckColumn(column);
  626. if (error == null) error = new DataError();
  627. return error.GetColumnError(column);
  628. }
  629. /// <summary>
  630. /// Clears the errors for the row, including the <see cref='System.Data.DataRow.RowError'/>
  631. /// and errors set with <see cref='System.Data.DataRow.SetColumnError(DataColumn, string)'/>
  632. /// </summary>
  633. public void ClearErrors() {
  634. if (error != null) {
  635. error.Clear();
  636. RowErrorChanged();
  637. }
  638. }
  639. internal void ClearError(DataColumn column) {
  640. if (error != null) {
  641. error.Clear(column);
  642. RowErrorChanged();
  643. }
  644. }
  645. /// <devdoc>
  646. /// <para>Gets a value indicating whether there are errors in a columns collection.</para>
  647. /// </devdoc>
  648. public bool HasErrors {
  649. get {
  650. return(error == null ? false : error.HasErrors);
  651. }
  652. }
  653. /// <devdoc>
  654. /// <para>Gets an array of columns that have errors.</para>
  655. /// </devdoc>
  656. public DataColumn[] GetColumnsInError() {
  657. if (error == null)
  658. return DataTable.zeroColumns;
  659. else
  660. return error.GetColumnsInError();
  661. }
  662. public DataRow[] GetChildRows(string relationName) {
  663. return GetChildRows(_table.ChildRelations[relationName], DataRowVersion.Default);
  664. }
  665. public DataRow[] GetChildRows(string relationName, DataRowVersion version) {
  666. return GetChildRows(_table.ChildRelations[relationName], version);
  667. }
  668. /// <devdoc>
  669. /// <para>Gets the child rows of this <see cref='System.Data.DataRow'/> using the
  670. /// specified <see cref='System.Data.DataRelation'/>
  671. /// .</para>
  672. /// </devdoc>
  673. public DataRow[] GetChildRows(DataRelation relation) {
  674. return GetChildRows(relation, DataRowVersion.Default);
  675. }
  676. /// <devdoc>
  677. /// <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>
  678. /// </devdoc>
  679. public DataRow[] GetChildRows(DataRelation relation, DataRowVersion version) {
  680. if (relation == null)
  681. return _table.NewRowArray(0);
  682. //if (-1 == rowID)
  683. // throw ExceptionBuilder.RowNotInTheTable();
  684. if (relation.DataSet != _table.DataSet)
  685. throw ExceptionBuilder.RowNotInTheDataSet();
  686. if (relation.ParentKey.Table != _table)
  687. throw ExceptionBuilder.RelationForeignTable(relation.ParentTable.TableName, _table.TableName);
  688. return DataRelation.GetChildRows(relation.ParentKey, relation.ChildKey, this, version);
  689. }
  690. internal DataColumn GetDataColumn(string columnName) {
  691. DataColumn column = _columns[columnName];
  692. if (null != column) {
  693. return column;
  694. }
  695. throw ExceptionBuilder.ColumnNotInTheTable(columnName, _table.TableName);
  696. }
  697. public DataRow GetParentRow(string relationName) {
  698. return GetParentRow(_table.ParentRelations[relationName], DataRowVersion.Default);
  699. }
  700. public DataRow GetParentRow(string relationName, DataRowVersion version) {
  701. return GetParentRow(_table.ParentRelations[relationName], version);
  702. }
  703. /// <devdoc>
  704. /// <para>Gets the parent row of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> .</para>
  705. /// </devdoc>
  706. public DataRow GetParentRow(DataRelation relation) {
  707. return GetParentRow(relation, DataRowVersion.Default);
  708. }
  709. /// <devdoc>
  710. /// <para>Gets the parent row of this <see cref='System.Data.DataRow'/>
  711. /// using the specified <see cref='System.Data.DataRelation'/> and <see cref='System.Data.DataRowVersion'/>.</para>
  712. /// </devdoc>
  713. public DataRow GetParentRow(DataRelation relation, DataRowVersion version) {
  714. if (relation == null)
  715. return null;
  716. //if (-1 == rowID)
  717. // throw ExceptionBuilder.RowNotInTheTable();
  718. if (relation.DataSet != _table.DataSet)
  719. throw ExceptionBuilder.RelationForeignRow();
  720. if (relation.ChildKey.Table != _table)
  721. throw ExceptionBuilder.GetParentRowTableMismatch(relation.ChildTable.TableName, _table.TableName);
  722. return DataRelation.GetParentRow(relation.ParentKey, relation.ChildKey, this, version);
  723. }
  724. // a multiple nested child table's row can have only one non-null FK per row. So table has multiple
  725. // parents, but a row can have only one parent. Same nested row cannot below to 2 parent rows.
  726. internal DataRow GetNestedParentRow(DataRowVersion version) {
  727. // 1) Walk over all FKs and get the non-null. 2) Get the relation. 3) Get the parent Row.
  728. DataRelation[] nestedParentRelations = _table.NestedParentRelations;
  729. foreach(DataRelation rel in nestedParentRelations) {
  730. if (rel == null) // don't like this but done for backward code compatability
  731. continue;
  732. if (rel.ParentTable == _table) // self-nested table
  733. this.CheckForLoops(rel);
  734. DataRow row = this.GetParentRow(rel, version);
  735. if (row != null) {
  736. return row;
  737. }
  738. }
  739. return null;// Rule 1: At all times, only ONE FK "(in a row) can be non-Null
  740. }
  741. // No Nested in 1-many
  742. /// <devdoc>
  743. /// <para>[To be supplied.]</para>
  744. /// </devdoc>
  745. public DataRow[] GetParentRows(string relationName) {
  746. return GetParentRows(_table.ParentRelations[relationName], DataRowVersion.Default);
  747. }
  748. /// <devdoc>
  749. /// <para>[To be supplied.]</para>
  750. /// </devdoc>
  751. public DataRow[] GetParentRows(string relationName, DataRowVersion version) {
  752. return GetParentRows(_table.ParentRelations[relationName], version);
  753. }
  754. /// <devdoc>
  755. /// <para>
  756. /// Gets the parent rows of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> .
  757. /// </para>
  758. /// </devdoc>
  759. public DataRow[] GetParentRows(DataRelation relation) {
  760. return GetParentRows(relation, DataRowVersion.Default);
  761. }
  762. /// <devdoc>
  763. /// <para>
  764. /// Gets the parent rows of this <see cref='System.Data.DataRow'/> using the specified <see cref='System.Data.DataRelation'/> .
  765. /// </para>
  766. /// </devdoc>
  767. public DataRow[] GetParentRows(DataRelation relation, DataRowVersion version) {
  768. if (relation == null)
  769. return _table.NewRowArray(0);
  770. //if (-1 == rowID)
  771. // throw ExceptionBuilder.RowNotInTheTable();
  772. if (relation.DataSet != _table.DataSet)
  773. throw ExceptionBuilder.RowNotInTheDataSet();
  774. if (relation.ChildKey.Table != _table)
  775. throw ExceptionBuilder.GetParentRowTableMismatch(relation.ChildTable.TableName, _table.TableName);
  776. return DataRelation.GetParentRows(relation.ParentKey, relation.ChildKey, this, version);
  777. }
  778. internal object[] GetColumnValues(DataColumn[] columns) {
  779. return GetColumnValues(columns, DataRowVersion.Default);
  780. }
  781. internal object[] GetColumnValues(DataColumn[] columns, DataRowVersion version) {
  782. DataKey key = new DataKey(columns, false); // temporary key, don't copy columns
  783. return GetKeyValues(key, version);
  784. }
  785. internal object[] GetKeyValues(DataKey key) {
  786. int record = GetDefaultRecord();
  787. return key.GetKeyValues(record);
  788. }
  789. internal object[] GetKeyValues(DataKey key, DataRowVersion version) {
  790. int record = GetRecordFromVersion(version);
  791. return key.GetKeyValues(record);
  792. }
  793. internal int GetCurrentRecordNo() {
  794. if (newRecord == -1)
  795. throw ExceptionBuilder.NoCurrentData();
  796. return newRecord;
  797. }
  798. internal int GetDefaultRecord() {
  799. if (tempRecord != -1)
  800. return tempRecord;
  801. if (newRecord != -1) {
  802. return newRecord;
  803. }
  804. // If row has oldRecord - this is deleted row.
  805. if (oldRecord == -1)
  806. throw ExceptionBuilder.RowRemovedFromTheTable();
  807. else
  808. throw ExceptionBuilder.DeletedRowInaccessible();
  809. }
  810. internal int GetOriginalRecordNo() {
  811. if (oldRecord == -1)
  812. throw ExceptionBuilder.NoOriginalData();
  813. return oldRecord;
  814. }
  815. private int GetProposedRecordNo() {
  816. if (tempRecord == -1)
  817. throw ExceptionBuilder.NoProposedData();
  818. return tempRecord;
  819. }
  820. internal int GetRecordFromVersion(DataRowVersion version) {
  821. switch (version) {
  822. case DataRowVersion.Original:
  823. return GetOriginalRecordNo();
  824. case DataRowVersion.Current:
  825. return GetCurrentRecordNo();
  826. case DataRowVersion.Proposed:
  827. return GetProposedRecordNo();
  828. case DataRowVersion.Default:
  829. return GetDefaultRecord();
  830. default:
  831. throw ExceptionBuilder.InvalidRowVersion();
  832. }
  833. }
  834. internal DataRowVersion GetDefaultRowVersion(DataViewRowState viewState) {
  835. if (oldRecord == newRecord) {
  836. if (oldRecord == -1) {
  837. // should be DataView.addNewRow
  838. return DataRowVersion.Default;
  839. }
  840. Debug.Assert(0 != (DataViewRowState.Unchanged & viewState), "not DataViewRowState.Unchanged");
  841. return DataRowVersion.Default;
  842. }
  843. else if (oldRecord == -1) {
  844. Debug.Assert(0 != (DataViewRowState.Added & viewState), "not DataViewRowState.Added");
  845. return DataRowVersion.Default;
  846. }
  847. else if (newRecord == -1) {
  848. Debug.Assert(_action==DataRowAction.Rollback || 0 != (DataViewRowState.Deleted & viewState), "not DataViewRowState.Deleted");
  849. return DataRowVersion.Original;
  850. }
  851. else if (0 != (DataViewRowState.ModifiedCurrent & viewState)) {
  852. return DataRowVersion.Default;
  853. }
  854. Debug.Assert(0 != (DataViewRowState.ModifiedOriginal & viewState), "not DataViewRowState.ModifiedOriginal");
  855. return DataRowVersion.Original;
  856. }
  857. internal DataViewRowState GetRecordState(int record) {
  858. if (record == -1)
  859. return DataViewRowState.None;
  860. if (record == oldRecord && record == newRecord)
  861. return DataViewRowState.Unchanged;
  862. if (record == oldRecord)
  863. return(newRecord != -1) ? DataViewRowState.ModifiedOriginal : DataViewRowState.Deleted;
  864. if (record == newRecord)
  865. return(oldRecord != -1) ? DataViewRowState.ModifiedCurrent : DataViewRowState.Added;
  866. return DataViewRowState.None;
  867. }
  868. internal bool HasKeyChanged(DataKey key) {
  869. return HasKeyChanged(key, DataRowVersion.Current, DataRowVersion.Proposed);
  870. }
  871. internal bool HasKeyChanged(DataKey key, DataRowVersion version1, DataRowVersion version2) {
  872. if (!HasVersion(version1) || !HasVersion(version2))
  873. return true;
  874. return !key.RecordsEqual(GetRecordFromVersion(version1), GetRecordFromVersion(version2));
  875. }
  876. /// <devdoc>
  877. /// <para>
  878. /// Gets a value indicating whether a specified version exists.
  879. /// </para>
  880. /// </devdoc>
  881. public bool HasVersion(DataRowVersion version) {
  882. switch (version) {
  883. case DataRowVersion.Original:
  884. return(oldRecord != -1);
  885. case DataRowVersion.Current:
  886. return(newRecord != -1);
  887. case DataRowVersion.Proposed:
  888. return(tempRecord != -1);
  889. case DataRowVersion.Default:
  890. return(tempRecord != -1 || newRecord != -1);
  891. default:
  892. throw ExceptionBuilder.InvalidRowVersion();
  893. }
  894. }
  895. internal bool HasChanges() {
  896. if (!HasVersion(DataRowVersion.Original) || !HasVersion(DataRowVersion.Current)) {
  897. return true; // if does not have original, its added row, if does not have current, its deleted row so it has changes
  898. }
  899. foreach(DataColumn dc in Table.Columns) {
  900. if (dc.Compare(oldRecord, newRecord) != 0) {
  901. return true;
  902. }
  903. }
  904. return false;
  905. }
  906. internal bool HaveValuesChanged(DataColumn[] columns) {
  907. return HaveValuesChanged(columns, DataRowVersion.Current, DataRowVersion.Proposed);
  908. }
  909. internal bool HaveValuesChanged(DataColumn[] columns, DataRowVersion version1, DataRowVersion version2) {
  910. for (int i = 0; i < columns.Length; i++) {
  911. CheckColumn(columns[i]);
  912. }
  913. DataKey key = new DataKey(columns, false); // temporary key, don't copy columns
  914. return HasKeyChanged(key, version1, version2);
  915. }
  916. /// <devdoc>
  917. /// <para>
  918. /// Gets
  919. /// a value indicating whether the column at the specified index contains a
  920. /// null value.
  921. /// </para>
  922. /// </devdoc>
  923. public bool IsNull(int columnIndex) {
  924. DataColumn column = _columns[columnIndex];
  925. int record = GetDefaultRecord();
  926. return column.IsNull(record);
  927. }
  928. /// <devdoc>
  929. /// <para>
  930. /// Gets a value indicating whether the named column contains a null value.
  931. /// </para>
  932. /// </devdoc>
  933. public bool IsNull(string columnName) {
  934. DataColumn column = GetDataColumn(columnName);
  935. int record = GetDefaultRecord();
  936. return column.IsNull(record);
  937. }
  938. /// <devdoc>
  939. /// <para>
  940. /// Gets a value indicating whether the specified <see cref='System.Data.DataColumn'/>
  941. /// contains a null value.
  942. /// </para>
  943. /// </devdoc>
  944. public bool IsNull(DataColumn column) {
  945. CheckColumn(column);
  946. int record = GetDefaultRecord();
  947. return column.IsNull(record);
  948. }
  949. /// <devdoc>
  950. /// <para>[To be supplied.]</para>
  951. /// </devdoc>
  952. public bool IsNull(DataColumn column, DataRowVersion version) {
  953. CheckColumn(column);
  954. int record = GetRecordFromVersion(version);
  955. return column.IsNull(record);
  956. }
  957. /// <devdoc>
  958. /// <para>
  959. /// Rejects all changes made to the row since <see cref='System.Data.DataRow.AcceptChanges'/>
  960. /// was last called.
  961. /// </para>
  962. /// </devdoc>
  963. public void RejectChanges() {
  964. IntPtr hscp;
  965. Bid.ScopeEnter(out hscp, "<ds.DataRow.RejectChanges|API> %d#\n", ObjectID);
  966. try {
  967. if (this.RowState != DataRowState.Detached) {
  968. if (_columns.ColumnsImplementingIChangeTrackingCount != _columns.ColumnsImplementingIRevertibleChangeTrackingCount) {
  969. foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
  970. if (!dc.ImplementsIRevertibleChangeTracking) {
  971. object value = null;
  972. if (this.RowState != DataRowState.Deleted)
  973. value = this[dc];
  974. else
  975. value = this[dc, DataRowVersion.Original];
  976. if (DBNull.Value != value){
  977. if (((IChangeTracking)value).IsChanged) {
  978. throw ExceptionBuilder.UDTImplementsIChangeTrackingButnotIRevertible(dc.DataType.AssemblyQualifiedName);
  979. }
  980. }
  981. }
  982. }
  983. }
  984. foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
  985. object value = null;
  986. if (this.RowState != DataRowState.Deleted)
  987. value = this[dc];
  988. else
  989. value = this[dc, DataRowVersion.Original];
  990. if (DBNull.Value != value) {
  991. IChangeTracking tracking = (IChangeTracking)value;
  992. if (tracking.IsChanged) {
  993. ((IRevertibleChangeTracking)value).RejectChanges();
  994. }
  995. }
  996. }
  997. }
  998. _table.RollbackRow(this);
  999. }
  1000. finally {
  1001. Bid.ScopeLeave(ref hscp);
  1002. }
  1003. }
  1004. internal void ResetLastChangedColumn() {
  1005. _lastChangedColumn = null;
  1006. _countColumnChange = 0;
  1007. }
  1008. internal void SetKeyValues(DataKey key, object[] keyValues) {
  1009. bool fFirstCall = true;
  1010. bool immediate = (tempRecord == -1);
  1011. for (int i = 0; i < keyValues.Length; i++) {
  1012. object value = this[key.ColumnsReference[i]];
  1013. if (!value.Equals(keyValues[i])) {
  1014. if (immediate && fFirstCall) {
  1015. fFirstCall = false;
  1016. BeginEditInternal();
  1017. }
  1018. this[key.ColumnsReference[i]] = keyValues[i];
  1019. }
  1020. }
  1021. if (!fFirstCall)
  1022. EndEdit();
  1023. }
  1024. /// <devdoc>
  1025. /// <para>
  1026. /// Sets the specified column's value to a null value.
  1027. /// </para>
  1028. /// </devdoc>
  1029. protected void SetNull(DataColumn column) {
  1030. this[column] = DBNull.Value;
  1031. }
  1032. internal void SetNestedParentRow(DataRow parentRow, bool setNonNested) {
  1033. if (parentRow == null) {
  1034. SetParentRowToDBNull();
  1035. return;
  1036. }
  1037. foreach (DataRelation relation in _table.ParentRelations) {
  1038. if (relation.Nested || setNonNested) {
  1039. if (relation.ParentKey.Table == parentRow._table) {
  1040. object[] parentKeyValues = parentRow.GetKeyValues(relation.ParentKey);
  1041. this.SetKeyValues(relation.ChildKey, parentKeyValues);
  1042. if (relation.Nested) {
  1043. if (parentRow._table == _table)
  1044. this.CheckForLoops(relation);
  1045. else
  1046. this.GetParentRow(relation);
  1047. }
  1048. }
  1049. }
  1050. }
  1051. }
  1052. /// <devdoc>
  1053. /// <para>[To be supplied.]</para>
  1054. /// </devdoc>
  1055. public void SetParentRow(DataRow parentRow) {
  1056. SetNestedParentRow(parentRow, true);
  1057. }
  1058. /// <devdoc>
  1059. /// <para>
  1060. /// Sets current row's parent row with specified relation.
  1061. /// </para>
  1062. /// </devdoc>
  1063. public void SetParentRow(DataRow parentRow, DataRelation relation) {
  1064. if (relation == null) {
  1065. SetParentRow(parentRow);
  1066. return;
  1067. }
  1068. if (parentRow == null) {
  1069. SetParentRowToDBNull(relation);
  1070. return;
  1071. }
  1072. //if (-1 == rowID)
  1073. // throw ExceptionBuilder.ChildRowNotInTheTable();
  1074. //if (-1 == parentRow.rowID)
  1075. // throw ExceptionBuilder.ParentRowNotInTheTable();
  1076. if (_table.DataSet != parentRow._table.DataSet)
  1077. throw ExceptionBuilder.ParentRowNotInTheDataSet();
  1078. if (relation.ChildKey.Table != _table)
  1079. throw ExceptionBuilder.SetParentRowTableMismatch(relation.ChildKey.Table.TableName, _table.TableName);
  1080. if (relation.ParentKey.Table != parentRow._table)
  1081. throw ExceptionBuilder.SetParentRowTableMismatch(relation.ParentKey.Table.TableName, parentRow._table.TableName);
  1082. object[] parentKeyValues = parentRow.GetKeyValues(relation.ParentKey);
  1083. this.SetKeyValues(relation.ChildKey, parentKeyValues);
  1084. }
  1085. internal void SetParentRowToDBNull() {
  1086. //if (-1 == rowID)
  1087. // throw ExceptionBuilder.ChildRowNotInTheTable();
  1088. foreach (DataRelation relation in _table.ParentRelations)
  1089. SetParentRowToDBNull(relation);
  1090. }
  1091. internal void SetParentRowToDBNull(DataRelation relation) {
  1092. Debug.Assert(relation != null, "The relation should not be null here.");
  1093. //if (-1 == rowID)
  1094. // throw ExceptionBuilder.ChildRowNotInTheTable();
  1095. if (relation.ChildKey.Table != _table)
  1096. throw ExceptionBuilder.SetParentRowTableMismatch(relation.ChildKey.Table.TableName, _table.TableName);
  1097. object[] parentKeyValues = new object[1];
  1098. parentKeyValues[0] = DBNull.Value;
  1099. this.SetKeyValues(relation.ChildKey, parentKeyValues);
  1100. }
  1101. public void SetAdded(){
  1102. if (this.RowState == DataRowState.Unchanged) {
  1103. _table.SetOldRecord(this, -1);
  1104. }
  1105. else {
  1106. throw ExceptionBuilder.SetAddedAndModifiedCalledOnnonUnchanged();
  1107. }
  1108. }
  1109. public void SetModified(){
  1110. if (this.RowState == DataRowState.Unchanged) {
  1111. tempRecord = _table.NewRecord(newRecord);
  1112. if (tempRecord != -1) {
  1113. // suppressing the ensure property changed because no values have changed
  1114. _table.SetNewRecord(this, tempRecord, suppressEnsurePropertyChanged: true);
  1115. }
  1116. }
  1117. else {
  1118. throw ExceptionBuilder.SetAddedAndModifiedCalledOnnonUnchanged();
  1119. }
  1120. }
  1121. /*
  1122. RecordList contains the empty column storage needed. We need to copy the existing record values into this storage.
  1123. */
  1124. internal int CopyValuesIntoStore(ArrayList storeList, ArrayList nullbitList, int storeIndex) {
  1125. int recordCount = 0;
  1126. if (oldRecord != -1) {//Copy original record for the row in Unchanged, Modified, Deleted state.
  1127. for (int i = 0; i < _columns.Count; i++) {
  1128. _columns[i].CopyValueIntoStore(oldRecord, storeList[i], (BitArray) nullbitList[i], storeIndex);
  1129. }
  1130. recordCount++;
  1131. storeIndex++;
  1132. }
  1133. DataRowState state = RowState;
  1134. if ((DataRowState.Added == state) || (DataRowState.Modified == state)) { //Copy current record for the row in Added, Modified state.
  1135. for (int i = 0; i < _columns.Count; i++) {
  1136. _columns[i].CopyValueIntoStore(newRecord, storeList[i], (BitArray) nullbitList[i], storeIndex);
  1137. }
  1138. recordCount++;
  1139. storeIndex++;
  1140. }
  1141. if (-1 != tempRecord) {//Copy temp record for the row in edit mode.
  1142. for (int i = 0; i < _columns.Count; i++) {
  1143. _columns[i].CopyValueIntoStore(tempRecord, storeList[i], (BitArray)nullbitList[i], storeIndex);
  1144. }
  1145. recordCount++;
  1146. storeIndex++;
  1147. }
  1148. return recordCount;
  1149. }
  1150. [Conditional("DEBUG")]
  1151. private void VerifyValueFromStorage(DataColumn column, DataRowVersion version, object valueFromStorage) {
  1152. // Dev11 900390: ignore deleted rows by adding "newRecord != -1" condition - we do not evaluate computed rows if they are deleted
  1153. if (column.DataExpression != null && !inChangingEvent && tempRecord == -1 && newRecord != -1)
  1154. {
  1155. // for unchanged rows, check current if original is asked for.
  1156. // this is because by design, there is only single storage for an unchanged row.
  1157. if (version == DataRowVersion.Original && oldRecord == newRecord) {
  1158. version = DataRowVersion.Current;
  1159. }
  1160. // There are various known issues detected by this assert for non-default versions,
  1161. // for example DevDiv2 bug 73753
  1162. // Since changes consitutute breaking change (either way customer will get another result),
  1163. // we decided not to fix them in Dev 11
  1164. Debug.Assert(valueFromStorage.Equals(column.DataExpression.Evaluate(this, version)),
  1165. "Value from storage does lazily computed expression value");
  1166. }
  1167. }
  1168. }
  1169. public sealed class DataRowBuilder {
  1170. internal readonly DataTable _table;
  1171. internal int _record;
  1172. internal DataRowBuilder(DataTable table, int record) {
  1173. _table = table;
  1174. _record = record;
  1175. }
  1176. }
  1177. }