2004-03-15 Umadevi S (sumadevi@novell.com)
[mono.git] / mcs / class / System.Data / System.Data / DataRow.cs
1 //
2 // System.Data.DataRow.cs
3 //
4 // Author:
5 //   Rodrigo Moya <rodrigo@ximian.com>
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Tim Coleman <tim@timcoleman.com>
8 //   Ville Palo <vi64pa@koti.soon.fi>
9 //   Alan Tam Siu Lung <Tam@SiuLung.com>
10 //
11 // (C) Ximian, Inc 2002
12 // (C) Daniel Morgan 2002, 2003
13 // Copyright (C) 2002 Tim Coleman
14 //
15
16 using System;
17 using System.Collections;
18 using System.Globalization;
19
20 namespace System.Data {
21         /// <summary>
22         /// Represents a row of data in a DataTable.
23         /// </summary>
24         [Serializable]
25         public class DataRow
26         {
27                 #region Fields
28
29                 private DataTable _table;
30
31                 private object[] original;
32                 private object[] proposed;
33                 private object[] current;
34
35                 private string[] columnErrors;
36                 private string rowError;
37                 private DataRowState rowState;
38                 internal int xmlRowID = 0;
39                 internal bool _nullConstraintViolation;
40                 private string _nullConstraintMessage;
41                 private bool editing = false;
42                 private bool _hasParentCollection;
43                 private bool _inChangingEvent;
44                 private int _rowId;
45
46                 #endregion
47
48                 #region Constructors
49
50                 /// <summary>
51                 /// This member supports the .NET Framework infrastructure and is not intended to be 
52                 /// used directly from your code.
53                 /// </summary>
54                 protected internal DataRow (DataRowBuilder builder)
55                 {
56                         _table = builder.Table;
57                         // Get the row id from the builder.
58                         _rowId = builder._rowId;
59
60                         original = null; 
61                         
62                         proposed = new object[_table.Columns.Count];
63                         // Initialise the data coloumns of the row with the dafault values, if any 
64                         for (int c = 0; c < _table.Columns.Count; c++) 
65                         {
66                                 if(_table.Columns [c].DefaultValue == null)
67                                         proposed[c] = DBNull.Value;
68                                 else
69                                         proposed [c] = _table.Columns[c].DefaultValue;
70                         }
71                         
72                         columnErrors = new string[_table.Columns.Count];
73                         rowError = String.Empty;
74
75                         //on first creating a DataRow it is always detached.
76                         rowState = DataRowState.Detached;
77                         
78                         foreach (DataColumn Col in _table.Columns) {
79                                 
80                                 if (Col.AutoIncrement) {
81                                         this [Col] = Col.AutoIncrementValue();
82                                 }
83                         }
84                         _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
85                 }
86
87                 
88                 #endregion
89
90                 #region Properties
91
92                 /// <summary>
93                 /// Gets a value indicating whether there are errors in a row.
94                 /// </summary>
95                 public bool HasErrors {
96                         get {
97                                 if (RowError != string.Empty)
98                                         return true;
99
100                                 for (int i= 0; i < columnErrors.Length; i++){
101                                         if (columnErrors[i] != null && columnErrors[i] != string.Empty)
102                                                 return true;
103                                 }
104
105                                 return false;
106                         }
107                 }
108
109                 /// <summary>
110                 /// Gets or sets the data stored in the column specified by name.
111                 /// </summary>
112                 public object this[string columnName] {
113                         get { return this[columnName, DataRowVersion.Default]; }
114                         set {
115                                 int columnIndex = _table.Columns.IndexOf (columnName);
116                                 if (columnIndex == -1)
117                                         throw new IndexOutOfRangeException ();
118                                 this[columnIndex] = value;
119                         }
120                 }
121
122                 /// <summary>
123                 /// Gets or sets the data stored in specified DataColumn
124                 /// </summary>
125                 public object this[DataColumn column] {
126
127                         get {
128                                 return this[column, DataRowVersion.Default];} 
129                         set {
130                                 int columnIndex = _table.Columns.IndexOf (column);
131                                 if (columnIndex == -1)
132                                         throw new ArgumentException ("The column does not belong to this table.");
133                                 this[columnIndex] = value;
134                         }
135                 }
136
137                 /// <summary>
138                 /// Gets or sets the data stored in column specified by index.
139                 /// </summary>
140                 public object this[int columnIndex] {
141                         get { return this[columnIndex, DataRowVersion.Default]; }
142                         set {
143                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
144                                         throw new IndexOutOfRangeException ();
145                                 if (rowState == DataRowState.Deleted)
146                                         throw new DeletedRowInaccessibleException ();
147                                 DataColumn column = _table.Columns[columnIndex];
148                                 _table.ChangingDataColumn (this, column, value);
149                                 
150                                 
151                                 bool orginalEditing = editing;
152                                 if (!orginalEditing) BeginEdit ();
153                                 object v = SetColumnValue (value, columnIndex);
154                                 proposed[columnIndex] = v;
155                                 _table.ChangedDataColumn (this, column, v);
156                                 if (!orginalEditing) EndEdit ();
157                         }
158                 }
159
160                 /// <summary>
161                 /// Gets the specified version of data stored in the named column.
162                 /// </summary>
163                 public object this[string columnName, DataRowVersion version] {
164                         get {
165                                 int columnIndex = _table.Columns.IndexOf (columnName);
166                                 if (columnIndex == -1)
167                                         throw new IndexOutOfRangeException ();
168                                 return this[columnIndex, version];
169                         }
170                 }
171
172                 /// <summary>
173                 /// Gets the specified version of data stored in the specified DataColumn.
174                 /// </summary>
175                 public object this[DataColumn column, DataRowVersion version] {
176                         get {
177                                 if (column.Table != Table)
178                                         throw new ArgumentException ("The column does not belong to this table.");
179                                 int columnIndex = column.Ordinal;
180                                 return this[columnIndex, version];
181                         }
182                 }
183
184                 /// <summary>
185                 /// Gets the data stored in the column, specified by index and version of the data to
186                 /// retrieve.
187                 /// </summary>
188                 public object this[int columnIndex, DataRowVersion version] {
189                         get {
190                                 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
191                                         throw new IndexOutOfRangeException ();
192                                 // Accessing deleted rows
193                                 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
194                                         throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
195                                 // Row not in table
196                                 //if (rowState == DataRowState.Detached && version == DataRowVersion.Default)
197                                 //      throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
198                                 // Non-existent version
199                                 //if (!HasVersion (version))
200                                 //      throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
201                                 if (HasVersion(version))
202                                 {
203                                         switch (version) 
204                                         {
205                                                 case DataRowVersion.Default:
206                                                         if (editing || rowState == DataRowState.Detached)
207                                                                 return proposed[columnIndex];
208                                                         return current[columnIndex];
209                                                 case DataRowVersion.Proposed:
210                                                         return proposed[columnIndex];
211                                                 case DataRowVersion.Current:
212                                                         return current[columnIndex];
213                                                 case DataRowVersion.Original:
214                                                         return original[columnIndex];
215                                                 default:
216                                                         throw new ArgumentException ();
217                                         }
218                                 }
219                                 if (rowState == DataRowState.Detached && version == DataRowVersion.Default && proposed == null)
220                                         throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
221                                 
222                                 throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
223                         }
224                 }
225                 
226                 internal void SetOriginalValue (string columnName, object val)
227                 {
228                         int columnIndex = _table.Columns.IndexOf (columnName);
229                         DataColumn column = _table.Columns[columnIndex];
230                         _table.ChangingDataColumn (this, column, val);
231                                 
232                         if (original == null) original = new object [_table.Columns.Count];
233                         val = SetColumnValue (val, columnIndex);
234                         original[columnIndex] = val;
235                         rowState = DataRowState.Modified;
236                 }
237
238                 /// <summary>
239                 /// Gets or sets all of the values for this row through an array.
240                 /// </summary>
241                 public object[] ItemArray {
242                         get { 
243                                 // row not in table
244                                 if (rowState == DataRowState.Detached)
245                                         throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
246                                 // Accessing deleted rows
247                                 if (rowState == DataRowState.Deleted)
248                                         throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
249                                 
250                                 return current; 
251                         }
252                         set {
253                                 if (value.Length > _table.Columns.Count)
254                                         throw new ArgumentException ();
255
256                                 if (rowState == DataRowState.Deleted)
257                                         throw new DeletedRowInaccessibleException ();
258                                 
259                                 object[] newItems = new object[_table.Columns.Count];                   
260                                 object v = null;
261                                 for (int i = 0; i < _table.Columns.Count; i++) {
262
263                                         if (i < value.Length)
264                                                 v = value[i];
265                                         else
266                                                 v = null;
267
268                                         newItems[i] = SetColumnValue (v, i);
269                                 }
270
271                                 bool orginalEditing = editing;
272                                 if (!orginalEditing) BeginEdit ();
273                                 proposed = newItems;
274                                 if (!orginalEditing) EndEdit ();
275                         }
276                 }
277
278                 private object SetColumnValue (object v, int index) 
279                 {               
280                         object newval = null;
281                         DataColumn col = _table.Columns[index];
282                         
283                         if (_hasParentCollection && col.ReadOnly && v != this[index])
284                                 throw new ReadOnlyException ();
285
286                         if (v == null)
287                         {
288                                 if (col.DataType.ToString().Equals("System.Guid"))
289                                         throw new ArgumentException("Cannot set column to be null, Please use DBNull instead");
290
291                                 if(col.DefaultValue != DBNull.Value) 
292                                 {
293                                         newval = col.DefaultValue;
294                                 }
295                                 else if(col.AutoIncrement == true) 
296                                 {
297                                         newval = this [index];
298                                 }
299                                 else 
300                                 {
301                                         if (!col.AllowDBNull)
302                                         {
303                                                 //Constraint violations during data load is raise in DataTable EndLoad
304                                                 this._nullConstraintViolation = true;
305                                                 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
306                                         }
307
308                                         newval = DBNull.Value;
309                                 }
310                         }               
311                         else if (v == DBNull.Value) 
312                         {
313                                 
314                                 if (!col.AllowDBNull)
315                                 {
316                                         //Constraint violations during data load is raise in DataTable EndLoad
317                                         this._nullConstraintViolation = true;
318                                         _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
319                                 }
320                                 
321                                 newval = DBNull.Value;
322                         }
323                         else 
324                         {       
325                                 Type vType = v.GetType(); // data type of value
326                                 Type cType = col.DataType; // column data type
327                                 if (cType != vType) 
328                                 {
329                                         TypeCode typeCode = Type.GetTypeCode(cType);
330                                         switch(typeCode) {
331                                                 case TypeCode.Boolean :
332                                                         v = Convert.ToBoolean (v);
333                                                         break;
334                                                 case TypeCode.Byte  :
335                                                         v = Convert.ToByte (v);
336                                                         break;
337                                                 case TypeCode.Char  :
338                                                         v = Convert.ToChar (v);
339                                                         break;
340                                                 case TypeCode.DateTime  :
341                                                         v = Convert.ToDateTime (v);
342                                                         break;
343                                                 case TypeCode.Decimal  :
344                                                         v = Convert.ToDecimal (v);
345                                                         break;
346                                                 case TypeCode.Double  :
347                                                         v = Convert.ToDouble (v);
348                                                         break;
349                                                 case TypeCode.Int16  :
350                                                         v = Convert.ToInt16 (v);
351                                                         break;
352                                                 case TypeCode.Int32  :
353                                                         v = Convert.ToInt32 (v);
354                                                         break;
355                                                 case TypeCode.Int64  :
356                                                         v = Convert.ToInt64 (v);
357                                                         break;
358                                                 case TypeCode.SByte  :
359                                                         v = Convert.ToSByte (v);
360                                                         break;
361                                                 case TypeCode.Single  :
362                                                         v = Convert.ToSingle (v);
363                                                         break;
364                                                 case TypeCode.String  :
365                                                         v = Convert.ToString (v);
366                                                         break;
367                                                 case TypeCode.UInt16  :
368                                                         v = Convert.ToUInt16 (v);
369                                                         break;
370                                                 case TypeCode.UInt32  :
371                                                         v = Convert.ToUInt32 (v);
372                                                         break;
373                                                 case TypeCode.UInt64  :
374                                                         v = Convert.ToUInt64 (v);
375                                                         break;
376                                                 default :
377                                                                 
378                                                 switch(cType.ToString()) {
379                                                         case "System.TimeSpan" :
380                                                                 v = (System.TimeSpan) v;
381                                                                 break;
382                                                         case "System.Type" :
383                                                                 v = (System.Type) v;
384                                                                 break;
385                                                         case "System.Object" :
386                                                                 //v = (System.Object) v;
387                                                                 break;
388                                                         default:
389                                                                 if (!cType.IsArray)
390                                                                         throw new InvalidCastException("Type not supported.");
391                                                                 break;
392                                                 }
393                                                         break;
394                                         }
395                                         vType = v.GetType();
396                                 }
397
398                                 // The MaxLength property is ignored for non-text columns
399                                 if ((Type.GetTypeCode(vType) == TypeCode.String) && (this.Table.Columns[index].MaxLength != -1) && 
400                                         (this.Table.Columns[index].MaxLength < ((string)v).Length)) {
401                                         throw new ArgumentException("Cannot set column '" + this.Table.Columns[index].ColumnName + "' to '" + v + "'. The value violates the MaxLength limit of this column.");
402                                 }
403                                 newval = v;
404                                 if(col.AutoIncrement == true) {
405                                         long inc = Convert.ToInt64(v);
406                                         col.UpdateAutoIncrementValue (inc);
407                                 }
408                         }
409                         col.DataHasBeenSet = true;
410                         return newval;
411                 }
412
413                 /// <summary>
414                 /// Gets or sets the custom error description for a row.
415                 /// </summary>
416                 public string RowError {
417                         get { return rowError; }
418                         set { rowError = value; }
419                 }
420
421                 /// <summary>
422                 /// Gets the current state of the row in regards to its relationship to the
423                 /// DataRowCollection.
424                 /// </summary>
425                 public DataRowState RowState {
426                         get { return rowState; }
427                 }
428
429                 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
430                 //to a Datatable so I added this method. Delete if there is a better way.
431                 internal void AttachRow() {
432                         current = proposed;
433                         proposed = null;
434                         rowState = DataRowState.Added;
435                 }
436
437                 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
438                 //from a Datatable so I added this method. Delete if there is a better way.
439                 internal void DetachRow() {
440                         proposed = null;
441                         _rowId = -1;
442                         _hasParentCollection = false;
443                         rowState = DataRowState.Detached;
444                 }
445
446                 /// <summary>
447                 /// Gets the DataTable for which this row has a schema.
448                 /// </summary>
449                 public DataTable Table {
450                         get { return _table; }
451                 }
452
453                 /// <summary>
454                 /// Gets and sets index of row. This is used from 
455                 /// XmlDataDocument.
456                 // </summary>
457                 internal int XmlRowID {
458                         get { return xmlRowID; }
459                         set { xmlRowID = value; }
460                 }
461                 
462                 /// <summary>
463                 /// Gets and sets index of row.
464                 // </summary>
465                 internal int RowID {
466                         get { return _rowId; }
467                         set { _rowId = value; }
468                 }
469
470                 #endregion
471
472                 #region Methods
473
474                 /// <summary>
475                 /// Commits all the changes made to this row since the last time AcceptChanges was
476                 /// called.
477                 /// </summary>
478                 public void AcceptChanges () 
479                 {
480                         EndEdit(); // in case it hasn't been called
481                         switch (rowState) {
482                         case DataRowState.Added:
483                         case DataRowState.Modified:
484                                 rowState = DataRowState.Unchanged;
485                                 break;
486                         case DataRowState.Deleted:
487                                 _table.Rows.RemoveInternal (this);
488                                 DetachRow();
489                                 break;
490                         case DataRowState.Detached:
491                                 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
492                         }
493                         // Accept from detached
494                         if (original == null)
495                                 original = new object[_table.Columns.Count];
496                         Array.Copy (current, original, _table.Columns.Count);
497                 }
498
499                 /// <summary>
500                 /// Begins an edit operation on a DataRow object.
501                 /// </summary>
502                 [MonoTODO]
503                 public void BeginEdit () 
504                 {
505                         if (rowState == DataRowState.Deleted)
506                                 throw new DeletedRowInaccessibleException ();
507                         if (!HasVersion (DataRowVersion.Proposed)) {
508                                 proposed = new object[_table.Columns.Count];
509                                 Array.Copy (current, proposed, current.Length);
510                         }
511                         //TODO: Suspend validation
512                         editing = true;
513                 }
514
515                 /// <summary>
516                 /// Cancels the current edit on the row.
517                 /// </summary>
518                 [MonoTODO]
519                 public void CancelEdit () 
520                 {
521                         editing = false;
522                         //TODO: Events
523                         if (HasVersion (DataRowVersion.Proposed)) {
524                                 proposed = null;
525                                 if (rowState == DataRowState.Modified)
526                                     rowState = DataRowState.Unchanged;
527                         }
528                 }
529
530                 /// <summary>
531                 /// Clears the errors for the row, including the RowError and errors set with
532                 /// SetColumnError.
533                 /// </summary>
534                 public void ClearErrors () 
535                 {
536                         rowError = String.Empty;
537                         columnErrors = new String[_table.Columns.Count];
538                 }
539
540                 /// <summary>
541                 /// Deletes the DataRow.
542                 /// </summary>
543                 [MonoTODO]
544                 public void Delete () 
545                 {
546                         _table.DeletingDataRow(this, DataRowAction.Delete);
547                         switch (rowState) {
548                         case DataRowState.Added:
549                                 // check what to do with child rows
550                                 CheckChildRows(DataRowAction.Delete);
551                                 _table.DeleteRowFromIndexes (this);
552                                 Table.Rows.RemoveInternal (this);
553
554                                 // if row was in Added state we move it to Detached.
555                                 DetachRow();
556                                 break;
557                         case DataRowState.Deleted:
558                                 break;
559                         default:
560                                 // check what to do with child rows
561                                 CheckChildRows(DataRowAction.Delete);
562                                 _table.DeleteRowFromIndexes (this);
563                                 rowState = DataRowState.Deleted;
564                                 break;
565                         }
566                         _table.DeletedDataRow(this, DataRowAction.Delete);
567                 }
568
569                 // check the child rows of this row before deleting the row.
570                 private void CheckChildRows(DataRowAction action)
571                 {
572                         
573                         // in this method we find the row that this row is in a relation with them.
574                         // in shortly we find all child rows of this row.
575                         // then we function according to the DeleteRule of the foriegnkey.
576
577
578                         // 1. find if this row is attached to dataset.
579                         // 2. find if EnforceConstraints is true.
580                         // 3. find if there are any constraint on the table that the row is in.
581                         if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
582                         {
583                                 foreach (DataTable table in _table.DataSet.Tables)
584                                 {
585                                         // loop on all ForeignKeyConstrain of the table.
586                                         foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
587                                         {
588                                                 if (fk.RelatedTable == _table)
589                                                 {
590                                                         Rule rule;
591                                                         if (action == DataRowAction.Delete)
592                                                                 rule = fk.DeleteRule;
593                                                         else
594                                                                 rule = fk.UpdateRule;
595                                                         CheckChildRows(fk, action, rule);
596                                                 }                       
597                                         }
598                                 }
599                         }
600                 }
601
602                 private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
603                 {                               
604                         DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
605                         switch (rule)
606                         {
607                                 case Rule.Cascade:  // delete or change all relted rows.
608                                         if (childRows != null)
609                                         {
610                                                 for (int j = 0; j < childRows.Length; j++)
611                                                 {
612                                                         // if action is delete we delete all child rows
613                                                         if (action == DataRowAction.Delete)
614                                                         {
615                                                                 if (childRows[j].RowState != DataRowState.Deleted)
616                                                                         childRows[j].Delete();
617                                                         }
618                                                         // if action is change we change the values in the child row
619                                                         else if (action == DataRowAction.Change)
620                                                         {
621                                                                 // change only the values in the key columns
622                                                                 // set the childcolumn value to the new parent row value
623                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
624                                                                         childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
625                                                         }
626                                                 }
627                                         }
628                                         break;
629                                 case Rule.None: // throw an exception if there are any child rows.
630                                         if (childRows != null)
631                                         {
632                                                 for (int j = 0; j < childRows.Length; j++)
633                                                 {
634                                                         if (childRows[j].RowState != DataRowState.Deleted)
635                                                         {
636                                                                 string changeStr = "Cannot change this row because constraints are enforced on relation " + fkc.ConstraintName +", and changing this row will strand child rows.";
637                                                                 string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", and deleting this row will strand child rows.";
638                                                                 string message = action == DataRowAction.Delete ? delStr : changeStr;
639                                                                 throw new InvalidConstraintException(message);
640                                                         }
641                                                 }
642                                         }
643                                         break;
644                                 case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
645                                         if (childRows != null)
646                                         {
647                                                 for (int j = 0; j < childRows.Length; j++)
648                                                 {
649                                                         DataRow child = childRows[j];
650                                                         if (childRows[j].RowState != DataRowState.Deleted)
651                                                         {
652                                                                 //set only the key columns to default
653                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
654                                                                         child[fkc.Columns[k]] = fkc.Columns[k].DefaultValue;
655                                                         }
656                                                 }
657                                         }
658                                         break;
659                                 case Rule.SetNull: // set the values in the child row to null.
660                                         if (childRows != null)
661                                         {
662                                                 for (int j = 0; j < childRows.Length; j++)
663                                                 {
664                                                         DataRow child = childRows[j];
665                                                         if (childRows[j].RowState != DataRowState.Deleted)
666                                                         {
667                                                                 // set only the key columns to DBNull
668                                                                 for (int k = 0; k < fkc.Columns.Length; k++)
669                                                                         child.SetNull(fkc.Columns[k]);
670                                                         }
671                                                 }
672                                         }
673                                         break;
674                         }
675
676                 }
677
678                 /// <summary>
679                 /// Ends the edit occurring on the row.
680                 /// </summary>
681                 [MonoTODO]
682                 public void EndEdit () 
683                 {
684                         if (_inChangingEvent)
685                                 throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
686                         if (rowState == DataRowState.Detached)
687                         {
688                                 editing = false;
689                                 return;
690                         }
691
692                         if (HasVersion (DataRowVersion.Proposed))
693                         {
694                                 _inChangingEvent = true;
695                                 try
696                                 {
697                                         _table.ChangingDataRow(this, DataRowAction.Change);
698                                 }
699                                 finally
700                                 {
701                                         _inChangingEvent = false;
702                                 }
703                                 if (rowState == DataRowState.Unchanged)
704                                         rowState = DataRowState.Modified;
705                                 
706                                 //Calling next method validates UniqueConstraints
707                                 //and ForeignKeys.
708                                 try
709                                 {
710                                         if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
711                                                 _table.Rows.ValidateDataRowInternal(this);
712                                 }
713                                 catch (Exception e)
714                                 {
715                                         editing = false;
716                                         proposed = null;
717                                         throw e;
718                                 }
719
720                                 // Now we are going to check all child rows of current row.
721                                 // In the case the cascade is true the child rows will look up for
722                                 // parent row. since lookup in index is always on current,
723                                 // we have to move proposed version of current row to current
724                                 // in the case of check child row failure we are rolling 
725                                 // current row state back.
726                                 object[] backup = current;
727                                 current = proposed;
728                                 proposed = null;
729                                 bool editing_backup = editing;
730                                 editing = false;
731                                 try {
732                                         // check all child rows.
733                                         CheckChildRows(DataRowAction.Change);
734                                 }
735                                 catch (Exception ex) {
736                                         // if check child rows failed - rollback to previous state
737                                         // i.e. restore proposed and current versions
738                                         proposed = current;
739                                         current = backup;
740                                         editing = editing_backup;
741                                         // since we failed - propagate an exception
742                                         throw ex;
743                                 }
744                                 _table.ChangedDataRow(this, DataRowAction.Change);
745                         }
746                 }
747
748                 /// <summary>
749                 /// Gets the child rows of this DataRow using the specified DataRelation.
750                 /// </summary>
751                 public DataRow[] GetChildRows (DataRelation relation) 
752                 {
753                         return GetChildRows (relation, DataRowVersion.Current);
754                 }
755
756                 /// <summary>
757                 /// Gets the child rows of a DataRow using the specified RelationName of a
758                 /// DataRelation.
759                 /// </summary>
760                 public DataRow[] GetChildRows (string relationName) 
761                 {
762                         return GetChildRows (Table.DataSet.Relations[relationName]);
763                 }
764
765                 /// <summary>
766                 /// Gets the child rows of a DataRow using the specified DataRelation, and
767                 /// DataRowVersion.
768                 /// </summary>
769                 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version) 
770                 {
771                         if (relation == null)
772                                 return new DataRow[0];
773
774                         if (this.Table == null || RowState == DataRowState.Detached)
775                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
776
777                         if (relation.DataSet != this.Table.DataSet)
778                                 throw new ArgumentException();
779
780                         if (relation.ChildKeyConstraint != null)
781                                 return GetChildRows (relation.ChildKeyConstraint, version);
782
783                         ArrayList rows = new ArrayList();
784                         DataColumn[] parentColumns = relation.ParentColumns;
785                         DataColumn[] childColumns = relation.ChildColumns;
786                         int numColumn = parentColumns.Length;
787                         if (HasVersion(version))
788                         {
789                                 object[] vals = new object[parentColumns.Length];
790                                 for (int i = 0; i < vals.Length; i++)
791                                         vals[i] = this[parentColumns[i], version];
792                                 
793                                 foreach (DataRow row in relation.ChildTable.Rows) 
794                                 {
795                                         bool allColumnsMatch = false;
796                                         if (row.HasVersion(DataRowVersion.Default))
797                                         {
798                                                 allColumnsMatch = true;
799                                                 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
800                                                 {
801                                                         if (!vals[columnCnt].Equals(
802                                                                 row[childColumns[columnCnt], DataRowVersion.Default])) 
803                                                         {
804                                                                 allColumnsMatch = false;
805                                                                 break;
806                                                         }
807                                                 }
808                                         }
809                                         if (allColumnsMatch) rows.Add(row);
810                                 }
811                         }
812                         DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
813                         rows.CopyTo(result, 0);
814                         return result;
815                 }
816
817                 /// <summary>
818                 /// Gets the child rows of a DataRow using the specified RelationName of a
819                 /// DataRelation, and DataRowVersion.
820                 /// </summary>
821                 public DataRow[] GetChildRows (string relationName, DataRowVersion version) 
822                 {
823                         return GetChildRows (Table.DataSet.Relations[relationName], version);
824                 }
825
826                 private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version) 
827                 {
828                         ArrayList rows = new ArrayList();
829                         DataColumn[] parentColumns = fkc.RelatedColumns;
830                         DataColumn[] childColumns = fkc.Columns;
831                         int numColumn = parentColumns.Length;
832                         if (HasVersion(version))
833                         {
834                                 object[] vals = new object[parentColumns.Length];
835                                 for (int i = 0; i < vals.Length; i++)
836                                         vals[i] = this[parentColumns[i], version];
837
838                                 Index index = fkc.Index;
839                                 if (index != null) {
840                                         // get the child rows from the index
841                                         Node[] childNodes = index.FindAllSimple (vals);
842                                         for (int i = 0; i < childNodes.Length; i++) {
843                                                 rows.Add (childNodes[i].Row);
844                                         }
845                                 }
846                                 else { // if there is no index we search manualy.
847                                         foreach (DataRow row in fkc.Table.Rows) 
848                                         {
849                                                 bool allColumnsMatch = false;
850                                                 if (row.HasVersion(DataRowVersion.Default))
851                                                 {
852                                                         allColumnsMatch = true;
853                                                         for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
854                                                         {
855                                                                 if (!vals[columnCnt].Equals(
856                                                                         row[childColumns[columnCnt], DataRowVersion.Default])) 
857                                                                 {
858                                                                         allColumnsMatch = false;
859                                                                         break;
860                                                                 }
861                                                         }
862                                                 }
863                                                 if (allColumnsMatch) rows.Add(row);
864                                         }
865                                 }
866                         }
867
868                         DataRow[] result = fkc.Table.NewRowArray(rows.Count);
869                         rows.CopyTo(result, 0);
870                         return result;
871                 }
872
873                 /// <summary>
874                 /// Gets the error description of the specified DataColumn.
875                 /// </summary>
876                 public string GetColumnError (DataColumn column) 
877                 {
878                         return GetColumnError (_table.Columns.IndexOf(column));
879                 }
880
881                 /// <summary>
882                 /// Gets the error description for the column specified by index.
883                 /// </summary>
884                 public string GetColumnError (int columnIndex) 
885                 {
886                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
887                                 throw new IndexOutOfRangeException ();
888
889                         string retVal = columnErrors[columnIndex];
890                         if (retVal == null)
891                                 retVal = string.Empty;
892                         return retVal;
893                 }
894
895                 /// <summary>
896                 /// Gets the error description for the column, specified by name.
897                 /// </summary>
898                 public string GetColumnError (string columnName) 
899                 {
900                         return GetColumnError (_table.Columns.IndexOf(columnName));
901                 }
902
903                 /// <summary>
904                 /// Gets an array of columns that have errors.
905                 /// </summary>
906                 public DataColumn[] GetColumnsInError () 
907                 {
908                         ArrayList dataColumns = new ArrayList ();
909
910                         for (int i = 0; i < columnErrors.Length; i += 1)
911                         {
912                                 if (columnErrors[i] != null && columnErrors[i] != String.Empty)
913                                         dataColumns.Add (_table.Columns[i]);
914                         }
915
916                         return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
917                 }
918
919                 /// <summary>
920                 /// Gets the parent row of a DataRow using the specified DataRelation.
921                 /// </summary>
922                 public DataRow GetParentRow (DataRelation relation) 
923                 {
924                         return GetParentRow (relation, DataRowVersion.Current);
925                 }
926
927                 /// <summary>
928                 /// Gets the parent row of a DataRow using the specified RelationName of a
929                 /// DataRelation.
930                 /// </summary>
931                 public DataRow GetParentRow (string relationName) 
932                 {
933                         return GetParentRow (relationName, DataRowVersion.Current);
934                 }
935
936                 /// <summary>
937                 /// Gets the parent row of a DataRow using the specified DataRelation, and
938                 /// DataRowVersion.
939                 /// </summary>
940                 public DataRow GetParentRow (DataRelation relation, DataRowVersion version) 
941                 {
942                         DataRow[] rows = GetParentRows(relation, version);
943                         if (rows.Length == 0) return null;
944                         return rows[0];
945                 }
946
947                 /// <summary>
948                 /// Gets the parent row of a DataRow using the specified RelationName of a 
949                 /// DataRelation, and DataRowVersion.
950                 /// </summary>
951                 public DataRow GetParentRow (string relationName, DataRowVersion version) 
952                 {
953                         return GetParentRow (Table.DataSet.Relations[relationName], version);
954                 }
955
956                 /// <summary>
957                 /// Gets the parent rows of a DataRow using the specified DataRelation.
958                 /// </summary>
959                 public DataRow[] GetParentRows (DataRelation relation) 
960                 {
961                         return GetParentRows (relation, DataRowVersion.Current);
962                 }
963
964                 /// <summary>
965                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
966                 /// DataRelation.
967                 /// </summary>
968                 public DataRow[] GetParentRows (string relationName) 
969                 {
970                         return GetParentRows (relationName, DataRowVersion.Current);
971                 }
972
973                 /// <summary>
974                 /// Gets the parent rows of a DataRow using the specified DataRelation, and
975                 /// DataRowVersion.
976                 /// </summary>
977                 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version) 
978                 {
979                         // TODO: Caching for better preformance
980                         if (relation == null)
981                                 return new DataRow[0];
982
983                         if (this.Table == null || RowState == DataRowState.Detached)
984                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
985
986                         if (relation.DataSet != this.Table.DataSet)
987                                 throw new ArgumentException();
988
989                         ArrayList rows = new ArrayList();
990                         DataColumn[] parentColumns = relation.ParentColumns;
991                         DataColumn[] childColumns = relation.ChildColumns;
992                         int numColumn = parentColumns.Length;
993                         if (HasVersion(version))
994                         {
995                                 object[] vals = new object[childColumns.Length];
996                                 for (int i = 0; i < vals.Length; i++)
997                                         vals[i] = this[childColumns[i], version];
998                                 
999                                 Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
1000                                 if (indx != null) { // get the child rows from the index
1001                                         Node[] childNodes = indx.FindAllSimple (vals);
1002                                         for (int i = 0; i < childNodes.Length; i++) {
1003                                                 rows.Add (childNodes[i].Row);
1004                                         }
1005                                 }
1006                                 else { // no index so we have to search manualy.
1007                                         foreach (DataRow row in relation.ParentTable.Rows) 
1008                                         {
1009                                                 bool allColumnsMatch = false;
1010                                                 if (row.HasVersion(DataRowVersion.Default))
1011                                                 {
1012                                                         allColumnsMatch = true;
1013                                                         for (int columnCnt = 0; columnCnt < numColumn; columnCnt++) 
1014                                                         {
1015                                                                 if (!this[childColumns[columnCnt], version].Equals(
1016                                                                         row[parentColumns[columnCnt], DataRowVersion.Default])) 
1017                                                                 {
1018                                                                         allColumnsMatch = false;
1019                                                                         break;
1020                                                                 }
1021                                                         }
1022                                                 }
1023                                                 if (allColumnsMatch) rows.Add(row);
1024                                         }
1025                                 }
1026                         }
1027
1028                         DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
1029                         rows.CopyTo(result, 0);
1030                         return result;
1031                 }
1032
1033                 /// <summary>
1034                 /// Gets the parent rows of a DataRow using the specified RelationName of a 
1035                 /// DataRelation, and DataRowVersion.
1036                 /// </summary>
1037                 public DataRow[] GetParentRows (string relationName, DataRowVersion version) 
1038                 {
1039                         return GetParentRows (Table.DataSet.Relations[relationName], version);
1040                 }
1041
1042                 /// <summary>
1043                 /// Gets a value indicating whether a specified version exists.
1044                 /// </summary>
1045                 public bool HasVersion (DataRowVersion version) 
1046                 {
1047                         switch (version)
1048                         {
1049                                 case DataRowVersion.Default:
1050                                         if (rowState == DataRowState.Deleted)
1051                                                 return false;
1052                                         if (rowState == DataRowState.Detached)
1053                                                 return proposed != null;
1054                                         return true;
1055                                 case DataRowVersion.Proposed:
1056                                         if (rowState == DataRowState.Deleted)
1057                                                 return false;
1058                                         return (proposed != null);
1059                                 case DataRowVersion.Current:
1060                                         if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
1061                                                 return false;
1062                                         return (current != null);
1063                                 case DataRowVersion.Original:
1064                                         if (rowState == DataRowState.Detached)
1065                                                 return false;
1066                                         return (original != null);
1067                         }
1068                         return false;
1069                 }
1070
1071                 /// <summary>
1072                 /// Gets a value indicating whether the specified DataColumn contains a null value.
1073                 /// </summary>
1074                 public bool IsNull (DataColumn column) 
1075                 {
1076                         object o = this[column];
1077                         return (o == DBNull.Value);
1078                 }
1079
1080                 /// <summary>
1081                 /// Gets a value indicating whether the column at the specified index contains a null
1082                 /// value.
1083                 /// </summary>
1084                 public bool IsNull (int columnIndex) 
1085                 {
1086                         object o = this[columnIndex];
1087                         return (o == DBNull.Value);
1088                 }
1089
1090                 /// <summary>
1091                 /// Gets a value indicating whether the named column contains a null value.
1092                 /// </summary>
1093                 public bool IsNull (string columnName) 
1094                 {
1095                         object o = this[columnName];
1096                         return (o == DBNull.Value);
1097                 }
1098
1099                 /// <summary>
1100                 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
1101                 /// contains a null value.
1102                 /// </summary>
1103                 public bool IsNull (DataColumn column, DataRowVersion version) 
1104                 {
1105                         object o = this[column, version];
1106                         return (o == DBNull.Value);
1107                 }
1108
1109                 /// <summary>
1110                 /// Returns a value indicating whether all of the row columns specified contain a null value.
1111                 /// </summary>
1112                 internal bool IsNullColumns(DataColumn[] columns)
1113                 {
1114                         bool allNull = true;
1115                         for (int i = 0; i < columns.Length; i++) 
1116                         {
1117                                 if (!IsNull(columns[i])) 
1118                                 {
1119                                         allNull = false;
1120                                         break;
1121                                 }
1122                         }
1123                         return allNull;
1124                 }
1125
1126                 /// <summary>
1127                 /// Rejects all changes made to the row since AcceptChanges was last called.
1128                 /// </summary>
1129                 public void RejectChanges () 
1130                 {
1131                         if (RowState == DataRowState.Detached)
1132                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
1133                         // If original is null, then nothing has happened since AcceptChanges
1134                         // was last called.  We have no "original" to go back to.
1135                         if (original != null)
1136                         {
1137                                 Array.Copy (original, current, _table.Columns.Count);
1138                                
1139                                 _table.ChangedDataRow (this, DataRowAction.Rollback);
1140                                 CancelEdit ();
1141                                 switch (rowState)
1142                                 {
1143                                         case DataRowState.Added:
1144                                                 _table.DeleteRowFromIndexes (this);
1145                                                 _table.Rows.RemoveInternal (this);
1146                                                 break;
1147                                         case DataRowState.Modified:
1148                                                 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1149                                                         _table.Rows.ValidateDataRowInternal(this);
1150                                                 rowState = DataRowState.Unchanged;
1151                                                 break;
1152                                         case DataRowState.Deleted:
1153                                                 rowState = DataRowState.Unchanged;
1154                                                 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1155                                                         _table.Rows.ValidateDataRowInternal(this);
1156                                                 break;
1157                                 } 
1158                                 
1159                         }                       
1160                         else {
1161                                 // If rows are just loaded via Xml the original values are null.
1162                                 // So in this case we have to remove all columns.
1163                                 // FIXME: I'm not realy sure, does this break something else, but
1164                                 // if so: FIXME ;)
1165                                 
1166                                 if ((rowState & DataRowState.Added) > 0)
1167                                 {
1168                                         _table.DeleteRowFromIndexes (this);
1169                                         _table.Rows.RemoveInternal (this);
1170                                         // if row was in Added state we move it to Detached.
1171                                         DetachRow();
1172                                 }
1173                         }
1174                 }
1175
1176                 /// <summary>
1177                 /// Sets the error description for a column specified as a DataColumn.
1178                 /// </summary>
1179                 public void SetColumnError (DataColumn column, string error) 
1180                 {
1181                         SetColumnError (_table.Columns.IndexOf (column), error);
1182                 }
1183
1184                 /// <summary>
1185                 /// Sets the error description for a column specified by index.
1186                 /// </summary>
1187                 public void SetColumnError (int columnIndex, string error) 
1188                 {
1189                         if (columnIndex < 0 || columnIndex >= columnErrors.Length)
1190                                 throw new IndexOutOfRangeException ();
1191                         columnErrors[columnIndex] = error;
1192                 }
1193
1194                 /// <summary>
1195                 /// Sets the error description for a column specified by name.
1196                 /// </summary>
1197                 public void SetColumnError (string columnName, string error) 
1198                 {
1199                         SetColumnError (_table.Columns.IndexOf (columnName), error);
1200                 }
1201
1202                 /// <summary>
1203                 /// Sets the value of the specified DataColumn to a null value.
1204                 /// </summary>
1205                 protected void SetNull (DataColumn column) 
1206                 {
1207                         this[column] = DBNull.Value;
1208                 }
1209
1210                 /// <summary>
1211                 /// Sets the parent row of a DataRow with specified new parent DataRow.
1212                 /// </summary>
1213                 public void SetParentRow (DataRow parentRow) 
1214                 {
1215                         SetParentRow(parentRow, null);
1216                 }
1217
1218                 /// <summary>
1219                 /// Sets the parent row of a DataRow with specified new parent DataRow and
1220                 /// DataRelation.
1221                 /// </summary>
1222                 public void SetParentRow (DataRow parentRow, DataRelation relation) 
1223                 {
1224                         if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
1225                                 throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
1226
1227                         if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
1228                                 throw new ArgumentException();
1229                         
1230                         BeginEdit();
1231                         if (relation == null)
1232                         {
1233                                 foreach (DataRelation parentRel in _table.ParentRelations)
1234                                 {
1235                                         DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
1236                                         DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
1237                                         
1238                                         for (int i = 0; i < parentCols.Length; i++)
1239                                         {
1240                                                 if (parentRow == null)
1241                                                         this[childCols[i].Ordinal] = DBNull.Value;
1242                                                 else
1243                                                         this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1244                                         }
1245                                         
1246                                 }
1247                         }
1248                         else
1249                         {
1250                                 DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
1251                                 DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
1252                                         
1253                                 for (int i = 0; i < parentCols.Length; i++)
1254                                 {
1255                                         if (parentRow == null)
1256                                                 this[childCols[i].Ordinal] = DBNull.Value;
1257                                         else
1258                                                 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1259                                 }
1260                         }
1261                         EndEdit();
1262                 }
1263                 
1264                 //Copy all values of this DataaRow to the row parameter.
1265                 internal void CopyValuesToRow(DataRow row)
1266                 {
1267                                                 
1268                         if (row == null)
1269                                 throw new ArgumentNullException("row");
1270                         if (row == this)
1271                                 throw new ArgumentException("'row' is the same as this object");
1272
1273                         DataColumnCollection columns = Table.Columns;
1274                         
1275                         for(int i = 0; i < columns.Count; i++){
1276
1277                                 string columnName = columns[i].ColumnName;
1278                                 int index = row.Table.Columns.IndexOf(columnName);
1279                                 //if a column with the same name exists in both rows copy the values
1280                                 if(index != -1) {
1281                                         if (HasVersion(DataRowVersion.Original))
1282                                         {
1283                                                 if (row.original == null)
1284                                                         row.original = new object[row.Table.Columns.Count];
1285                                                 row.original[index] = row.SetColumnValue(original[i], index);
1286                                         }
1287                                         if (HasVersion(DataRowVersion.Current))
1288                                         {
1289                                                 if (row.current == null)
1290                                                         row.current = new object[row.Table.Columns.Count];
1291                                                 row.current[index] = row.SetColumnValue(current[i], index);
1292                                         }
1293                                         if (HasVersion(DataRowVersion.Proposed))
1294                                         {
1295                                                 if (row.proposed == null)
1296                                                         row.proposed = new object[row.Table.Columns.Count];
1297                                                 row.proposed[index] = row.SetColumnValue(proposed[i], index);
1298                                         }
1299                                         
1300                                         //Saving the current value as the column value
1301                                         row[index] = row.current[index];
1302                                         
1303                                 }
1304                         }
1305
1306                         row.rowState = RowState;
1307                         row.RowError = RowError;
1308                         row.columnErrors = columnErrors;
1309                 }
1310
1311                 
1312                 public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
1313                 {
1314                         // if a column is added we hava to add an additional value the 
1315                         // the priginal, current and propoed arrays.
1316                         // this scenario can happened in merge operation.
1317
1318                         if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
1319                         {
1320                                 object[] tmp;
1321                                 if (current != null)
1322                                 {
1323                                         tmp = new object[current.Length + 1];
1324                                         Array.Copy (current, tmp, current.Length);
1325                                         tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1326                                         current = tmp;
1327                                 }
1328                                 if (proposed != null)
1329                                 {
1330                                         tmp = new object[proposed.Length + 1];
1331                                         Array.Copy (proposed, tmp, proposed.Length);
1332                                         tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1333                                         proposed = tmp;
1334                                 }
1335                                 if(original != null)
1336                                 {
1337                                         tmp = new object[original.Length + 1];
1338                                         Array.Copy (original, tmp, original.Length);
1339                                         tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1340                                         original = tmp;
1341                                 }
1342
1343                         }
1344                 }
1345
1346                 internal bool IsRowChanged(DataRowState rowState) {
1347                         if((RowState & rowState) != 0)
1348                                 return true;
1349
1350                         //we need to find if child rows of this row changed.
1351                         //if yes - we should return true
1352
1353                         // if the rowState is deleted we should get the original version of the row
1354                         // else - we should get the current version of the row.
1355                         DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
1356                         int count = Table.ChildRelations.Count;
1357                         for (int i = 0; i < count; i++){
1358                                 DataRelation rel = Table.ChildRelations[i];
1359                                 DataRow[] childRows = GetChildRows(rel, version);
1360                                 for (int j = 0; j < childRows.Length; j++){
1361                                         if (childRows[j].IsRowChanged(rowState))
1362                                                 return true;
1363                                 }
1364                         }
1365
1366                         return false;
1367                 }
1368
1369                 internal bool HasParentCollection
1370                 {
1371                         get
1372                         {
1373                                 return _hasParentCollection;
1374                         }
1375                         set
1376                         {
1377                                 _hasParentCollection = value;
1378                         }
1379                 }
1380
1381                 internal void CheckNullConstraints()
1382                 {
1383                         if (_nullConstraintViolation)
1384                         {
1385                                 for (int i = 0; i < proposed.Length; i++)
1386                                 {
1387                                         if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
1388                                                 throw new NoNullAllowedException(_nullConstraintMessage);
1389                                 }
1390                                 _nullConstraintViolation = false;
1391                         }
1392                 }
1393
1394                 #endregion // Methods
1395         }
1396
1397         
1398
1399 }