DbDataAdapter.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. //
  2. // System.Data.Common.DbDataAdapter.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya ([email protected])
  6. // Tim Coleman ([email protected])
  7. //
  8. // (C) Ximian, Inc
  9. // Copyright (C) 2002 Tim Coleman
  10. //
  11. using System;
  12. using System.Collections;
  13. using System.ComponentModel;
  14. using System.Data;
  15. namespace System.Data.Common {
  16. public abstract class DbDataAdapter : DataAdapter, ICloneable
  17. {
  18. #region Fields
  19. public const string DefaultSourceTableName = "Table";
  20. const string DefaultSourceColumnName = "Column";
  21. #endregion // Fields
  22. #region Constructors
  23. protected DbDataAdapter()
  24. {
  25. }
  26. #endregion // Fields
  27. #region Properties
  28. IDbCommand DeleteCommand {
  29. get { return ((IDbDataAdapter) this).DeleteCommand; }
  30. }
  31. IDbCommand InsertCommand {
  32. get { return ((IDbDataAdapter) this).InsertCommand; }
  33. }
  34. IDbCommand SelectCommand {
  35. get { return ((IDbDataAdapter) this).SelectCommand; }
  36. }
  37. IDbCommand UpdateCommand {
  38. get { return ((IDbDataAdapter) this).UpdateCommand; }
  39. }
  40. #endregion // Properties
  41. #region Events
  42. [DataCategory ("Fill")]
  43. [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")]
  44. public event FillErrorEventHandler FillError;
  45. #endregion // Events
  46. #region Methods
  47. protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
  48. protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
  49. private FillErrorEventArgs CreateFillErrorEvent (DataTable dataTable, object[] values, Exception e)
  50. {
  51. FillErrorEventArgs args = new FillErrorEventArgs (dataTable, values);
  52. args.Errors = e;
  53. args.Continue = false;
  54. return args;
  55. }
  56. protected override void Dispose (bool disposing)
  57. {
  58. if (disposing) {
  59. ((IDbDataAdapter) this).SelectCommand = null;
  60. ((IDbDataAdapter) this).InsertCommand = null;
  61. ((IDbDataAdapter) this).UpdateCommand = null;
  62. ((IDbDataAdapter) this).DeleteCommand = null;
  63. }
  64. }
  65. public override int Fill (DataSet dataSet)
  66. {
  67. return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, CommandBehavior.Default);
  68. }
  69. public int Fill (DataTable dataTable)
  70. {
  71. if (dataTable == null)
  72. throw new NullReferenceException ();
  73. return Fill (dataTable, SelectCommand, CommandBehavior.Default);
  74. }
  75. public int Fill (DataSet dataSet, string srcTable)
  76. {
  77. return Fill (dataSet, 0, 0, srcTable, SelectCommand, CommandBehavior.Default);
  78. }
  79. protected virtual int Fill (DataTable dataTable, IDataReader dataReader)
  80. {
  81. int count = 0;
  82. bool doContinue = true;
  83. if (dataReader.FieldCount == 0) {
  84. dataReader.Close ();
  85. return 0;
  86. }
  87. try
  88. {
  89. object[] itemArray = new object [dataReader.FieldCount];
  90. string tableName = SetupSchema (SchemaType.Mapped, dataTable.TableName);
  91. if (tableName != null)
  92. {
  93. dataTable.TableName = tableName;
  94. Hashtable mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
  95. while (doContinue && dataReader.Read ())
  96. {
  97. // we get the values from the datareader
  98. dataReader.GetValues (itemArray);
  99. // we only need the values that has a mapping to the table.
  100. object[] tableArray = new object[mapping.Count];
  101. for (int i = 0; i < tableArray.Length; i++)
  102. tableArray[i] = mapping[i]; // get the value for each column
  103. try
  104. {
  105. dataTable.BeginLoadData ();
  106. dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
  107. dataTable.EndLoadData ();
  108. count += 1;
  109. }
  110. catch (Exception e)
  111. {
  112. FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e);
  113. OnFillError (args);
  114. doContinue = args.Continue;
  115. }
  116. }
  117. }
  118. }
  119. finally
  120. {
  121. dataReader.Close ();
  122. }
  123. return count;
  124. }
  125. protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior)
  126. {
  127. CommandBehavior commandBehavior = behavior;
  128. // first see that the connection is not close.
  129. if (command.Connection.State == ConnectionState.Closed)
  130. {
  131. command.Connection.Open ();
  132. commandBehavior |= CommandBehavior.CloseConnection;
  133. }
  134. return Fill (dataTable, command.ExecuteReader (commandBehavior));
  135. }
  136. public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable)
  137. {
  138. return this.Fill (dataSet, startRecord, maxRecords, srcTable, SelectCommand, CommandBehavior.Default);
  139. }
  140. protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
  141. {
  142. if (startRecord < 0)
  143. throw new ArgumentException ("The startRecord parameter was less than 0.");
  144. if (maxRecords < 0)
  145. throw new ArgumentException ("The maxRecords parameter was less than 0.");
  146. if (dataReader.FieldCount == 0) {
  147. dataReader.Close ();
  148. return 0;
  149. }
  150. DataTable dataTable;
  151. int resultIndex = 0;
  152. int count = 0;
  153. bool doContinue = true;
  154. try
  155. {
  156. string tableName = srcTable;
  157. object[] itemArray;
  158. do
  159. {
  160. tableName = SetupSchema (SchemaType.Mapped, tableName);
  161. if (tableName != null)
  162. {
  163. // check if the table exists in the dataset
  164. if (dataSet.Tables.Contains (tableName))
  165. // get the table from the dataset
  166. dataTable = dataSet.Tables [tableName];
  167. else
  168. {
  169. dataTable = new DataTable(tableName);
  170. dataSet.Tables.Add (dataTable);
  171. }
  172. Hashtable mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
  173. for (int i = 0; i < startRecord; i += 1)
  174. dataReader.Read ();
  175. itemArray = new object [dataReader.FieldCount];
  176. while (doContinue && dataReader.Read () && !(maxRecords > 0 && count >= maxRecords))
  177. {
  178. // we get the values from the datareader
  179. dataReader.GetValues (itemArray);
  180. // we only need the values that has a mapping to the table.
  181. object[] tableArray = new object[mapping.Count];
  182. for (int i = 0; i < tableArray.Length; i++)
  183. tableArray[i] = itemArray[(int)mapping[i]]; // get the value for each column
  184. try
  185. {
  186. dataTable.BeginLoadData ();
  187. //dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
  188. dataTable.LoadDataRow (tableArray, AcceptChangesDuringFill);
  189. dataTable.EndLoadData ();
  190. count += 1;
  191. }
  192. catch (Exception e)
  193. {
  194. FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e);
  195. OnFillError (args);
  196. doContinue = args.Continue;
  197. }
  198. }
  199. tableName = String.Format ("{0}{1}", srcTable, ++resultIndex);
  200. startRecord = 0;
  201. maxRecords = 0;
  202. }
  203. } while (doContinue && dataReader.NextResult ());
  204. }
  205. finally
  206. {
  207. dataReader.Close ();
  208. }
  209. return count;
  210. }
  211. protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
  212. {
  213. CommandBehavior commandBehavior = behavior;
  214. if (command.Connection.State == ConnectionState.Closed) {
  215. command.Connection.Open ();
  216. commandBehavior |= CommandBehavior.CloseConnection;
  217. }
  218. return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords);
  219. }
  220. public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType)
  221. {
  222. return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default);
  223. }
  224. public DataTable FillSchema (DataTable dataTable, SchemaType schemaType)
  225. {
  226. return FillSchema (dataTable, schemaType, SelectCommand, CommandBehavior.Default);
  227. }
  228. public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable)
  229. {
  230. return FillSchema (dataSet, schemaType, SelectCommand, srcTable, CommandBehavior.Default);
  231. }
  232. [MonoTODO ("Verify")]
  233. protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior)
  234. {
  235. behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
  236. if (command.Connection.State == ConnectionState.Closed) {
  237. command.Connection.Open ();
  238. behavior |= CommandBehavior.CloseConnection;
  239. }
  240. IDataReader reader = command.ExecuteReader (behavior);
  241. try
  242. {
  243. string tableName = SetupSchema (schemaType, dataTable.TableName);
  244. if (tableName != null)
  245. {
  246. BuildSchema (reader, dataTable, schemaType);
  247. }
  248. }
  249. finally
  250. {
  251. reader.Close ();
  252. }
  253. return dataTable;
  254. }
  255. [MonoTODO ("Verify")]
  256. protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)
  257. {
  258. behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
  259. if (command.Connection.State == ConnectionState.Closed) {
  260. command.Connection.Open ();
  261. behavior |= CommandBehavior.CloseConnection;
  262. }
  263. IDataReader reader = command.ExecuteReader (behavior);
  264. ArrayList output = new ArrayList ();
  265. string tableName = srcTable;
  266. int index = 0;
  267. DataTable table;
  268. try
  269. {
  270. tableName = SetupSchema (schemaType, tableName);
  271. if (tableName != null)
  272. {
  273. if (dataSet.Tables.Contains (tableName))
  274. table = dataSet.Tables [tableName];
  275. else
  276. {
  277. table = new DataTable(tableName);
  278. dataSet.Tables.Add (table);
  279. }
  280. BuildSchema (reader, table, schemaType);
  281. output.Add (table);
  282. tableName = String.Format ("{0}{1}", srcTable, ++index);
  283. }
  284. }
  285. finally
  286. {
  287. reader.Close ();
  288. }
  289. return (DataTable[]) output.ToArray (typeof (DataTable));
  290. }
  291. private string SetupSchema (SchemaType schemaType, string sourceTableName)
  292. {
  293. DataTableMapping tableMapping = null;
  294. if (schemaType == SchemaType.Mapped)
  295. {
  296. tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTableName, sourceTableName, MissingMappingAction);
  297. if (tableMapping != null)
  298. return tableMapping.DataSetTable;
  299. return null;
  300. }
  301. else
  302. return sourceTableName;
  303. }
  304. [EditorBrowsable (EditorBrowsableState.Advanced)]
  305. public override IDataParameter[] GetFillParameters ()
  306. {
  307. IDataParameter[] parameters = new IDataParameter[SelectCommand.Parameters.Count];
  308. SelectCommand.Parameters.CopyTo (parameters, 0);
  309. return parameters;
  310. }
  311. // this method bulds the schema for a given datatable
  312. // returns a hashtable that his keys are the ordinal of the datatable columns, and his values
  313. // are the indexes of the source columns in the data reader.
  314. // each column in the datatable has a mapping to a specific column in the datareader
  315. // the hashtable represents this match.
  316. [MonoTODO ("Test")]
  317. private Hashtable BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType)
  318. {
  319. int readerIndex = 0;
  320. Hashtable mapping = new Hashtable(); // hashing the reader indexes with the datatable indexes
  321. ArrayList primaryKey = new ArrayList ();
  322. ArrayList sourceColumns = new ArrayList ();
  323. foreach (DataRow schemaRow in reader.GetSchemaTable ().Rows) {
  324. // generate a unique column name in the source table.
  325. string sourceColumnName;
  326. if (schemaRow ["ColumnName"].Equals (DBNull.Value))
  327. sourceColumnName = DefaultSourceColumnName;
  328. else
  329. sourceColumnName = (string) schemaRow ["ColumnName"];
  330. string realSourceColumnName = sourceColumnName;
  331. for (int i = 1; sourceColumns.Contains (realSourceColumnName); i += 1)
  332. realSourceColumnName = String.Format ("{0}{1}", sourceColumnName, i);
  333. sourceColumns.Add(realSourceColumnName);
  334. // generate DataSetColumnName from DataTableMapping, if any
  335. string dsColumnName = realSourceColumnName;
  336. DataTableMapping tableMapping = null;
  337. if (schemaType == SchemaType.Mapped)
  338. tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, table.TableName, table.TableName, MissingMappingAction);
  339. if (tableMapping != null)
  340. {
  341. table.TableName = tableMapping.DataSetTable;
  342. // check to see if the column mapping exists
  343. DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction(tableMapping.ColumnMappings, realSourceColumnName, MissingMappingAction);
  344. if (columnMapping != null)
  345. {
  346. DataColumn col =
  347. columnMapping.GetDataColumnBySchemaAction(
  348. table ,
  349. (Type)schemaRow["DataType"],
  350. MissingSchemaAction);
  351. if (col != null)
  352. {
  353. // if the column is not in the table - add it.
  354. if (table.Columns.IndexOf(col) == -1)
  355. {
  356. if (MissingSchemaAction == MissingSchemaAction.Add || MissingSchemaAction == MissingSchemaAction.AddWithKey)
  357. table.Columns.Add(col);
  358. }
  359. if (!schemaRow["IsKey"].Equals (DBNull.Value))
  360. if ((bool) (schemaRow ["IsKey"]))
  361. primaryKey.Add (col);
  362. // add the ordinal of the column as a key and the index of the column in the datareader as a value.
  363. mapping.Add(col.Ordinal, readerIndex);
  364. }
  365. }
  366. }
  367. readerIndex++;
  368. }
  369. if (MissingSchemaAction == MissingSchemaAction.AddWithKey && primaryKey.Count > 0)
  370. table.PrimaryKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn)));
  371. return mapping;
  372. }
  373. [MonoTODO]
  374. object ICloneable.Clone ()
  375. {
  376. throw new NotImplementedException ();
  377. }
  378. [MonoTODO]
  379. public int Update (DataRow[] dataRows)
  380. {
  381. if (dataRows == null)
  382. throw new ArgumentNullException("dataRows");
  383. if (dataRows.Length == 0)
  384. return 0;
  385. if (dataRows[0] == null)
  386. throw new ArgumentException("dataRows[0].");
  387. DataTable table = dataRows[0].Table;
  388. if (table == null)
  389. throw new ArgumentException("table is null reference.");
  390. // all rows must be in the same table
  391. for (int i = 0; i < dataRows.Length; i++)
  392. {
  393. if (dataRows[i] == null)
  394. throw new ArgumentException("dataRows[" + i + "].");
  395. if (dataRows[i].Table != table)
  396. throw new ArgumentException(
  397. " DataRow["
  398. + i
  399. + "] is from a different DataTable than DataRow[0].");
  400. }
  401. // get table mapping for this rows
  402. DataTableMapping tableMapping = TableMappings.GetByDataSetTable(table.TableName);
  403. if (tableMapping == null)
  404. {
  405. tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(
  406. TableMappings,
  407. table.TableName,
  408. table.TableName,
  409. MissingMappingAction);
  410. if (tableMapping == null)
  411. tableMapping =
  412. new DataTableMapping(
  413. table.TableName,
  414. table.TableName);
  415. }
  416. DataRow[] copy = new DataRow [dataRows.Length];
  417. Array.Copy(dataRows, 0, copy, 0, dataRows.Length);
  418. return Update(copy, tableMapping);
  419. }
  420. public override int Update (DataSet dataSet)
  421. {
  422. return Update (dataSet, DefaultSourceTableName);
  423. }
  424. public int Update (DataTable dataTable)
  425. {
  426. int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
  427. if (index < 0)
  428. throw new ArgumentException ();
  429. return Update (dataTable, TableMappings [index]);
  430. }
  431. private int Update (DataTable dataTable, DataTableMapping tableMapping)
  432. {
  433. DataRow[] rows = new DataRow [dataTable.Rows.Count];
  434. dataTable.Rows.CopyTo (rows, 0);
  435. return Update (rows, tableMapping);
  436. }
  437. [MonoTODO]
  438. protected virtual int Update (DataRow[] dataRows, DataTableMapping tableMapping)
  439. {
  440. int updateCount = 0;
  441. foreach (DataRow row in dataRows) {
  442. StatementType statementType = StatementType.Update;
  443. IDbCommand command = null;
  444. string commandName = String.Empty;
  445. bool useCommandBuilder = false;
  446. switch (row.RowState) {
  447. case DataRowState.Added:
  448. statementType = StatementType.Insert;
  449. command = InsertCommand;
  450. commandName = "Insert";
  451. break;
  452. case DataRowState.Deleted:
  453. statementType = StatementType.Delete;
  454. command = DeleteCommand;
  455. commandName = "Delete";
  456. break;
  457. case DataRowState.Modified:
  458. statementType = StatementType.Update;
  459. command = UpdateCommand;
  460. commandName = "Update";
  461. break;
  462. case DataRowState.Unchanged:
  463. continue;
  464. case DataRowState.Detached:
  465. throw new NotImplementedException ();
  466. }
  467. if (command == null)
  468. useCommandBuilder = true;
  469. RowUpdatingEventArgs args = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
  470. OnRowUpdating (args);
  471. if (args.Status == UpdateStatus.ErrorsOccurred)
  472. throw (args.Errors);
  473. if (command == null && args.Command != null)
  474. command = args.Command;
  475. else if (command == null)
  476. throw new InvalidOperationException (String.Format ("Update requires a valid {0}Command when passed a DataRow collection with modified rows.", commandName));
  477. if (!useCommandBuilder) {
  478. DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
  479. foreach (IDataParameter parameter in command.Parameters) {
  480. string dsColumnName = parameter.SourceColumn;
  481. DataColumnMapping mapping = columnMappings [parameter.SourceColumn];
  482. if (mapping != null) dsColumnName = mapping.DataSetColumn;
  483. DataRowVersion rowVersion = DataRowVersion.Default;
  484. // Parameter version is ignored for non-update commands
  485. if (statementType == StatementType.Update)
  486. rowVersion = parameter.SourceVersion;
  487. if (statementType == StatementType.Delete)
  488. rowVersion = DataRowVersion.Original;
  489. parameter.Value = row [dsColumnName, rowVersion];
  490. }
  491. row.AcceptChanges ();
  492. }
  493. if (command.Connection.State == ConnectionState.Closed)
  494. command.Connection.Open ();
  495. try
  496. {
  497. int tmp = command.ExecuteNonQuery ();
  498. // if the execute does not effect any rows we throw an exception.
  499. if (tmp == 0)
  500. throw new DBConcurrencyException("Concurrency violation: the " + commandName +"Command affected 0 records.");
  501. updateCount += tmp;
  502. OnRowUpdated (CreateRowUpdatedEvent (row, command, statementType, tableMapping));
  503. }
  504. catch (Exception e)
  505. {
  506. if (ContinueUpdateOnError)
  507. row.RowError = e.Message;// do somthing with the error
  508. else
  509. throw e;
  510. }
  511. }
  512. return updateCount;
  513. }
  514. public int Update (DataSet dataSet, string sourceTable)
  515. {
  516. MissingMappingAction mappingAction = MissingMappingAction;
  517. if (mappingAction == MissingMappingAction.Ignore)
  518. mappingAction = MissingMappingAction.Error;
  519. DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTable, sourceTable, mappingAction);
  520. DataTable dataTable = dataSet.Tables[tableMapping.DataSetTable];
  521. if (dataTable == null)
  522. throw new ArgumentException ("sourceTable");
  523. return Update (dataTable, tableMapping);
  524. }
  525. protected virtual void OnFillError (FillErrorEventArgs value)
  526. {
  527. if (FillError != null)
  528. FillError (this, value);
  529. }
  530. protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
  531. protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
  532. #endregion // Methods
  533. }
  534. }