2 // System.Data.DataRowCollection.cs
5 // Daniel Morgan <danmorg@sc.rr.com>
6 // Tim Coleman <tim@timcoleman.com>
8 // (C) Ximian, Inc 2002
9 // (C) Copyright 2002 Tim Coleman
10 // (C) Copyright 2002 Daniel Morgan
14 using System.Collections;
15 using System.ComponentModel;
20 /// Collection of DataRows in a DataTable
23 public class DataRowCollection : InternalDataCollectionBase
25 private DataTable table;
28 /// Internal constructor used to build a DataRowCollection.
30 internal DataRowCollection (DataTable table) : base ()
36 /// Gets the row at the specified index.
38 public DataRow this[int index]
42 throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
44 return (DataRow) list[index];
49 /// This member overrides InternalDataCollectionBase.List
51 protected override ArrayList List
57 /// Adds the specified DataRow to the DataRowCollection object.
59 public void Add (DataRow row)
63 throw new ArgumentNullException("row", "'row' argument cannot be null.");
65 if (row.Table != this.table)
66 throw new ArgumentException ("This row already belongs to another table.");
68 // If row id is not -1, we know that it is in the collection.
70 throw new ArgumentException ("This row already belongs to this table.");
73 if ((table.DataSet == null || table.DataSet.EnforceConstraints) && !table._duringDataLoad)
74 // we have to check that the new row doesn't colide with existing row
75 ValidateDataRowInternal(row);
77 row.HasParentCollection = true;
80 row.RowID = list.Count - 1;
82 row.Table.ChangedDataRow (row, DataRowAction.Add);
86 /// Creates a row using specified values and adds it to the DataRowCollection.
88 public virtual DataRow Add (object[] values)
90 DataRow row = table.NewRow ();
91 row.ItemArray = values;
97 /// Clears the collection of all rows.
101 if (this.table.DataSet != null)
103 foreach (DataTable table in this.table.DataSet.Tables)
105 foreach (Constraint c in table.Constraints)
107 if (c is ForeignKeyConstraint)
109 if (((ForeignKeyConstraint) c).RelatedTable.Equals(this.table))
110 throw new InvalidConstraintException("Cannot clear table Parent because ForeignKeyConstraint " + c.ConstraintName + " enforces Child.");
119 /// Gets a value indicating whether the primary key of any row in the collection contains
120 /// the specified value.
122 public bool Contains (object key)
124 return Find (key) != null;
128 /// Gets a value indicating whether the primary key column(s) of any row in the
129 /// collection contains the values specified in the object array.
131 public bool Contains (object[] keys)
133 if (table.PrimaryKey.Length != keys.Length)
134 throw new ArgumentException ("Expecting " + table.PrimaryKey.Length + " value(s) for the key " +
135 "being indexed, but received " + keys.Length + " value(s).");
137 return Find (keys) != null;
141 /// Gets the row specified by the primary key value.
143 public DataRow Find (object key)
145 if (table.PrimaryKey.Length == 0)
146 throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
147 if (table.PrimaryKey.Length > 1)
148 throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +
149 " value(s) for the key being indexed, but received 1 value(s).");
151 string primColumnName = table.PrimaryKey [0].ColumnName;
153 object newKey = null;
155 foreach (DataRow row in this) {
157 if (row.RowState != DataRowState.Deleted)
159 object primValue = row [primColumnName];
162 if (primValue == null)
168 newKey = Convert.ChangeType (key, Type.GetTypeCode(primValue.GetType ()));
170 if (primValue.Equals (newKey))
179 /// Gets the row containing the specified primary key values.
181 public DataRow Find (object[] keys)
183 if (table.PrimaryKey.Length == 0)
184 throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
186 string [] primColumnNames = new string [table.PrimaryKey.Length];
188 for (int i = 0; i < primColumnNames.Length; i++)
189 primColumnNames [i] = table.PrimaryKey [i].ColumnName;
192 object newKey = null;
194 foreach (DataRow row in this) {
196 if (row.RowState != DataRowState.Deleted)
199 for (int i = 0; i < keys.Length; i++)
202 object primValue = row [primColumnNames [i]];
203 object keyValue = keys [i];
204 if (keyValue == null)
206 if (primValue == null)
212 newKey = Convert.ChangeType (keyValue, Type.GetTypeCode(primValue.GetType ()));
214 if (!primValue.Equals (newKey))
230 /// Inserts a new row into the collection at the specified location.
232 public void InsertAt (DataRow row, int pos)
235 throw new IndexOutOfRangeException ("The row insert position " + pos + " is invalid.");
238 throw new ArgumentNullException("row", "'row' argument cannot be null.");
240 if (row.Table != this.table)
241 throw new ArgumentException ("This row already belongs to another table.");
243 // If row id is not -1, we know that it is in the collection.
245 throw new ArgumentException ("This row already belongs to this table.");
247 if ((table.DataSet == null || table.DataSet.EnforceConstraints) && !table._duringDataLoad)
248 // we have to check that the new row doesn't colide with existing row
249 ValidateDataRowInternal(row);
251 if (pos >= list.Count)
254 list.Insert (pos, row);
256 row.HasParentCollection = true;
258 row.Table.ChangedDataRow (row, DataRowAction.Add);
262 /// Removes the specified DataRow from the internal list. Used by DataRow to commit the removing.
264 internal void RemoveInternal (DataRow row) {
266 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
268 int index = list.IndexOf(row);
270 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
272 list.RemoveAt(index);
276 /// Removes the specified DataRow from the collection.
278 public void Remove (DataRow row)
281 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
282 int index = list.IndexOf(row);
284 throw new IndexOutOfRangeException ("The given datarow is not in the current DataRowCollection.");
286 // if the row was in added state it will be in Detached state after the
287 // delete operation, so we have to check it.
288 if (row.RowState != DataRowState.Detached)
293 /// Removes the row at the specified index from the collection.
295 public void RemoveAt (int index)
297 if (index < 0 || index >= list.Count)
298 throw new IndexOutOfRangeException ("There is no row at position " + index + ".");
299 DataRow row = (DataRow)list [index];
305 ///Internal method used to validate a given DataRow with respect
306 ///to the DataRowCollection
309 internal void ValidateDataRowInternal(DataRow row)
311 //first check for null violations.
312 row.CheckNullConstraints();
313 // This validates constraints in the specific order :
314 // first unique/primary keys first, then Foreignkeys, etc
315 ArrayList uniqueConstraintsDone = new ArrayList();
316 ArrayList foreignKeyConstraintsDone = new ArrayList();
318 foreach(Constraint constraint in table.Constraints.UniqueConstraints) {
319 constraint.AssertConstraint(row);
320 uniqueConstraintsDone.Add(constraint);
323 foreach(Constraint constraint in table.Constraints.ForeignKeyConstraints) {
324 constraint.AssertConstraint(row);
325 foreignKeyConstraintsDone.Add(constraint);
328 // if one of the AssertConstraint failed - we need to "rollback" all the changes
329 // caused by AssertCoinstraint calls already succeeded
330 catch(ConstraintException e) {
331 RollbackAsserts(row,foreignKeyConstraintsDone,uniqueConstraintsDone);
334 catch(InvalidConstraintException e) {
335 RollbackAsserts(row,foreignKeyConstraintsDone,uniqueConstraintsDone);
340 private void RollbackAsserts(DataRow row,ICollection foreignKeyConstraintsDone,
341 ICollection uniqueConstraintsDone)
343 // if any of constraints assert failed -
344 // we have to rollback all the asserts scceeded
345 // on order reverse to thier original execution
346 foreach(Constraint constraint in foreignKeyConstraintsDone) {
347 constraint.RollbackAssert(row);
350 foreach(Constraint constraint in uniqueConstraintsDone) {
351 constraint.RollbackAssert(row);