2
0

DbDataAdapter.cs 15 KB

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