DbDataAdapter.cs 15 KB

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