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 {
47 DataTable _schemaTable;
48 bool _tableCleared = false;
49 bool _subscribed = false;
51 bool _schemaChanged = false;
55 public DataTableReader (DataTable dt)
56 : this (new DataTable[] {dt})
60 public DataTableReader (DataTable[] dataTables)
62 if (dataTables == null || dataTables.Length <= 0)
63 throw new ArgumentException ("Cannot Create DataTable. Argument Empty!");
65 this._tables = new DataTable [dataTables.Length];
67 for (int i = 0; i < dataTables.Length; i++)
68 this._tables [i] = dataTables [i];
74 _tableCleared = false;
79 #endregion // Constructors
83 public override int Depth {
87 public override int FieldCount {
88 get { return CurrentTable.Columns.Count; }
91 public override bool HasRows {
92 get { return CurrentTable.Rows.Count > 0; }
95 public override bool IsClosed {
96 get { return _closed; }
99 public override object this [int index] {
102 if (index < 0 || index >= FieldCount)
103 throw new ArgumentOutOfRangeException ("index " + index + " is not in the range");
104 DataRow row = CurrentRow;
105 if (row.RowState == DataRowState.Deleted)
106 throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
111 private DataTable CurrentTable {
112 get { return _tables [_index]; }
115 private DataRow CurrentRow {
116 get { return (DataRow) CurrentTable.Rows [_current]; }
120 public override object this [string name] {
123 DataRow row = CurrentRow;
124 if (row.RowState == DataRowState.Deleted)
125 throw new InvalidOperationException ("Deleted Row's information cannot be accessed!");
130 public override int RecordsAffected {
134 #endregion // Properties
138 private void SubscribeEvents ()
140 if (_subscribed) // avoid subscribing multiple times
142 CurrentTable.TableCleared += new DataTableClearEventHandler (OnTableCleared);
143 CurrentTable.RowChanged += new DataRowChangeEventHandler (OnRowChanged);
144 CurrentTable.Columns.CollectionChanged += new CollectionChangeEventHandler (OnColumnCollectionChanged);
145 for (int i=0; i < CurrentTable.Columns.Count; ++i)
146 CurrentTable.Columns [i].PropertyChanged += new PropertyChangedEventHandler (OnColumnChanged);
148 _schemaChanged = false;
151 private void UnsubscribeEvents ()
153 if (!_subscribed) // avoid un-subscribing multiple times
155 CurrentTable.TableCleared -= new DataTableClearEventHandler (OnTableCleared);
156 CurrentTable.RowChanged -= new DataRowChangeEventHandler (OnRowChanged);
157 CurrentTable.Columns.CollectionChanged -= new CollectionChangeEventHandler (OnColumnCollectionChanged);
158 for (int i=0; i < CurrentTable.Columns.Count; ++i)
159 CurrentTable.Columns [i].PropertyChanged -= new PropertyChangedEventHandler (OnColumnChanged);
161 _schemaChanged = false;
164 public override void Close ()
169 UnsubscribeEvents ();
173 public override bool GetBoolean (int i)
175 return (bool) GetValue (i);
178 public override byte GetByte (int i)
180 return (byte) GetValue (i);
183 public override long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
185 byte[] value = this [i] as byte[];
187 ThrowInvalidCastException (this [i].GetType (), typeof (byte[]));
190 int copylen = length > value.Length ? value.Length : length;
191 Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
195 public override char GetChar (int i)
197 return (char) GetValue (i);
200 public override long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
202 char[] value = this [i] as char[];
204 ThrowInvalidCastException (this [i].GetType (), typeof (char[]));
207 int copylen = length > value.Length ? value.Length : length;
208 Array.Copy (value, dataIndex, buffer, bufferIndex, copylen);
212 public override string GetDataTypeName (int i)
214 return GetFieldType (i).ToString ();
217 public override DateTime GetDateTime (int i)
219 return (DateTime) GetValue (i);
222 public override decimal GetDecimal (int i)
224 return (decimal) GetValue (i);
227 public override double GetDouble (int i)
229 return (double) GetValue (i);
232 public override IEnumerator GetEnumerator ()
234 return new DbEnumerator (this);
237 public override Type GetProviderSpecificFieldType (int i)
239 return GetFieldType (i);
242 public override Type GetFieldType (int i)
245 return CurrentTable.Columns [i].DataType;
248 public override float GetFloat (int i)
250 return (float) GetValue (i);
253 public override Guid GetGuid (int i)
255 return (Guid) GetValue (i);
258 public override short GetInt16 (int i)
260 return (short) GetValue (i);
263 public override int GetInt32 (int i)
265 return (int) GetValue (i);
268 public override long GetInt64 (int i)
270 return (long) GetValue (i);
273 public override string GetName (int i)
276 return CurrentTable.Columns [i].ColumnName;
279 public override int GetOrdinal (string name)
282 int index = CurrentTable.Columns.IndexOf (name);
284 throw new ArgumentException (String.Format ("Column {0} is not found in the schema", name));
288 public override object GetProviderSpecificValue (int i)
293 public override int GetProviderSpecificValues (object[] values)
295 return GetValues (values);
298 public override string GetString (int i)
300 return (string) GetValue (i);
303 public override object GetValue (int i)
308 public override int GetValues (object[] values)
311 if (CurrentRow.RowState == DataRowState.Deleted)
312 throw new DeletedRowInaccessibleException ("");
314 int count = (FieldCount < values.Length ? FieldCount : values.Length);
315 for (int i=0; i < count; ++i)
316 values [i] = CurrentRow [i];
320 public override bool IsDBNull (int i)
322 return GetValue (i) is DBNull;
325 public override DataTable GetSchemaTable ()
328 ValidateSchemaIntact ();
330 if (_schemaTable != null)
333 DataTable dt = new DataTable ();
334 dt.Columns.Add ("ColumnName", typeof (string));
335 dt.Columns.Add ("ColumnOrdinal", typeof (int));
336 dt.Columns.Add ("ColumnSize", typeof (int));
337 dt.Columns.Add ("NumericPrecision", typeof (short));
338 dt.Columns.Add ("NumericScale", typeof (short));
339 dt.Columns.Add ("DataType", typeof (Type));
340 dt.Columns.Add ("ProviderType", typeof (int));
341 dt.Columns.Add ("IsLong", typeof (bool));
342 dt.Columns.Add ("AllowDBNull", typeof (bool));
343 dt.Columns.Add ("IsReadOnly", typeof (bool));
344 dt.Columns.Add ("IsRowVersion", typeof (bool));
345 dt.Columns.Add ("IsUnique", typeof (bool));
346 dt.Columns.Add ("IsKey", typeof (bool));
347 dt.Columns.Add ("IsAutoIncrement", typeof (bool));
348 dt.Columns.Add ("BaseCatalogName", typeof (string));
349 dt.Columns.Add ("BaseSchemaName", typeof (string));
350 dt.Columns.Add ("BaseTableName", typeof (string));
351 dt.Columns.Add ("BaseColumnName", typeof (string));
352 dt.Columns.Add ("AutoIncrementSeed", typeof (Int64));
353 dt.Columns.Add ("AutoIncrementStep", typeof (Int64));
354 dt.Columns.Add ("DefaultValue", typeof (object));
355 dt.Columns.Add ("Expression", typeof (string));
356 dt.Columns.Add ("ColumnMapping", typeof (MappingType));
357 dt.Columns.Add ("BaseTableNamespace", typeof (string));
358 dt.Columns.Add ("BaseColumnNamespace", typeof (string));
362 for (int i=0; i < CurrentTable.Columns.Count; ++i) {
364 col = CurrentTable.Columns [i];
365 row ["ColumnName"] = col.ColumnName;
366 row ["BaseColumnName"] = col.ColumnName;
367 row ["ColumnOrdinal"] = col.Ordinal;
368 row ["ColumnSize"] = col.MaxLength;
369 // ms.net doesent set precision and scale even for Decimal values
370 // when are these set ?
371 row ["NumericPrecision"] = DBNull.Value;
372 row ["NumericScale"] = DBNull.Value;
373 row ["DataType"] = col.DataType;
374 row ["ProviderType"] = DBNull.Value; //col.ProviderType;
375 // ms.net doesent set this when datatype is string and maxlength = -1
376 // when is this set ?
377 row ["IsLong"] = false;
378 row ["AllowDBNull"] = col.AllowDBNull;
379 row ["IsReadOnly"] = col.ReadOnly;
380 row ["IsRowVersion"] = false; //this is always false
381 row ["IsUnique"] = col.Unique;
382 row ["IsKey"] = (Array.IndexOf (CurrentTable.PrimaryKey, col) != -1) ;
383 row ["IsAutoIncrement"]= col.AutoIncrement;
384 row ["AutoIncrementSeed"] = col.AutoIncrementSeed;
385 row ["AutoIncrementStep"] = col.AutoIncrementStep;
386 row ["BaseCatalogName"] = (CurrentTable.DataSet != null ? CurrentTable.DataSet.DataSetName : null);
387 row ["BaseSchemaName"] = DBNull.Value; // this is always null
388 row ["BaseTableName"] = CurrentTable.TableName;
389 row ["DefaultValue"] = col.DefaultValue;
390 // If col expression depends on any external table , then the
391 // Expression value is set to empty string in the schematable.
392 if (col.Expression == "")
393 row ["Expression"] = col.Expression;
395 Regex reg = new Regex ("((Parent|Child)( )*[.(])", RegexOptions.IgnoreCase);
396 if (reg.IsMatch (col.Expression, 0))
397 row ["Expression"] = DBNull.Value;
399 row ["Expression"] = col.Expression;
402 row ["ColumnMapping"] = col.ColumnMapping;
403 row ["BaseTableNamespace"] = CurrentTable.Namespace;
404 row ["BaseColumnNamespace"] = col.Namespace;
408 return _schemaTable = dt;
411 private void Validate ()
415 if (_index >= _tables.Length)
416 throw new InvalidOperationException ("Invalid attempt to read when no data is present");
418 throw new RowNotInTableException ("The table is cleared, no rows are accessible");
420 throw new InvalidOperationException ("DataReader is invalid for the DataTable");
421 ValidateSchemaIntact ();
424 private void ValidateClosed ()
427 throw new InvalidOperationException ("Invalid attempt to read when the reader is closed");
430 private void ValidateSchemaIntact ()
433 throw new InvalidOperationException ("Schema of current DataTable '" + CurrentTable.TableName +
434 "' in DataTableReader has changed, DataTableReader is invalid.");
437 void ThrowInvalidCastException (Type sourceType, Type destType)
439 throw new InvalidCastException (
440 String.Format ("Unable to cast object of type '{0}' to type '{1}'.", sourceType, destType));
443 private bool MoveNext ()
445 if (_index >= _tables.Length || _tableCleared)
450 } while (_current < CurrentTable.Rows.Count && CurrentRow.RowState == DataRowState.Deleted);
452 _rowRef = _current < CurrentTable.Rows.Count ? CurrentRow : null;
454 return _current < CurrentTable.Rows.Count;
458 public override bool NextResult ()
460 if ((_index + 1) >= _tables.Length) {
461 UnsubscribeEvents ();
462 _index = _tables.Length; // to make any attempt invalid
463 return false; // end of tables.
466 UnsubscribeEvents ();
470 _schemaTable = null; // force to create fresh
471 _tableCleared = false;
476 public override bool Read ()
482 #endregion // Methods
484 #region // Event Handlers
486 private void OnColumnChanged (object sender, PropertyChangedEventArgs args)
488 _schemaChanged = true;
491 private void OnColumnCollectionChanged (object sender, CollectionChangeEventArgs args)
493 _schemaChanged = true;
496 private void OnRowChanged (object src, DataRowChangeEventArgs args)
498 DataRowAction action = args.Action;
499 DataRow row = args.Row;
500 if (action == DataRowAction.Add) {
501 if (_tableCleared && _current != -1)
504 if (_current == -1 || (_current >= 0 && row.RowID > CurrentRow.RowID)) {
505 _tableCleared = false;
506 return; // no. movement required, if row added after current.
510 _rowRef = CurrentRow;
513 if (action == DataRowAction.Commit && row.RowState == DataRowState.Detached) {
514 // if i am the row deleted, move one down
515 if (_rowRef == row) {
517 _rowRef = _current >= 0 ? CurrentRow : null;
520 // if the row deleted is the last row, move down
521 if (_current >= CurrentTable.Rows.Count) {
523 _rowRef = _current >= 0 ? CurrentRow : null;
527 // deleting a row below _current moves the row one down
528 if (_current > 0 && _rowRef == CurrentTable.Rows [_current-1]) {
530 _rowRef = CurrentRow;
536 private void OnTableCleared (object src, DataTableClearEventArgs args)
538 _tableCleared = true;
541 #endregion // Event Handlers