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