DbDataAdapter.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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. object[] itemArray = new object [dataReader.FieldCount];
  88. SetupSchema (SchemaType.Mapped, dataTable.TableName, dataTable); // FIXME
  89. BuildSchema (dataReader, dataTable, SchemaType.Mapped);
  90. while (doContinue && dataReader.Read ()) {
  91. dataReader.GetValues (itemArray);
  92. try {
  93. dataTable.BeginLoadData ();
  94. dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
  95. dataTable.EndLoadData ();
  96. count += 1;
  97. }
  98. catch (Exception e) {
  99. FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e);
  100. OnFillError (args);
  101. doContinue = args.Continue;
  102. }
  103. }
  104. dataReader.Close ();
  105. return count;
  106. }
  107. protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior)
  108. {
  109. return Fill (dataTable, command.ExecuteReader (behavior));
  110. }
  111. public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable)
  112. {
  113. return this.Fill (dataSet, startRecord, maxRecords, srcTable, SelectCommand, CommandBehavior.Default);
  114. }
  115. protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
  116. {
  117. if (startRecord < 0)
  118. throw new ArgumentException ("The startRecord parameter was less than 0.");
  119. if (maxRecords < 0)
  120. throw new ArgumentException ("The maxRecords parameter was less than 0.");
  121. if (dataReader.FieldCount == 0) {
  122. dataReader.Close ();
  123. return 0;
  124. }
  125. DataTable dataTable;
  126. int resultIndex = 0;
  127. int count = 0;
  128. bool doContinue = true;
  129. string tableName = srcTable;
  130. object[] itemArray = new object [dataReader.FieldCount];
  131. do {
  132. dataTable = new DataTable ();
  133. SetupSchema (SchemaType.Mapped, tableName, dataTable);
  134. // check if the table exists in the dataset
  135. if (dataSet.Tables.Contains (dataTable.TableName))
  136. // get the table from the dataset
  137. dataTable = dataSet.Tables [dataTable.TableName];
  138. else
  139. dataSet.Tables.Add (dataTable);
  140. BuildSchema (dataReader, dataTable, SchemaType.Mapped);
  141. for (int i = 0; i < startRecord; i += 1)
  142. dataReader.Read ();
  143. while (doContinue && dataReader.Read () && !(maxRecords > 0 && count >= maxRecords)) {
  144. dataReader.GetValues (itemArray);
  145. try {
  146. dataTable.BeginLoadData ();
  147. dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
  148. dataTable.EndLoadData ();
  149. count += 1;
  150. }
  151. catch (Exception e) {
  152. FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e);
  153. OnFillError (args);
  154. doContinue = args.Continue;
  155. }
  156. }
  157. tableName = String.Format ("{0}{1}", srcTable, ++resultIndex);
  158. startRecord = 0;
  159. maxRecords = 0;
  160. } while (doContinue && dataReader.NextResult ());
  161. dataReader.Close ();
  162. return count;
  163. }
  164. protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
  165. {
  166. CommandBehavior commandBehavior = behavior;
  167. if (command.Connection.State == ConnectionState.Closed) {
  168. command.Connection.Open ();
  169. commandBehavior |= CommandBehavior.CloseConnection;
  170. }
  171. return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords);
  172. }
  173. public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType)
  174. {
  175. return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default);
  176. }
  177. public DataTable FillSchema (DataTable dataTable, SchemaType schemaType)
  178. {
  179. return FillSchema (dataTable, schemaType, SelectCommand, CommandBehavior.Default);
  180. }
  181. public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable)
  182. {
  183. return FillSchema (dataSet, schemaType, SelectCommand, srcTable, CommandBehavior.Default);
  184. }
  185. [MonoTODO ("Verify")]
  186. protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior)
  187. {
  188. DataTable table;
  189. behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
  190. if (command.Connection.State == ConnectionState.Closed) {
  191. command.Connection.Open ();
  192. behavior |= CommandBehavior.CloseConnection;
  193. }
  194. IDataReader reader = command.ExecuteReader (behavior);
  195. table = new DataTable ();
  196. SetupSchema (schemaType, DefaultSourceTableName, table);
  197. BuildSchema (reader, table, schemaType);
  198. reader.Close ();
  199. return table;
  200. }
  201. [MonoTODO ("Verify")]
  202. protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)
  203. {
  204. behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
  205. if (command.Connection.State == ConnectionState.Closed) {
  206. command.Connection.Open ();
  207. behavior |= CommandBehavior.CloseConnection;
  208. }
  209. IDataReader reader = command.ExecuteReader (behavior);
  210. ArrayList output = new ArrayList ();
  211. string tableName = srcTable;
  212. int index = 0;
  213. do {
  214. // FixMe: Allocate table only if it doesn't exist already!
  215. DataTable table = new DataTable ();
  216. SetupSchema (schemaType, tableName, table);
  217. if (dataSet.Tables.Contains (table.TableName))
  218. table = dataSet.Tables [table.TableName];
  219. else
  220. dataSet.Tables.Add (table);
  221. BuildSchema (reader, table, schemaType);
  222. output.Add (table);
  223. tableName = String.Format ("{0}{1}", srcTable, ++index);
  224. } while (reader.NextResult ());
  225. reader.Close ();
  226. return (DataTable[]) output.ToArray (typeof (DataTable));
  227. }
  228. private void SetupSchema (SchemaType schemaType, string sourceTableName, DataTable table)
  229. {
  230. DataTableMapping tableMapping = null;
  231. if (schemaType == SchemaType.Mapped)
  232. tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTableName, "", MissingMappingAction.Ignore);
  233. if (tableMapping != null)
  234. table.TableName = tableMapping.DataSetTable;
  235. else
  236. table.TableName = sourceTableName;
  237. }
  238. [EditorBrowsable (EditorBrowsableState.Advanced)]
  239. public override IDataParameter[] GetFillParameters ()
  240. {
  241. object[] parameters = new object [SelectCommand.Parameters.Count];
  242. SelectCommand.Parameters.CopyTo (parameters, 0);
  243. return (IDataParameter[]) parameters;
  244. }
  245. [MonoTODO ("Test")]
  246. private void BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType)
  247. {
  248. ArrayList primaryKey = new ArrayList ();
  249. ArrayList sourceColumns = new ArrayList ();
  250. foreach (DataRow schemaRow in reader.GetSchemaTable ().Rows) {
  251. // generate a unique column name in the source table.
  252. string sourceColumnName;
  253. if (schemaRow ["ColumnName"].Equals (DBNull.Value))
  254. sourceColumnName = DefaultSourceColumnName;
  255. else
  256. sourceColumnName = (string) schemaRow ["ColumnName"];
  257. string realSourceColumnName = sourceColumnName;
  258. for (int i = 1; sourceColumns.Contains (realSourceColumnName); i += 1)
  259. realSourceColumnName = String.Format ("{0}{1}", sourceColumnName, i);
  260. sourceColumns.Add(realSourceColumnName);
  261. // generate DataSetColumnName from DataTableMapping, if any
  262. string dsColumnName = realSourceColumnName;
  263. DataTableMapping tableMapping = null;
  264. if (schemaType == SchemaType.Mapped)
  265. tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, table.TableName, table.TableName, MissingMappingAction.Ignore);
  266. if (tableMapping != null) {
  267. table.TableName = tableMapping.DataSetTable;
  268. // check to see if the column mapping exists
  269. if (tableMapping.ColumnMappings.Contains (dsColumnName)) {
  270. dsColumnName = tableMapping.ColumnMappings [realSourceColumnName].DataSetColumn;
  271. } else {
  272. if (MissingSchemaAction == MissingSchemaAction.Error)
  273. throw new SystemException ();
  274. tableMapping.ColumnMappings.Add (sourceColumnName, dsColumnName);
  275. }
  276. if (!TableMappings.Contains (tableMapping))
  277. TableMappings.Add (tableMapping);
  278. }
  279. if (!table.Columns.Contains(dsColumnName))
  280. table.Columns.Add (dsColumnName, (Type) schemaRow ["DataType"]);
  281. if (!schemaRow["IsKey"].Equals (DBNull.Value))
  282. if ((bool) (schemaRow ["IsKey"]))
  283. primaryKey.Add (table.Columns [dsColumnName]);
  284. }
  285. if (MissingSchemaAction == MissingSchemaAction.AddWithKey && primaryKey.Count > 0)
  286. table.PrimaryKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn)));
  287. }
  288. [MonoTODO]
  289. object ICloneable.Clone ()
  290. {
  291. throw new NotImplementedException ();
  292. }
  293. [MonoTODO]
  294. public int Update (DataRow[] dataRows)
  295. {
  296. throw new NotImplementedException (); // FIXME: Which mapping?
  297. }
  298. public override int Update (DataSet dataSet)
  299. {
  300. return Update (dataSet, DefaultSourceTableName);
  301. }
  302. public int Update (DataTable dataTable)
  303. {
  304. int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
  305. if (index < 0)
  306. throw new ArgumentException ();
  307. return Update (dataTable, TableMappings [index]);
  308. }
  309. private int Update (DataTable dataTable, DataTableMapping tableMapping)
  310. {
  311. DataRow[] rows = new DataRow [dataTable.Rows.Count];
  312. dataTable.Rows.CopyTo (rows, 0);
  313. return Update (rows, tableMapping);
  314. }
  315. [MonoTODO]
  316. protected virtual int Update (DataRow[] dataRows, DataTableMapping tableMapping)
  317. {
  318. int updateCount = 0;
  319. foreach (DataRow row in dataRows) {
  320. StatementType statementType = StatementType.Update;
  321. IDbCommand command = null;
  322. string commandName = String.Empty;
  323. bool useCommandBuilder = false;
  324. switch (row.RowState) {
  325. case DataRowState.Added:
  326. statementType = StatementType.Insert;
  327. command = InsertCommand;
  328. commandName = "Insert";
  329. break;
  330. case DataRowState.Deleted:
  331. statementType = StatementType.Delete;
  332. command = DeleteCommand;
  333. commandName = "Delete";
  334. break;
  335. case DataRowState.Modified:
  336. statementType = StatementType.Update;
  337. command = UpdateCommand;
  338. commandName = "Update";
  339. break;
  340. case DataRowState.Unchanged:
  341. continue;
  342. case DataRowState.Detached:
  343. throw new NotImplementedException ();
  344. }
  345. if (command == null)
  346. useCommandBuilder = true;
  347. RowUpdatingEventArgs args = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
  348. OnRowUpdating (args);
  349. if (args.Status == UpdateStatus.ErrorsOccurred)
  350. throw (args.Errors);
  351. if (command == null && args.Command != null)
  352. command = args.Command;
  353. else if (command == null)
  354. throw new InvalidOperationException (String.Format ("Update requires a valid {0}Command when passed a DataRow collection with modified rows.", commandName));
  355. if (!useCommandBuilder) {
  356. DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
  357. foreach (IDataParameter parameter in command.Parameters) {
  358. string dsColumnName = parameter.SourceColumn;
  359. DataColumnMapping mapping = columnMappings [parameter.SourceColumn];
  360. if (mapping != null) dsColumnName = mapping.DataSetColumn;
  361. DataRowVersion rowVersion = DataRowVersion.Default;
  362. // Parameter version is ignored for non-update commands
  363. if (statementType == StatementType.Update)
  364. rowVersion = parameter.SourceVersion;
  365. if (statementType == StatementType.Delete)
  366. rowVersion = DataRowVersion.Original;
  367. parameter.Value = row [dsColumnName, rowVersion];
  368. }
  369. row.AcceptChanges ();
  370. }
  371. updateCount += command.ExecuteNonQuery ();
  372. OnRowUpdated (CreateRowUpdatedEvent (row, command, statementType, tableMapping));
  373. }
  374. return updateCount;
  375. }
  376. public int Update (DataSet dataSet, string sourceTable)
  377. {
  378. MissingMappingAction mappingAction = MissingMappingAction;
  379. if (mappingAction == MissingMappingAction.Ignore)
  380. mappingAction = MissingMappingAction.Error;
  381. DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTable, sourceTable, mappingAction);
  382. DataTable dataTable = dataSet.Tables[tableMapping.DataSetTable];
  383. if (dataTable == null)
  384. throw new ArgumentException ("sourceTable");
  385. return Update (dataTable, tableMapping);
  386. }
  387. protected virtual void OnFillError (FillErrorEventArgs value)
  388. {
  389. if (FillError != null)
  390. FillError (this, value);
  391. }
  392. protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
  393. protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
  394. #endregion // Methods
  395. }
  396. }