2 // System.Data.DataTableReader.cs
5 // Sureshkumar T <tsureshkumar@novell.com>
6 // Tim Coleman (tim@timcoleman.com)
8 // Copyright (C) Tim Coleman, 2003
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.Data.Common;
38 using System.ComponentModel;
39 using System.Text.RegularExpressions;
41 namespace System.Data {
42 public sealed class DataTableReader : DbDataReader
48 DataTable _schemaTable;
49 bool _tableCleared = false;
50 bool _subscribed = false;
52 bool _schemaChanged = false;
56 public DataTableReader (DataTable dt)
57 : this (new DataTable[] {dt})
61 public DataTableReader (DataTable[] dataTables)
63 if (dataTables == null
64 || dataTables.Length <= 0)
65 throw new ArgumentException ("Cannot Create DataTable. Argument Empty!");
67 this._tables = new DataTable [dataTables.Length];
69 for (int i = 0; i < dataTables.Length; i++)
70 this._tables [i] = dataTables [i];
76 _tableCleared = false;
81 #endregion // Constructors
85 public override int Depth {
89 public override int FieldCount {
90 get { return CurrentTable.Columns.Count; }
93 public override bool HasRows {
94 get { return CurrentTable.Rows.Count > 0; }
97 public override bool IsClosed {
98 get { return _closed; }
101 public override object this [int index] {
104 if (index < 0 || index >= FieldCount)
105 throw new ArgumentOutOfRangeException ("index " + index + " is not in the range");
106 DataRow row = CurrentRow;
107 if (row.RowState == DataRowState.Deleted)
108 throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
113 private DataTable CurrentTable
115 get { return _tables [_index]; }
118 private DataRow CurrentRow
120 get { return (DataRow) CurrentTable.Rows [_current]; }
124 public override object this [string name] {
127 DataRow row = CurrentRow;
128 if (row.RowState == DataRowState.Deleted)
129 throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
134 public override int RecordsAffected {
138 #endregion // Properties
142 private void SubscribeEvents ()
144 if (_subscribed) // avoid subscribing multiple times
146 CurrentTable.TableCleared += new DataTableClearEventHandler (OnTableCleared);
147 CurrentTable.RowChanged += new DataRowChangeEventHandler (OnRowChanged);
148 CurrentTable.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnCollectionChanged);
149 for (int i=0; i < CurrentTable.Columns.Count; ++i)
150 CurrentTable.Columns [i].PropertyChanged += new PropertyChangedEventHandler (OnColumnChanged);
152 _schemaChanged = false;
155 private void UnsubscribeEvents ()
157 if (!_subscribed) // avoid un-subscribing multiple times
159 CurrentTable.TableCleared -= new DataTableClearEventHandler (OnTableCleared);
160 CurrentTable.RowChanged -= new DataRowChangeEventHandler (OnRowChanged);
161 CurrentTable.Columns.CollectionChanged -= new CollectionChangeEventHandler (OnColumnCollectionChanged);
162 for (int i=0; i < CurrentTable.Columns.Count; ++i)
163 CurrentTable.Columns [i].PropertyChanged -= new PropertyChangedEventHandler (OnColumnChanged);
165 _schemaChanged = false;
168 public override void Close ()
173 UnsubscribeEvents ();
177 public override bool GetBoolean (int i)
179 return (bool) GetValue (i);
182 public override byte GetByte (int i)
184 return (byte) GetValue (i);
187 public override long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
189 byte[] value = this [i] as byte[];
191 ThrowInvalidCastException (this [i].GetType (), typeof (byte[]));
194 int copylen = length > value.Length ? value.Length : length;
195 Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
199 public override char GetChar (int i)
201 return (char) GetValue (i);
204 public override long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
206 char[] value = this [i] as char[];
208 ThrowInvalidCastException (this [i].GetType (), typeof (char[]));
211 int copylen = length > value.Length ? value.Length : length;
212 Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
216 public override string GetDataTypeName (int i)
218 return GetFieldType (i).ToString ();
221 public override DateTime GetDateTime (int i)
223 return (DateTime) GetValue (i);
226 public override decimal GetDecimal (int i)
228 return (decimal) GetValue (i);
231 public override double GetDouble (int i)
233 return (double) GetValue (i);
236 public override IEnumerator GetEnumerator ()
238 return new DbEnumerator (this);
241 public override Type GetProviderSpecificFieldType (int i)
243 return GetFieldType (i);
246 public override Type GetFieldType (int i)
249 return CurrentTable.Columns [i].DataType;
252 public override float GetFloat (int i)
254 return (float) GetValue (i);
257 public override Guid GetGuid (int i)
259 return (Guid) GetValue (i);
262 public override short GetInt16 (int i)
264 return (short) GetValue (i);
267 public override int GetInt32 (int i)
269 return (int) GetValue (i);
272 public override long GetInt64 (int i)
274 return (long) GetValue (i);
277 public override string GetName (int i)
280 return CurrentTable.Columns [i].ColumnName;
283 public override int GetOrdinal (string name)
286 int index = CurrentTable.Columns.IndexOf (name);
288 throw new ArgumentException (String.Format ("Column {0} is not found in the schema",
293 public override object GetProviderSpecificValue (int i)
298 public override int GetProviderSpecificValues (object[] values)
300 return GetValues (values);
303 public override string GetString (int i)
305 return (string) GetValue (i);
308 public override object GetValue (int i)
313 public override int GetValues (object[] values)
316 if (CurrentRow.RowState == DataRowState.Deleted)
317 throw new DeletedRowInaccessibleException ("");
319 int count = (FieldCount < values.Length ? FieldCount : values.Length);
320 for (int i=0; i < count; ++i)
321 values [i] = CurrentRow [i];
325 public override bool IsDBNull (int i)
327 return GetValue (i) is DBNull;
330 public override DataTable GetSchemaTable ()
333 ValidateSchemaIntact ();
335 if (_schemaTable != null)
338 DataTable dt = new DataTable ();
339 DataColumn[] cols = new DataColumn [25];
340 dt.Columns.Add ("ColumnName", typeof (string));
341 dt.Columns.Add ("ColumnOrdinal", typeof (int));
342 dt.Columns.Add ("ColumnSize", typeof (int));
343 dt.Columns.Add ("NumericPrecision", typeof (short));
344 dt.Columns.Add ("NumericScale", typeof (short));
345 dt.Columns.Add ("DataType", typeof (Type));
346 dt.Columns.Add ("ProviderType", typeof (int));
347 dt.Columns.Add ("IsLong", typeof (bool));
348 dt.Columns.Add ("AllowDBNull", typeof (bool));
349 dt.Columns.Add ("IsReadOnly", typeof (bool));
350 dt.Columns.Add ("IsRowVersion", typeof (bool));
351 dt.Columns.Add ("IsUnique", typeof (bool));
352 dt.Columns.Add ("IsKey", typeof (bool));
353 dt.Columns.Add ("IsAutoIncrement", typeof (bool));
354 dt.Columns.Add ("BaseCatalogName", typeof (string));
355 dt.Columns.Add ("BaseSchemaName", typeof (string));
356 dt.Columns.Add ("BaseTableName", typeof (string));
357 dt.Columns.Add ("BaseColumnName", typeof (string));
358 dt.Columns.Add ("AutoIncrementSeed", typeof (Int64));
359 dt.Columns.Add ("AutoIncrementStep", typeof (Int64));
360 dt.Columns.Add ("DefaultValue", typeof (object));
361 dt.Columns.Add ("Expression", typeof (string));
362 dt.Columns.Add ("ColumnMapping", typeof (MappingType));
363 dt.Columns.Add ("BaseTableNamespace", typeof (string));
364 dt.Columns.Add ("BaseColumnNamespace", typeof (string));
368 for (int i=0; i < CurrentTable.Columns.Count; ++i) {
370 col = CurrentTable.Columns [i];
371 row ["ColumnName"] = col.ColumnName;
372 row ["BaseColumnName"] = col.ColumnName;
373 row ["ColumnOrdinal"] = col.Ordinal;
374 row ["ColumnSize"] = col.MaxLength;
375 // ms.net doesent set precision and scale even for Decimal values
376 // when are these set ?
377 row ["NumericPrecision"] = DBNull.Value;
378 row ["NumericScale"] = DBNull.Value;
379 row ["DataType"] = col.DataType;
380 row ["ProviderType"] = DBNull.Value; //col.ProviderType;
381 // ms.net doesent set this when datatype is string and maxlength = -1
382 // when is this set ?
383 row ["IsLong"] = false;
384 row ["AllowDBNull"] = col.AllowDBNull;
385 row ["IsReadOnly"] = col.ReadOnly;
386 row ["IsRowVersion"] = false; //this is always false
387 row ["IsUnique"] = col.Unique;
388 row ["IsKey"] = (Array.IndexOf (CurrentTable.PrimaryKey, col) != -1) ;
389 row ["IsAutoIncrement"]= col.AutoIncrement;
390 row ["AutoIncrementSeed"] = col.AutoIncrementSeed;
391 row ["AutoIncrementStep"] = col.AutoIncrementStep;
392 row ["BaseCatalogName"] = (CurrentTable.DataSet != null ? CurrentTable.DataSet.DataSetName : null);
393 row ["BaseSchemaName"] = DBNull.Value; // this is always null
394 row ["BaseTableName"] = CurrentTable.TableName;
395 row ["DefaultValue"] = col.DefaultValue;
396 // If col expression depends on any external table , then the
397 // Expression value is set to empty string in the schematable.
398 if (col.Expression == "")
399 row ["Expression"] = col.Expression;
401 Regex reg = new Regex ("((Parent|Child)( )*[.(])", RegexOptions.IgnoreCase);
402 if (reg.IsMatch (col.Expression, 0))
403 row ["Expression"] = DBNull.Value;
405 row ["Expression"] = col.Expression;
408 row ["ColumnMapping"] = col.ColumnMapping;
409 row ["BaseTableNamespace"] = CurrentTable.Namespace;
410 row ["BaseColumnNamespace"] = col.Namespace;
414 return _schemaTable = dt;
417 private void Validate ()
421 if (_index >= _tables.Length)
422 throw new InvalidOperationException ("Invalid attempt to read when " +
423 "no data is present");
425 throw new RowNotInTableException ("The table is cleared, no rows are " +
428 throw new InvalidOperationException ("DataReader is invalid " +
429 "for the DataTable");
430 ValidateSchemaIntact ();
433 private void ValidateClosed ()
436 throw new InvalidOperationException ("Invalid attempt to read when " +
437 "the reader is closed");
440 private void ValidateSchemaIntact ()
443 throw new InvalidOperationException ("Schema of current DataTable '" + CurrentTable.TableName +
444 "' in DataTableReader has changed, DataTableReader is invalid.");
447 void ThrowInvalidCastException (Type sourceType, Type destType)
449 throw new InvalidCastException (String.Format ("Unable to cast object of type '{0}' to type '{1}'.",
450 sourceType, destType));
453 private bool MoveNext ()
455 if (_index >= _tables.Length || _tableCleared)
460 } while (_current < CurrentTable.Rows.Count
461 && CurrentRow.RowState == DataRowState.Deleted);
463 _rowRef = _current < CurrentTable.Rows.Count ? CurrentRow : null;
465 return _current < CurrentTable.Rows.Count;
469 public override bool NextResult ()
471 if ((_index + 1) >= _tables.Length) {
472 UnsubscribeEvents ();
473 _index = _tables.Length; // to make any attempt invalid
474 return false; // end of tables.
477 UnsubscribeEvents ();
481 _schemaTable = null; // force to create fresh
482 _tableCleared = false;
487 public override bool Read ()
493 #endregion // Methods
495 #region // Event Handlers
497 private void OnColumnChanged (object sender, PropertyChangedEventArgs args)
499 _schemaChanged = true;
502 private void OnColumnCollectionChanged (object sender, CollectionChangeEventArgs args)
504 _schemaChanged = true;
507 private void OnRowChanged (object src, DataRowChangeEventArgs args)
509 DataRowAction action = args.Action;
510 DataRow row = args.Row;
511 if (action == DataRowAction.Add) {
512 if (_tableCleared && _current != -1)
515 if (_current == -1 // yet to read
516 || (_current >= 0 && row.RowID > CurrentRow.RowID) // row added above
518 _tableCleared = false;
519 return; // no. movement required, if row added after current.
523 _rowRef = CurrentRow;
527 if (action == DataRowAction.Commit
528 && row.RowState == DataRowState.Detached) {
530 // if i am the row deleted, move one down
531 if (_rowRef == row) {
533 _rowRef = _current >= 0 ? CurrentRow : null;
536 // if the row deleted is the last row, move down
537 if (_current >= CurrentTable.Rows.Count) {
539 _rowRef = _current >= 0 ? CurrentRow : null;
543 // deleting a row below _current moves the row one down
544 if (_current > 0 && _rowRef == CurrentTable.Rows [_current-1]) {
546 _rowRef = CurrentRow;
552 private void OnTableCleared (object src, DataTableClearEventArgs args)
554 _tableCleared = true;
557 #endregion // Event Handlers