DataRow.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  1. //
  2. // System.Data.DataRow.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya <[email protected]>
  6. // Daniel Morgan <[email protected]>
  7. // Tim Coleman <[email protected]>
  8. // Ville Palo <[email protected]>
  9. // Alan Tam Siu Lung <[email protected]>
  10. //
  11. // (C) Ximian, Inc 2002
  12. // (C) Daniel Morgan 2002, 2003
  13. // Copyright (C) 2002 Tim Coleman
  14. //
  15. using System;
  16. using System.Collections;
  17. using System.Globalization;
  18. namespace System.Data {
  19. /// <summary>
  20. /// Represents a row of data in a DataTable.
  21. /// </summary>
  22. [Serializable]
  23. public class DataRow
  24. {
  25. #region Fields
  26. private DataTable _table;
  27. private object[] original;
  28. private object[] proposed;
  29. private object[] current;
  30. private string[] columnErrors;
  31. private string rowError;
  32. private DataRowState rowState;
  33. internal int xmlRowID = 0;
  34. internal bool _nullConstraintViolation;
  35. private bool editing = false;
  36. private bool _hasParentCollection;
  37. #endregion
  38. #region Constructors
  39. /// <summary>
  40. /// This member supports the .NET Framework infrastructure and is not intended to be
  41. /// used directly from your code.
  42. /// </summary>
  43. protected internal DataRow (DataRowBuilder builder)
  44. {
  45. _table = builder.Table;
  46. original = null;
  47. proposed = new object[_table.Columns.Count];
  48. for (int c = 0; c < _table.Columns.Count; c++)
  49. {
  50. proposed[c] = DBNull.Value;
  51. }
  52. columnErrors = new string[_table.Columns.Count];
  53. rowError = String.Empty;
  54. //on first creating a DataRow it is always detached.
  55. rowState = DataRowState.Detached;
  56. foreach (DataColumn Col in _table.Columns) {
  57. if (Col.AutoIncrement) {
  58. this [Col] = Col.AutoIncrementValue();
  59. }
  60. }
  61. _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
  62. }
  63. #endregion
  64. #region Properties
  65. /// <summary>
  66. /// Gets a value indicating whether there are errors in a row.
  67. /// </summary>
  68. public bool HasErrors {
  69. [MonoTODO]
  70. get {
  71. if (RowError != string.Empty)
  72. return true;
  73. for (int i= 0; i < columnErrors.Length; i++){
  74. if (columnErrors[i] != null && columnErrors[i] != string.Empty)
  75. return true;
  76. }
  77. return false;
  78. }
  79. }
  80. /// <summary>
  81. /// Gets or sets the data stored in the column specified by name.
  82. /// </summary>
  83. public object this[string columnName] {
  84. get { return this[columnName, DataRowVersion.Default]; }
  85. set {
  86. int columnIndex = _table.Columns.IndexOf (columnName);
  87. if (columnIndex == -1)
  88. throw new IndexOutOfRangeException ();
  89. this[columnIndex] = value;
  90. }
  91. }
  92. /// <summary>
  93. /// Gets or sets the data stored in specified DataColumn
  94. /// </summary>
  95. public object this[DataColumn column] {
  96. get {
  97. return this[column, DataRowVersion.Default];}
  98. set {
  99. int columnIndex = _table.Columns.IndexOf (column);
  100. if (columnIndex == -1)
  101. throw new ArgumentException ("The column does not belong to this table.");
  102. this[columnIndex] = value;
  103. }
  104. }
  105. /// <summary>
  106. /// Gets or sets the data stored in column specified by index.
  107. /// </summary>
  108. public object this[int columnIndex] {
  109. get { return this[columnIndex, DataRowVersion.Default]; }
  110. set {
  111. if (columnIndex < 0 || columnIndex > _table.Columns.Count)
  112. throw new IndexOutOfRangeException ();
  113. if (rowState == DataRowState.Deleted)
  114. throw new DeletedRowInaccessibleException ();
  115. DataColumn column = _table.Columns[columnIndex];
  116. _table.ChangingDataColumn (this, column, value);
  117. bool orginalEditing = editing;
  118. if (!orginalEditing) BeginEdit ();
  119. object v = SetColumnValue (value, columnIndex);
  120. proposed[columnIndex] = v;
  121. _table.ChangedDataColumn (this, column, v);
  122. if (!orginalEditing) EndEdit ();
  123. }
  124. }
  125. /// <summary>
  126. /// Gets the specified version of data stored in the named column.
  127. /// </summary>
  128. public object this[string columnName, DataRowVersion version] {
  129. get {
  130. int columnIndex = _table.Columns.IndexOf (columnName);
  131. if (columnIndex == -1)
  132. throw new IndexOutOfRangeException ();
  133. return this[columnIndex, version];
  134. }
  135. }
  136. /// <summary>
  137. /// Gets the specified version of data stored in the specified DataColumn.
  138. /// </summary>
  139. public object this[DataColumn column, DataRowVersion version] {
  140. get {
  141. int columnIndex = _table.Columns.IndexOf (column);
  142. if (columnIndex == -1)
  143. throw new ArgumentException ("The column does not belong to this table.");
  144. return this[columnIndex, version];
  145. }
  146. }
  147. /// <summary>
  148. /// Gets the data stored in the column, specified by index and version of the data to
  149. /// retrieve.
  150. /// </summary>
  151. public object this[int columnIndex, DataRowVersion version] {
  152. get {
  153. if (columnIndex < 0 || columnIndex > _table.Columns.Count)
  154. throw new IndexOutOfRangeException ();
  155. // Non-existent version
  156. if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
  157. throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
  158. // Accessing deleted rows
  159. if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
  160. throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
  161. switch (version) {
  162. case DataRowVersion.Default:
  163. if (editing || rowState == DataRowState.Detached)
  164. return proposed[columnIndex];
  165. return current[columnIndex];
  166. case DataRowVersion.Proposed:
  167. return proposed[columnIndex];
  168. case DataRowVersion.Current:
  169. return current[columnIndex];
  170. case DataRowVersion.Original:
  171. return original[columnIndex];
  172. default:
  173. throw new ArgumentException ();
  174. }
  175. }
  176. }
  177. /// <summary>
  178. /// Gets or sets all of the values for this row through an array.
  179. /// </summary>
  180. [MonoTODO]
  181. public object[] ItemArray {
  182. get {
  183. return current;
  184. }
  185. set {
  186. if (value.Length > _table.Columns.Count)
  187. throw new ArgumentException ();
  188. if (rowState == DataRowState.Deleted)
  189. throw new DeletedRowInaccessibleException ();
  190. object[] newItems = new object[_table.Columns.Count];
  191. object v = null;
  192. for (int i = 0; i < _table.Columns.Count; i++) {
  193. if (i < value.Length)
  194. v = value[i];
  195. else
  196. v = null;
  197. newItems[i] = SetColumnValue (v, i);
  198. }
  199. bool orginalEditing = editing;
  200. if (!orginalEditing) BeginEdit ();
  201. proposed = newItems;
  202. if (!orginalEditing) EndEdit ();
  203. }
  204. }
  205. private object SetColumnValue (object v, int index)
  206. {
  207. object newval = null;
  208. DataColumn col = _table.Columns[index];
  209. if (_hasParentCollection && col.ReadOnly && v != this[index])
  210. throw new ReadOnlyException ();
  211. if (v == null)
  212. {
  213. if(col.DefaultValue != DBNull.Value)
  214. {
  215. newval = col.DefaultValue;
  216. }
  217. else if(col.AutoIncrement == true)
  218. {
  219. newval = this [index];
  220. }
  221. else
  222. {
  223. if (!col.AllowDBNull)
  224. {
  225. if (!this._table._duringDataLoad)
  226. {
  227. throw new NoNullAllowedException ();
  228. }
  229. else
  230. {
  231. //Constraint violations during data load is raise in DataTable EndLoad
  232. this._nullConstraintViolation = true;
  233. }
  234. }
  235. newval= DBNull.Value;
  236. }
  237. }
  238. else if (v == DBNull.Value)
  239. {
  240. if (!col.AllowDBNull)
  241. {
  242. if (!this._table._duringDataLoad)
  243. {
  244. throw new NoNullAllowedException ();
  245. }
  246. else
  247. {
  248. //Constraint violations during data load is raise in DataTable EndLoad
  249. this._nullConstraintViolation = true;
  250. }
  251. }
  252. newval= DBNull.Value;
  253. }
  254. else
  255. {
  256. Type vType = v.GetType(); // data type of value
  257. Type cType = col.DataType; // column data type
  258. if (cType != vType)
  259. {
  260. TypeCode typeCode = Type.GetTypeCode(cType);
  261. switch(typeCode) {
  262. case TypeCode.Boolean :
  263. v = Convert.ToBoolean (v);
  264. break;
  265. case TypeCode.Byte :
  266. v = Convert.ToByte (v);
  267. break;
  268. case TypeCode.Char :
  269. v = Convert.ToChar (v);
  270. break;
  271. case TypeCode.DateTime :
  272. v = Convert.ToDateTime (v);
  273. break;
  274. case TypeCode.Decimal :
  275. v = Convert.ToDecimal (v);
  276. break;
  277. case TypeCode.Double :
  278. v = Convert.ToDouble (v);
  279. break;
  280. case TypeCode.Int16 :
  281. v = Convert.ToInt16 (v);
  282. break;
  283. case TypeCode.Int32 :
  284. v = Convert.ToInt32 (v);
  285. break;
  286. case TypeCode.Int64 :
  287. v = Convert.ToInt64 (v);
  288. break;
  289. case TypeCode.SByte :
  290. v = Convert.ToSByte (v);
  291. break;
  292. case TypeCode.Single :
  293. v = Convert.ToSingle (v);
  294. break;
  295. case TypeCode.String :
  296. v = Convert.ToString (v);
  297. break;
  298. case TypeCode.UInt16 :
  299. v = Convert.ToUInt16 (v);
  300. break;
  301. case TypeCode.UInt32 :
  302. v = Convert.ToUInt32 (v);
  303. break;
  304. case TypeCode.UInt64 :
  305. v = Convert.ToUInt64 (v);
  306. break;
  307. default :
  308. switch(cType.ToString()) {
  309. case "System.TimeSpan" :
  310. v = (System.TimeSpan) v;
  311. break;
  312. case "System.Type" :
  313. v = (System.Type) v;
  314. break;
  315. case "System.Object" :
  316. //v = (System.Object) v;
  317. break;
  318. default:
  319. // FIXME: is exception correct?
  320. throw new InvalidCastException("Type not supported.");
  321. }
  322. break;
  323. }
  324. vType = v.GetType();
  325. }
  326. newval = v;
  327. if(col.AutoIncrement == true) {
  328. long inc = Convert.ToInt64(v);
  329. col.UpdateAutoIncrementValue (inc);
  330. }
  331. }
  332. col.DataHasBeenSet = true;
  333. return newval;
  334. }
  335. /// <summary>
  336. /// Gets or sets the custom error description for a row.
  337. /// </summary>
  338. public string RowError {
  339. get { return rowError; }
  340. set { rowError = value; }
  341. }
  342. /// <summary>
  343. /// Gets the current state of the row in regards to its relationship to the
  344. /// DataRowCollection.
  345. /// </summary>
  346. public DataRowState RowState {
  347. get { return rowState; }
  348. }
  349. //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
  350. //to a Datatable so I added this method. Delete if there is a better way.
  351. internal void AttachRow() {
  352. current = proposed;
  353. proposed = null;
  354. rowState = DataRowState.Added;
  355. }
  356. //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
  357. //from a Datatable so I added this method. Delete if there is a better way.
  358. internal void DetachRow() {
  359. proposed = null;
  360. _hasParentCollection = false;
  361. rowState = DataRowState.Detached;
  362. }
  363. /// <summary>
  364. /// Gets the DataTable for which this row has a schema.
  365. /// </summary>
  366. public DataTable Table {
  367. get { return _table; }
  368. }
  369. /// <summary>
  370. /// Gets and sets index of row. This is used from
  371. /// XmlDataDocument.
  372. // </summary>
  373. internal int XmlRowID {
  374. get { return xmlRowID; }
  375. set { xmlRowID = value; }
  376. }
  377. #endregion
  378. #region Methods
  379. /// <summary>
  380. /// Commits all the changes made to this row since the last time AcceptChanges was
  381. /// called.
  382. /// </summary>
  383. public void AcceptChanges ()
  384. {
  385. EndEdit(); // in case it hasn't been called
  386. switch (rowState) {
  387. case DataRowState.Added:
  388. case DataRowState.Modified:
  389. rowState = DataRowState.Unchanged;
  390. break;
  391. case DataRowState.Deleted:
  392. _table.Rows.Remove (this);
  393. break;
  394. case DataRowState.Detached:
  395. throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
  396. }
  397. // Accept from detached
  398. if (original == null)
  399. original = new object[_table.Columns.Count];
  400. Array.Copy (current, original, _table.Columns.Count);
  401. }
  402. /// <summary>
  403. /// Begins an edit operation on a DataRow object.
  404. /// </summary>
  405. [MonoTODO]
  406. public void BeginEdit ()
  407. {
  408. if (rowState == DataRowState.Deleted)
  409. throw new DeletedRowInaccessibleException ();
  410. if (!HasVersion (DataRowVersion.Proposed)) {
  411. proposed = new object[_table.Columns.Count];
  412. Array.Copy (current, proposed, current.Length);
  413. }
  414. //TODO: Suspend validation
  415. editing = true;
  416. }
  417. /// <summary>
  418. /// Cancels the current edit on the row.
  419. /// </summary>
  420. [MonoTODO]
  421. public void CancelEdit ()
  422. {
  423. editing = false;
  424. //TODO: Events
  425. if (HasVersion (DataRowVersion.Proposed)) {
  426. proposed = null;
  427. if (rowState == DataRowState.Modified)
  428. rowState = DataRowState.Unchanged;
  429. }
  430. }
  431. /// <summary>
  432. /// Clears the errors for the row, including the RowError and errors set with
  433. /// SetColumnError.
  434. /// </summary>
  435. public void ClearErrors ()
  436. {
  437. rowError = String.Empty;
  438. columnErrors = new String[_table.Columns.Count];
  439. }
  440. /// <summary>
  441. /// Deletes the DataRow.
  442. /// </summary>
  443. [MonoTODO]
  444. public void Delete ()
  445. {
  446. _table.DeletingDataRow(this, DataRowAction.Delete);
  447. switch (rowState) {
  448. case DataRowState.Added:
  449. Table.Rows.Remove (this);
  450. break;
  451. case DataRowState.Deleted:
  452. throw new DeletedRowInaccessibleException ();
  453. default:
  454. // check what to do with child rows
  455. CheckChildRows(DataRowAction.Delete);
  456. rowState = DataRowState.Deleted;
  457. break;
  458. }
  459. _table.DeletedDataRow(this, DataRowAction.Delete);
  460. }
  461. // check the child rows of this row before deleting the row.
  462. private void CheckChildRows(DataRowAction action)
  463. {
  464. // in this method we find the row that this row is in a reltion with them.
  465. // in shortly we find all child rows of this row.
  466. // then we function according to the DeleteRule of the foriegnkey.
  467. // 1. find if this row is attached to dataset.
  468. // 2. find if EnforceConstraints is true.
  469. // 3. find if there are any constraint on the table that the row is in.
  470. if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
  471. {
  472. foreach (DataTable table in _table.DataSet.Tables)
  473. {
  474. // loop on all constraints of the table.
  475. ConstraintCollection constraintsCollection = table.Constraints;
  476. for (int i = 0; i < constraintsCollection.Count; i++)
  477. {
  478. ForeignKeyConstraint fk = null;
  479. if (constraintsCollection[i] is ForeignKeyConstraint)
  480. {
  481. fk = (ForeignKeyConstraint)constraintsCollection[i];
  482. if (fk.RelatedTable == _table)
  483. {
  484. //we create a dummy relation because we do not want to duplicate code of GetChild().
  485. // we use the dummy relation to find child rows.
  486. DataRelation rel = new DataRelation("dummy", fk.RelatedColumns, fk.Columns, false);
  487. Rule rule;
  488. if (action == DataRowAction.Delete)
  489. rule = fk.DeleteRule;
  490. else
  491. rule = fk.UpdateRule;
  492. CheckChildRows(rel, action, rule);
  493. }
  494. }
  495. }
  496. }
  497. }
  498. }
  499. private void CheckChildRows(DataRelation rel, DataRowAction action, Rule rule)
  500. {
  501. DataRow[] childRows = GetChildRows(rel);
  502. switch (rule)
  503. {
  504. case Rule.Cascade: // delete or change all relted rows.
  505. if (childRows != null)
  506. {
  507. for (int j = 0; j < childRows.Length; j++)
  508. {
  509. // if action is delete we delete all child rows
  510. if (action == DataRowAction.Delete)
  511. {
  512. if (childRows[j].RowState != DataRowState.Deleted)
  513. childRows[j].Delete();
  514. }
  515. // if action is change we change the values in the child row
  516. else if (action == DataRowAction.Change)
  517. {
  518. // change only the values in the key columns
  519. // set the childcolumn value to the new parent row value
  520. for (int k = 0; k < rel.ChildColumns.Length; k++)
  521. childRows[j][rel.ChildColumns[k]] = this[rel.ParentColumns[k], DataRowVersion.Proposed];
  522. }
  523. }
  524. }
  525. break;
  526. case Rule.None: // throw an exception if there are any child rows.
  527. if (childRows != null)
  528. {
  529. for (int j = 0; j < childRows.Length; j++)
  530. {
  531. if (childRows[j].RowState != DataRowState.Deleted)
  532. {
  533. string changeStr = "Cannot change this row because constraints are enforced on relation " + rel.RelationName +", and changing this row will strand child rows.";
  534. string delStr = "Cannot delete this row because constraints are enforced on relation " + rel.RelationName +", and deleting this row will strand child rows.";
  535. string message = action == DataRowAction.Delete ? delStr : changeStr;
  536. throw new InvalidConstraintException(message);
  537. }
  538. }
  539. }
  540. break;
  541. case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
  542. if (childRows != null)
  543. {
  544. for (int j = 0; j < childRows.Length; j++)
  545. {
  546. DataRow child = childRows[j];
  547. if (childRows[j].RowState != DataRowState.Deleted)
  548. {
  549. //set only the key columns to default
  550. for (int k = 0; k < rel.ChildColumns.Length; k++)
  551. child[rel.ChildColumns[k]] = rel.ChildColumns[k].DefaultValue;
  552. }
  553. }
  554. }
  555. break;
  556. case Rule.SetNull: // set the values in the child row to null.
  557. if (childRows != null)
  558. {
  559. for (int j = 0; j < childRows.Length; j++)
  560. {
  561. DataRow child = childRows[j];
  562. if (childRows[j].RowState != DataRowState.Deleted)
  563. {
  564. // set only the key columns to DBNull
  565. for (int k = 0; k < rel.ChildColumns.Length; k++)
  566. child.SetNull(rel.ChildColumns[k]);
  567. }
  568. }
  569. }
  570. break;
  571. }
  572. }
  573. /// <summary>
  574. /// Ends the edit occurring on the row.
  575. /// </summary>
  576. [MonoTODO]
  577. public void EndEdit ()
  578. {
  579. editing = false;
  580. if (rowState == DataRowState.Detached)
  581. return;
  582. if (HasVersion (DataRowVersion.Proposed))
  583. {
  584. _table.ChangingDataRow(this, DataRowAction.Change);
  585. if (rowState == DataRowState.Unchanged)
  586. rowState = DataRowState.Modified;
  587. //Calling next method validates UniqueConstraints
  588. //and ForeignKeys.
  589. try
  590. {
  591. if (_table.DataSet == null || _table.DataSet.EnforceConstraints)
  592. _table.Rows.ValidateDataRowInternal(this);
  593. }
  594. catch (Exception e)
  595. {
  596. proposed = null;
  597. throw e;
  598. }
  599. // check all child rows.
  600. CheckChildRows(DataRowAction.Change);
  601. current = proposed;
  602. proposed = null;
  603. _table.ChangedDataRow(this, DataRowAction.Change);
  604. }
  605. }
  606. /// <summary>
  607. /// Gets the child rows of this DataRow using the specified DataRelation.
  608. /// </summary>
  609. public DataRow[] GetChildRows (DataRelation relation)
  610. {
  611. return GetChildRows (relation, DataRowVersion.Current);
  612. }
  613. /// <summary>
  614. /// Gets the child rows of a DataRow using the specified RelationName of a
  615. /// DataRelation.
  616. /// </summary>
  617. public DataRow[] GetChildRows (string relationName)
  618. {
  619. return GetChildRows (Table.DataSet.Relations[relationName]);
  620. }
  621. /// <summary>
  622. /// Gets the child rows of a DataRow using the specified DataRelation, and
  623. /// DataRowVersion.
  624. /// </summary>
  625. public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version)
  626. {
  627. if (relation == null)
  628. return new DataRow[0];
  629. if (this.Table == null)
  630. throw new RowNotInTableException();
  631. if (relation.DataSet != this.Table.DataSet)
  632. throw new ArgumentException();
  633. // TODO: Caching for better preformance
  634. ArrayList rows = new ArrayList();
  635. DataColumn[] parentColumns = relation.ParentColumns;
  636. DataColumn[] childColumns = relation.ChildColumns;
  637. int numColumn = parentColumns.Length;
  638. if (HasVersion(version))
  639. {
  640. foreach (DataRow row in relation.ChildTable.Rows)
  641. {
  642. bool allColumnsMatch = false;
  643. if (row.HasVersion(DataRowVersion.Default))
  644. {
  645. allColumnsMatch = true;
  646. for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
  647. {
  648. if (!this[parentColumns[columnCnt], version].Equals(
  649. row[childColumns[columnCnt], DataRowVersion.Default]))
  650. {
  651. allColumnsMatch = false;
  652. break;
  653. }
  654. }
  655. }
  656. if (allColumnsMatch) rows.Add(row);
  657. }
  658. }
  659. return rows.ToArray(typeof(DataRow)) as DataRow[];
  660. }
  661. /// <summary>
  662. /// Gets the child rows of a DataRow using the specified RelationName of a
  663. /// DataRelation, and DataRowVersion.
  664. /// </summary>
  665. public DataRow[] GetChildRows (string relationName, DataRowVersion version)
  666. {
  667. return GetChildRows (Table.DataSet.Relations[relationName], version);
  668. }
  669. /// <summary>
  670. /// Gets the error description of the specified DataColumn.
  671. /// </summary>
  672. public string GetColumnError (DataColumn column)
  673. {
  674. return GetColumnError (_table.Columns.IndexOf(column));
  675. }
  676. /// <summary>
  677. /// Gets the error description for the column specified by index.
  678. /// </summary>
  679. public string GetColumnError (int columnIndex)
  680. {
  681. if (columnIndex < 0 || columnIndex >= columnErrors.Length)
  682. throw new IndexOutOfRangeException ();
  683. string retVal = columnErrors[columnIndex];
  684. if (retVal == null)
  685. retVal = string.Empty;
  686. return retVal;
  687. }
  688. /// <summary>
  689. /// Gets the error description for the column, specified by name.
  690. /// </summary>
  691. public string GetColumnError (string columnName)
  692. {
  693. return GetColumnError (_table.Columns.IndexOf(columnName));
  694. }
  695. /// <summary>
  696. /// Gets an array of columns that have errors.
  697. /// </summary>
  698. public DataColumn[] GetColumnsInError ()
  699. {
  700. ArrayList dataColumns = new ArrayList ();
  701. for (int i = 0; i < columnErrors.Length; i += 1)
  702. {
  703. if (columnErrors[i] != null && columnErrors[i] != String.Empty)
  704. dataColumns.Add (_table.Columns[i]);
  705. }
  706. return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
  707. }
  708. /// <summary>
  709. /// Gets the parent row of a DataRow using the specified DataRelation.
  710. /// </summary>
  711. public DataRow GetParentRow (DataRelation relation)
  712. {
  713. return GetParentRow (relation, DataRowVersion.Current);
  714. }
  715. /// <summary>
  716. /// Gets the parent row of a DataRow using the specified RelationName of a
  717. /// DataRelation.
  718. /// </summary>
  719. public DataRow GetParentRow (string relationName)
  720. {
  721. return GetParentRow (relationName, DataRowVersion.Current);
  722. }
  723. /// <summary>
  724. /// Gets the parent row of a DataRow using the specified DataRelation, and
  725. /// DataRowVersion.
  726. /// </summary>
  727. public DataRow GetParentRow (DataRelation relation, DataRowVersion version)
  728. {
  729. DataRow[] rows = GetParentRows(relation, version);
  730. if (rows.Length == 0) return null;
  731. return rows[0];
  732. }
  733. /// <summary>
  734. /// Gets the parent row of a DataRow using the specified RelationName of a
  735. /// DataRelation, and DataRowVersion.
  736. /// </summary>
  737. public DataRow GetParentRow (string relationName, DataRowVersion version)
  738. {
  739. return GetParentRow (Table.DataSet.Relations[relationName], version);
  740. }
  741. /// <summary>
  742. /// Gets the parent rows of a DataRow using the specified DataRelation.
  743. /// </summary>
  744. public DataRow[] GetParentRows (DataRelation relation)
  745. {
  746. return GetParentRows (relation, DataRowVersion.Current);
  747. }
  748. /// <summary>
  749. /// Gets the parent rows of a DataRow using the specified RelationName of a
  750. /// DataRelation.
  751. /// </summary>
  752. public DataRow[] GetParentRows (string relationName)
  753. {
  754. return GetParentRows (relationName, DataRowVersion.Current);
  755. }
  756. /// <summary>
  757. /// Gets the parent rows of a DataRow using the specified DataRelation, and
  758. /// DataRowVersion.
  759. /// </summary>
  760. public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version)
  761. {
  762. // TODO: Caching for better preformance
  763. if (relation == null)
  764. return new DataRow[0];
  765. if (this.Table == null)
  766. throw new RowNotInTableException();
  767. if (relation.DataSet != this.Table.DataSet)
  768. throw new ArgumentException();
  769. ArrayList rows = new ArrayList();
  770. DataColumn[] parentColumns = relation.ParentColumns;
  771. DataColumn[] childColumns = relation.ChildColumns;
  772. int numColumn = parentColumns.Length;
  773. if (HasVersion(version))
  774. {
  775. foreach (DataRow row in relation.ParentTable.Rows)
  776. {
  777. bool allColumnsMatch = false;
  778. if (row.HasVersion(DataRowVersion.Default))
  779. {
  780. allColumnsMatch = true;
  781. for (int columnCnt = 0; columnCnt < numColumn; columnCnt++)
  782. {
  783. if (!this[childColumns[columnCnt], version].Equals(
  784. row[parentColumns[columnCnt], DataRowVersion.Default]))
  785. {
  786. allColumnsMatch = false;
  787. break;
  788. }
  789. }
  790. }
  791. if (allColumnsMatch) rows.Add(row);
  792. }
  793. }
  794. return rows.ToArray(typeof(DataRow)) as DataRow[];
  795. }
  796. /// <summary>
  797. /// Gets the parent rows of a DataRow using the specified RelationName of a
  798. /// DataRelation, and DataRowVersion.
  799. /// </summary>
  800. public DataRow[] GetParentRows (string relationName, DataRowVersion version)
  801. {
  802. return GetParentRows (Table.DataSet.Relations[relationName], version);
  803. }
  804. /// <summary>
  805. /// Gets a value indicating whether a specified version exists.
  806. /// </summary>
  807. public bool HasVersion (DataRowVersion version)
  808. {
  809. switch (version)
  810. {
  811. case DataRowVersion.Default:
  812. if (rowState == DataRowState.Deleted)
  813. return false;
  814. if (rowState == DataRowState.Detached)
  815. return proposed != null;
  816. return true;
  817. case DataRowVersion.Proposed:
  818. if (rowState == DataRowState.Deleted)
  819. return false;
  820. return (proposed != null);
  821. case DataRowVersion.Current:
  822. if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
  823. return false;
  824. return (current != null);
  825. case DataRowVersion.Original:
  826. if (rowState == DataRowState.Detached)
  827. return false;
  828. return (original != null);
  829. }
  830. return false;
  831. }
  832. /// <summary>
  833. /// Gets a value indicating whether the specified DataColumn contains a null value.
  834. /// </summary>
  835. public bool IsNull (DataColumn column)
  836. {
  837. object o = this[column];
  838. return (o == null || o == DBNull.Value);
  839. }
  840. /// <summary>
  841. /// Gets a value indicating whether the column at the specified index contains a null
  842. /// value.
  843. /// </summary>
  844. public bool IsNull (int columnIndex)
  845. {
  846. object o = this[columnIndex];
  847. return (o == null || o == DBNull.Value);
  848. }
  849. /// <summary>
  850. /// Gets a value indicating whether the named column contains a null value.
  851. /// </summary>
  852. public bool IsNull (string columnName)
  853. {
  854. object o = this[columnName];
  855. return (o == null || o == DBNull.Value);
  856. }
  857. /// <summary>
  858. /// Gets a value indicating whether the specified DataColumn and DataRowVersion
  859. /// contains a null value.
  860. /// </summary>
  861. public bool IsNull (DataColumn column, DataRowVersion version)
  862. {
  863. object o = this[column, version];
  864. return (o == null || o == DBNull.Value);
  865. }
  866. /// <summary>
  867. /// Rejects all changes made to the row since AcceptChanges was last called.
  868. /// </summary>
  869. public void RejectChanges ()
  870. {
  871. // If original is null, then nothing has happened since AcceptChanges
  872. // was last called. We have no "original" to go back to.
  873. if (original != null)
  874. {
  875. Array.Copy (original, current, _table.Columns.Count);
  876. _table.ChangedDataRow (this, DataRowAction.Rollback);
  877. CancelEdit ();
  878. switch (rowState)
  879. {
  880. case DataRowState.Added:
  881. _table.Rows.Remove (this);
  882. break;
  883. case DataRowState.Modified:
  884. rowState = DataRowState.Unchanged;
  885. break;
  886. case DataRowState.Deleted:
  887. rowState = DataRowState.Unchanged;
  888. break;
  889. }
  890. }
  891. else {
  892. // If rows are just loaded via Xml the original values are null.
  893. // So in this case we have to remove all columns.
  894. // FIXME: I'm not realy sure, does this break something else, but
  895. // if so: FIXME ;)
  896. if ((rowState & DataRowState.Added) > 0)
  897. _table.Rows.Remove (this);
  898. }
  899. }
  900. /// <summary>
  901. /// Sets the error description for a column specified as a DataColumn.
  902. /// </summary>
  903. public void SetColumnError (DataColumn column, string error)
  904. {
  905. SetColumnError (_table.Columns.IndexOf (column), error);
  906. }
  907. /// <summary>
  908. /// Sets the error description for a column specified by index.
  909. /// </summary>
  910. public void SetColumnError (int columnIndex, string error)
  911. {
  912. if (columnIndex < 0 || columnIndex >= columnErrors.Length)
  913. throw new IndexOutOfRangeException ();
  914. columnErrors[columnIndex] = error;
  915. }
  916. /// <summary>
  917. /// Sets the error description for a column specified by name.
  918. /// </summary>
  919. public void SetColumnError (string columnName, string error)
  920. {
  921. SetColumnError (_table.Columns.IndexOf (columnName), error);
  922. }
  923. /// <summary>
  924. /// Sets the value of the specified DataColumn to a null value.
  925. /// </summary>
  926. protected void SetNull (DataColumn column)
  927. {
  928. this[column] = DBNull.Value;
  929. }
  930. /// <summary>
  931. /// Sets the parent row of a DataRow with specified new parent DataRow.
  932. /// </summary>
  933. [MonoTODO]
  934. public void SetParentRow (DataRow parentRow)
  935. {
  936. SetParentRow(parentRow, null);
  937. }
  938. /// <summary>
  939. /// Sets the parent row of a DataRow with specified new parent DataRow and
  940. /// DataRelation.
  941. /// </summary>
  942. [MonoTODO]
  943. public void SetParentRow (DataRow parentRow, DataRelation relation)
  944. {
  945. if (_table == null || parentRow.Table == null)
  946. throw new RowNotInTableException();
  947. if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
  948. throw new ArgumentException();
  949. BeginEdit();
  950. if (relation == null)
  951. {
  952. foreach (DataRelation parentRel in _table.ParentRelations)
  953. {
  954. DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
  955. DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
  956. for (int i = 0; i < parentCols.Length; i++)
  957. {
  958. if (parentRow == null)
  959. this[childCols[i].Ordinal] = DBNull.Value;
  960. else
  961. this[childCols[i].Ordinal] = parentRow[parentCols[i]];
  962. }
  963. }
  964. }
  965. else
  966. {
  967. DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
  968. DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
  969. for (int i = 0; i < parentCols.Length; i++)
  970. {
  971. if (parentRow == null)
  972. this[childCols[i].Ordinal] = DBNull.Value;
  973. else
  974. this[childCols[i].Ordinal] = parentRow[parentCols[i]];
  975. }
  976. }
  977. EndEdit();
  978. }
  979. //Copy all values of this DataaRow to the row parameter.
  980. internal void CopyValuesToRow(DataRow row)
  981. {
  982. if (row == null)
  983. throw new ArgumentNullException("row");
  984. if (row == this)
  985. throw new ArgumentException("'row' is the same as this object");
  986. DataColumnCollection columns = Table.Columns;
  987. for(int i = 0; i < columns.Count; i++){
  988. string columnName = columns[i].ColumnName;
  989. int index = row.Table.Columns.IndexOf(columnName);
  990. //if a column with the same name exists in both rows copy the values
  991. if(index != -1) {
  992. if (HasVersion(DataRowVersion.Original))
  993. {
  994. if (row.original == null)
  995. row.original = new object[row.Table.Columns.Count];
  996. row.original[index] = row.SetColumnValue(original[i], index);
  997. }
  998. if (HasVersion(DataRowVersion.Current))
  999. {
  1000. if (row.current == null)
  1001. row.current = new object[row.Table.Columns.Count];
  1002. row.current[index] = row.SetColumnValue(current[i], index);
  1003. }
  1004. if (HasVersion(DataRowVersion.Proposed))
  1005. {
  1006. if (row.proposed == null)
  1007. row.proposed = new object[row.Table.Columns.Count];
  1008. row.proposed[index] = row.SetColumnValue(proposed[i], index);
  1009. }
  1010. //Saving the current value as the column value
  1011. row[index] = row.current[index];
  1012. }
  1013. }
  1014. row.rowState = RowState;
  1015. row.RowError = RowError;
  1016. row.columnErrors = columnErrors;
  1017. }
  1018. public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
  1019. {
  1020. // if a column is added we hava to add an additional value the
  1021. // the priginal, current and propoed arrays.
  1022. // this scenario can happened in merge operation.
  1023. if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
  1024. {
  1025. object[] tmp;
  1026. if (current != null)
  1027. {
  1028. tmp = new object[current.Length + 1];
  1029. Array.Copy (current, tmp, current.Length);
  1030. tmp[tmp.Length - 1] = DBNull.Value;
  1031. current = tmp;
  1032. }
  1033. if (proposed != null)
  1034. {
  1035. tmp = new object[proposed.Length + 1];
  1036. Array.Copy (proposed, tmp, proposed.Length);
  1037. tmp[tmp.Length - 1] = DBNull.Value;
  1038. proposed = tmp;
  1039. }
  1040. if(original != null)
  1041. {
  1042. tmp = new object[original.Length + 1];
  1043. Array.Copy (original, tmp, original.Length);
  1044. tmp[tmp.Length - 1] = DBNull.Value;
  1045. original = tmp;
  1046. }
  1047. }
  1048. }
  1049. internal bool IsRowChanged(DataRowState rowState) {
  1050. if((RowState & rowState) != 0)
  1051. return true;
  1052. //we need to find if child rows of this row changed.
  1053. //if yes - we should return true
  1054. // if the rowState is deleted we should get the original version of the row
  1055. // else - we should get the current version of the row.
  1056. DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
  1057. int count = Table.ChildRelations.Count;
  1058. for (int i = 0; i < count; i++){
  1059. DataRelation rel = Table.ChildRelations[i];
  1060. DataRow[] childRows = GetChildRows(rel, version);
  1061. for (int j = 0; j < childRows.Length; j++){
  1062. if (childRows[j].IsRowChanged(rowState))
  1063. return true;
  1064. }
  1065. }
  1066. return false;
  1067. }
  1068. internal bool HasParentCollection
  1069. {
  1070. get
  1071. {
  1072. return _hasParentCollection;
  1073. }
  1074. set
  1075. {
  1076. _hasParentCollection = value;
  1077. }
  1078. }
  1079. #endregion // Methods
  1080. }
  1081. }