DbDataAdapter.cs 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbDataAdapter.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. // <owner current="true" primary="false">Microsoft</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.Common {
  9. using System;
  10. using System.ComponentModel;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using System.Data;
  14. using System.Data.ProviderBase;
  15. using System.Diagnostics;
  16. using System.Reflection;
  17. using System.Threading;
  18. public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, ICloneable { // V1.0.3300, MDAC 69629
  19. public const string DefaultSourceTableName = "Table"; // V1.0.3300
  20. internal static readonly object ParameterValueNonNullValue = 0;
  21. internal static readonly object ParameterValueNullValue = 1;
  22. private IDbCommand _deleteCommand, _insertCommand, _selectCommand, _updateCommand;
  23. private CommandBehavior _fillCommandBehavior;
  24. private struct BatchCommandInfo {
  25. internal int CommandIdentifier; // whatever AddToBatch returns, so we can reference the command later in GetBatchedParameter
  26. internal int ParameterCount; // number of parameters on the command, so we know how many to loop over when processing output parameters
  27. internal DataRow Row; // the row that the command is intended to update
  28. internal StatementType StatementType; // the statement type of the command, needed for accept changes
  29. internal UpdateRowSource UpdatedRowSource; // the UpdatedRowSource value from the command, to know whether we need to look for output parameters or not
  30. internal int? RecordsAffected;
  31. internal Exception Errors;
  32. }
  33. protected DbDataAdapter() : base() { // V1.0.3300
  34. }
  35. protected DbDataAdapter(DbDataAdapter adapter) : base(adapter) { // V1.0.5000
  36. CloneFrom(adapter);
  37. }
  38. private IDbDataAdapter _IDbDataAdapter {
  39. get {
  40. return (IDbDataAdapter)this;
  41. }
  42. }
  43. [
  44. Browsable(false),
  45. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  46. ]
  47. public DbCommand DeleteCommand { // V1.2.3300
  48. get {
  49. return (DbCommand)(_IDbDataAdapter.DeleteCommand);
  50. }
  51. set {
  52. _IDbDataAdapter.DeleteCommand = value;
  53. }
  54. }
  55. IDbCommand IDbDataAdapter.DeleteCommand { // V1.2.3300
  56. get {
  57. return _deleteCommand;
  58. }
  59. set {
  60. _deleteCommand = value;
  61. }
  62. }
  63. protected internal CommandBehavior FillCommandBehavior { // V1.2.3300, MDAC 87511
  64. get {
  65. //Bid.Trace("<comm.DbDataAdapter.get_FillCommandBehavior|API> %d#\n", ObjectID);
  66. return (_fillCommandBehavior | CommandBehavior.SequentialAccess);
  67. }
  68. set {
  69. // setting |= SchemaOnly; /* similar to FillSchema (which also uses KeyInfo) */
  70. // setting |= KeyInfo; /* same as MissingSchemaAction.AddWithKey */
  71. // setting |= SequentialAccess; /* required and always present */
  72. // setting |= CloseConnection; /* close connection regardless of start condition */
  73. _fillCommandBehavior = (value | CommandBehavior.SequentialAccess);
  74. //Bid.Trace("<comm.DbDataAdapter.set_FillCommandBehavior|API> %d#, %d{ds.CommandBehavior}\n", (int)value);
  75. }
  76. }
  77. [
  78. Browsable(false),
  79. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  80. ]
  81. public DbCommand InsertCommand { // V1.2.3300
  82. get {
  83. return (DbCommand)(_IDbDataAdapter.InsertCommand);
  84. }
  85. set {
  86. _IDbDataAdapter.InsertCommand = value;
  87. }
  88. }
  89. IDbCommand IDbDataAdapter.InsertCommand { // V1.2.3300
  90. get {
  91. return _insertCommand;
  92. }
  93. set {
  94. _insertCommand = value;
  95. }
  96. }
  97. [
  98. Browsable(false),
  99. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  100. ]
  101. public DbCommand SelectCommand { // V1.2.3300
  102. get {
  103. return (DbCommand)(_IDbDataAdapter.SelectCommand);
  104. }
  105. set {
  106. _IDbDataAdapter.SelectCommand = value;
  107. }
  108. }
  109. IDbCommand IDbDataAdapter.SelectCommand { // V1.2.3300
  110. get {
  111. return _selectCommand;
  112. }
  113. set {
  114. _selectCommand = value;
  115. }
  116. }
  117. [
  118. DefaultValue(1),
  119. ResCategoryAttribute(Res.DataCategory_Update),
  120. ResDescriptionAttribute(Res.DbDataAdapter_UpdateBatchSize),
  121. ]
  122. virtual public int UpdateBatchSize {
  123. get {
  124. return 1;
  125. }
  126. set {
  127. if (1 != value) {
  128. throw ADP.NotSupported();
  129. }
  130. }
  131. }
  132. [
  133. Browsable(false),
  134. DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
  135. ]
  136. public DbCommand UpdateCommand { // V1.2.3300
  137. get {
  138. return (DbCommand)(_IDbDataAdapter.UpdateCommand);
  139. }
  140. set {
  141. _IDbDataAdapter.UpdateCommand = value;
  142. }
  143. }
  144. IDbCommand IDbDataAdapter.UpdateCommand { // V1.2.3300
  145. get {
  146. return _updateCommand;
  147. }
  148. set {
  149. _updateCommand = value;
  150. }
  151. }
  152. private System.Data.MissingMappingAction UpdateMappingAction {
  153. get {
  154. if (System.Data.MissingMappingAction.Passthrough == MissingMappingAction) {
  155. return System.Data.MissingMappingAction.Passthrough;
  156. }
  157. return System.Data.MissingMappingAction.Error;
  158. }
  159. }
  160. private System.Data.MissingSchemaAction UpdateSchemaAction {
  161. get {
  162. System.Data.MissingSchemaAction action = MissingSchemaAction;
  163. if ((System.Data.MissingSchemaAction.Add == action) || (System.Data.MissingSchemaAction.AddWithKey == action)) {
  164. return System.Data.MissingSchemaAction.Ignore;
  165. }
  166. return System.Data.MissingSchemaAction.Error;
  167. }
  168. }
  169. protected virtual int AddToBatch(IDbCommand command) {
  170. // Called to add a single command to the batch of commands that need
  171. // to be executed as a batch, when batch updates are requested. It
  172. // must return an identifier that can be used to identify the command
  173. // to GetBatchedParameter later.
  174. throw ADP.NotSupported();
  175. }
  176. virtual protected void ClearBatch() {
  177. // Called when batch updates are requested to clear out the contents
  178. // of the batch, whether or not it's been executed.
  179. throw ADP.NotSupported();
  180. }
  181. object ICloneable.Clone() { // V1.0.3300, MDAC 69629
  182. #pragma warning disable 618 // ignore obsolete warning about CloneInternals
  183. DbDataAdapter clone = (DbDataAdapter)CloneInternals();
  184. #pragma warning restore 618
  185. clone.CloneFrom(this);
  186. return clone;
  187. }
  188. private void CloneFrom(DbDataAdapter from) {
  189. IDbDataAdapter pfrom = from._IDbDataAdapter;
  190. _IDbDataAdapter.SelectCommand = CloneCommand(pfrom.SelectCommand);
  191. _IDbDataAdapter.InsertCommand = CloneCommand(pfrom.InsertCommand);
  192. _IDbDataAdapter.UpdateCommand = CloneCommand(pfrom.UpdateCommand);
  193. _IDbDataAdapter.DeleteCommand = CloneCommand(pfrom.DeleteCommand);
  194. }
  195. private IDbCommand CloneCommand(IDbCommand command) {
  196. return (IDbCommand) ((command is ICloneable) ? ((ICloneable) command).Clone() : null);
  197. }
  198. virtual protected RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) { // V1.0.3300
  199. return new RowUpdatedEventArgs(dataRow, command, statementType, tableMapping);
  200. }
  201. virtual protected RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) { // V1.0.3300
  202. return new RowUpdatingEventArgs(dataRow, command, statementType, tableMapping);
  203. }
  204. override protected void Dispose(bool disposing) { // V1.0.3300, MDAC 69629
  205. if (disposing) { // release mananged objects
  206. IDbDataAdapter pthis = (IDbDataAdapter) this; // must cast to interface to obtain correct value
  207. pthis.SelectCommand = null;
  208. pthis.InsertCommand = null;
  209. pthis.UpdateCommand = null;
  210. pthis.DeleteCommand = null;
  211. }
  212. // release unmanaged objects
  213. base.Dispose(disposing); // notify base classes
  214. }
  215. protected virtual int ExecuteBatch() {
  216. // Called to execute the batched update command, returns the number
  217. // of rows affected, just as ExecuteNonQuery would.
  218. throw ADP.NotSupported();
  219. }
  220. public DataTable FillSchema(DataTable dataTable, SchemaType schemaType) { // V1.0.3300
  221. IntPtr hscp;
  222. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.FillSchema|API> %d#, dataTable, schemaType=%d{ds.SchemaType}\n", ObjectID, (int)schemaType);
  223. try {
  224. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  225. CommandBehavior cmdBehavior = FillCommandBehavior;
  226. return FillSchema(dataTable, schemaType, selectCmd, cmdBehavior); // MDAC 67666
  227. }
  228. finally {
  229. Bid.ScopeLeave(ref hscp);
  230. }
  231. }
  232. override public DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType) { // V1.0.3300
  233. IntPtr hscp;
  234. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.FillSchema|API> %d#, dataSet, schemaType=%d{ds.SchemaType}\n", ObjectID, (int)schemaType);
  235. try {
  236. IDbCommand command = _IDbDataAdapter.SelectCommand;
  237. if (DesignMode && ((null == command) || (null == command.Connection) || ADP.IsEmpty(command.CommandText))) {
  238. return new DataTable[0]; // design-time support
  239. }
  240. CommandBehavior cmdBehavior = FillCommandBehavior;
  241. return FillSchema(dataSet, schemaType, command, DbDataAdapter.DefaultSourceTableName, cmdBehavior);
  242. }
  243. finally {
  244. Bid.ScopeLeave(ref hscp);
  245. }
  246. }
  247. public DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType, string srcTable) { // V1.0.3300
  248. IntPtr hscp;
  249. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.FillSchema|API> %d#, dataSet, schemaType=%d{ds.SchemaType}, srcTable=%ls%\n", ObjectID, (int)schemaType, srcTable);
  250. try {
  251. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  252. CommandBehavior cmdBehavior = FillCommandBehavior;
  253. return FillSchema(dataSet, schemaType, selectCmd, srcTable, cmdBehavior);
  254. }
  255. finally {
  256. Bid.ScopeLeave(ref hscp);
  257. }
  258. }
  259. virtual protected DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior) { // V1.0.3300
  260. IntPtr hscp;
  261. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.FillSchema|API> %d#, dataSet, schemaType, command, srcTable, behavior=%d{ds.CommandBehavior}\n", ObjectID, (int)behavior);
  262. try {
  263. if (null == dataSet) {
  264. throw ADP.ArgumentNull("dataSet");
  265. }
  266. if ((SchemaType.Source != schemaType) && (SchemaType.Mapped != schemaType)) {
  267. throw ADP.InvalidSchemaType(schemaType);
  268. }
  269. if (ADP.IsEmpty(srcTable)) {
  270. throw ADP.FillSchemaRequiresSourceTableName("srcTable");
  271. }
  272. if (null == command) {
  273. throw ADP.MissingSelectCommand(ADP.FillSchema);
  274. }
  275. return (DataTable[]) FillSchemaInternal(dataSet, null, schemaType, command, srcTable, behavior);
  276. } finally {
  277. Bid.ScopeLeave(ref hscp);
  278. }
  279. }
  280. virtual protected DataTable FillSchema(DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior) { // V1.0.3300
  281. IntPtr hscp;
  282. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.FillSchema|API> %d#, dataTable, schemaType, command, behavior=%d{ds.CommandBehavior}\n", ObjectID, (int)behavior);
  283. try {
  284. if (null == dataTable) {
  285. throw ADP.ArgumentNull("dataTable");
  286. }
  287. if ((SchemaType.Source != schemaType) && (SchemaType.Mapped != schemaType)) {
  288. throw ADP.InvalidSchemaType(schemaType);
  289. }
  290. if (null == command) {
  291. throw ADP.MissingSelectCommand(ADP.FillSchema);
  292. }
  293. string srcTableName = dataTable.TableName;
  294. int index = IndexOfDataSetTable(srcTableName);
  295. if (-1 != index) {
  296. srcTableName = TableMappings[index].SourceTable;
  297. }
  298. return (DataTable) FillSchemaInternal(null, dataTable, schemaType, command, srcTableName, behavior | CommandBehavior.SingleResult);
  299. } finally {
  300. Bid.ScopeLeave(ref hscp);
  301. }
  302. }
  303. private object FillSchemaInternal(DataSet dataset, DataTable datatable, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior) {
  304. object dataTables = null;
  305. bool restoreNullConnection = (null == command.Connection);
  306. try {
  307. IDbConnection activeConnection = DbDataAdapter.GetConnection3(this, command, ADP.FillSchema);
  308. ConnectionState originalState = ConnectionState.Open;
  309. try {
  310. QuietOpen(activeConnection, out originalState);
  311. using(IDataReader dataReader = command.ExecuteReader(behavior | CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo)) {
  312. if (null != datatable) { // delegate to next set of protected FillSchema methods
  313. dataTables = FillSchema(datatable, schemaType, dataReader);
  314. }
  315. else {
  316. dataTables = FillSchema(dataset, schemaType, srcTable, dataReader);
  317. }
  318. }
  319. }
  320. finally {
  321. QuietClose(activeConnection, originalState);
  322. }
  323. }
  324. finally {
  325. if (restoreNullConnection) {
  326. command.Transaction = null;
  327. command.Connection = null;
  328. }
  329. }
  330. return dataTables;
  331. }
  332. override public int Fill(DataSet dataSet) { // V1.0.3300
  333. IntPtr hscp;
  334. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, dataSet\n", ObjectID);
  335. try {
  336. // delegate to Fill4
  337. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  338. CommandBehavior cmdBehavior = FillCommandBehavior;
  339. return Fill(dataSet, 0, 0, DbDataAdapter.DefaultSourceTableName, selectCmd, cmdBehavior);
  340. }
  341. finally {
  342. Bid.ScopeLeave(ref hscp);
  343. }
  344. }
  345. public int Fill(DataSet dataSet, string srcTable) { // V1.0.3300
  346. IntPtr hscp;
  347. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, dataSet, srcTable='%ls'\n", ObjectID, srcTable);
  348. try {
  349. // delegate to Fill4
  350. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  351. CommandBehavior cmdBehavior = FillCommandBehavior;
  352. return Fill(dataSet, 0, 0, srcTable, selectCmd, cmdBehavior);
  353. }
  354. finally {
  355. Bid.ScopeLeave(ref hscp);
  356. }
  357. }
  358. public int Fill(DataSet dataSet, int startRecord, int maxRecords, string srcTable) { // V1.0.3300
  359. IntPtr hscp;
  360. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, dataSet, startRecord=%d, maxRecords=%d, srcTable='%ls'\n", ObjectID, startRecord, maxRecords, srcTable);
  361. try {
  362. // delegate to Fill4
  363. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  364. CommandBehavior cmdBehavior = FillCommandBehavior;
  365. return Fill(dataSet, startRecord, maxRecords, srcTable, selectCmd, cmdBehavior);
  366. }
  367. finally {
  368. Bid.ScopeLeave(ref hscp);
  369. }
  370. }
  371. virtual protected int Fill(DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior) { // V1.0.3300
  372. IntPtr hscp;
  373. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, dataSet, startRecord, maxRecords, srcTable, command, behavior=%d{ds.CommandBehavior}\n", ObjectID, (int)behavior);
  374. try {
  375. if (null == dataSet) {
  376. throw ADP.FillRequires("dataSet");
  377. }
  378. if (startRecord < 0) {
  379. throw ADP.InvalidStartRecord("startRecord", startRecord);
  380. }
  381. if (maxRecords < 0) {
  382. throw ADP.InvalidMaxRecords("maxRecords", maxRecords);
  383. }
  384. if (ADP.IsEmpty(srcTable)) {
  385. throw ADP.FillRequiresSourceTableName("srcTable");
  386. }
  387. if (null == command) {
  388. throw ADP.MissingSelectCommand(ADP.Fill);
  389. }
  390. return FillInternal(dataSet, null, startRecord, maxRecords, srcTable, command, behavior);
  391. }finally {
  392. Bid.ScopeLeave(ref hscp);
  393. }
  394. }
  395. public int Fill(DataTable dataTable) { // V1.0.3300
  396. IntPtr hscp;
  397. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, dataTable\n", ObjectID);
  398. try {
  399. // delegate to Fill8
  400. DataTable[] dataTables = new DataTable[1] { dataTable};
  401. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  402. CommandBehavior cmdBehavior = FillCommandBehavior;
  403. return Fill(dataTables, 0, 0, selectCmd, cmdBehavior);
  404. }
  405. finally {
  406. Bid.ScopeLeave(ref hscp);
  407. }
  408. }
  409. public int Fill(int startRecord, int maxRecords, params DataTable[] dataTables) { // V1.2.3300
  410. IntPtr hscp;
  411. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, startRecord=%d, maxRecords=%d, dataTable[]\n", ObjectID, startRecord, maxRecords);
  412. try {
  413. // delegate to Fill8
  414. IDbCommand selectCmd = _IDbDataAdapter.SelectCommand;
  415. CommandBehavior cmdBehavior = FillCommandBehavior;
  416. return Fill(dataTables, startRecord, maxRecords, selectCmd, cmdBehavior);
  417. }
  418. finally {
  419. Bid.ScopeLeave(ref hscp);
  420. }
  421. }
  422. virtual protected int Fill(DataTable dataTable, IDbCommand command, CommandBehavior behavior) { // V1.0.3300
  423. IntPtr hscp;
  424. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> dataTable, command, behavior=%d{ds.CommandBehavior}%d#\n", ObjectID, (int)behavior);
  425. try {
  426. // delegate to Fill8
  427. DataTable[] dataTables = new DataTable[1] { dataTable};
  428. return Fill(dataTables, 0, 0, command, behavior);
  429. }
  430. finally {
  431. Bid.ScopeLeave(ref hscp);
  432. }
  433. }
  434. virtual protected int Fill(DataTable[] dataTables, int startRecord, int maxRecords, IDbCommand command, CommandBehavior behavior) { // V1.2.3300
  435. IntPtr hscp;
  436. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Fill|API> %d#, dataTables[], startRecord, maxRecords, command, behavior=%d{ds.CommandBehavior}\n", ObjectID, (int)behavior);
  437. try {
  438. if ((null == dataTables) || (0 == dataTables.Length) || (null == dataTables[0])) {
  439. throw ADP.FillRequires("dataTable");
  440. }
  441. if (startRecord < 0) {
  442. throw ADP.InvalidStartRecord("startRecord", startRecord);
  443. }
  444. if (maxRecords < 0) {
  445. throw ADP.InvalidMaxRecords("maxRecords", maxRecords);
  446. }
  447. if ((1 < dataTables.Length) && ((0 != startRecord) || (0 != maxRecords))) {
  448. throw ADP.OnlyOneTableForStartRecordOrMaxRecords();
  449. }
  450. if (null == command) {
  451. throw ADP.MissingSelectCommand(ADP.Fill);
  452. }
  453. if (1 == dataTables.Length) {
  454. behavior |= CommandBehavior.SingleResult;
  455. }
  456. return FillInternal(null, dataTables, startRecord, maxRecords, null, command, behavior);
  457. }
  458. finally {
  459. Bid.ScopeLeave(ref hscp);
  460. }
  461. }
  462. private int FillInternal(DataSet dataset, DataTable[] datatables, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior) {
  463. int rowsAddedToDataSet = 0;
  464. bool restoreNullConnection = (null == command.Connection);
  465. try {
  466. IDbConnection activeConnection = DbDataAdapter.GetConnection3(this, command, ADP.Fill);
  467. ConnectionState originalState = ConnectionState.Open;
  468. // the default is MissingSchemaAction.Add, the user must explicitly
  469. // set MisingSchemaAction.AddWithKey to get key information back in the dataset
  470. if (Data.MissingSchemaAction.AddWithKey == MissingSchemaAction) {
  471. behavior |= CommandBehavior.KeyInfo;
  472. }
  473. try {
  474. QuietOpen(activeConnection, out originalState);
  475. behavior |= CommandBehavior.SequentialAccess;
  476. IDataReader dataReader = null;
  477. try {
  478. dataReader = command.ExecuteReader(behavior);
  479. if (null != datatables) { // delegate to next set of protected Fill methods
  480. rowsAddedToDataSet = Fill(datatables, dataReader, startRecord, maxRecords);
  481. }
  482. else {
  483. rowsAddedToDataSet = Fill(dataset, srcTable, dataReader, startRecord, maxRecords);
  484. }
  485. }
  486. finally {
  487. if (null != dataReader) {
  488. dataReader.Dispose();
  489. }
  490. }
  491. }
  492. finally {
  493. QuietClose(activeConnection, originalState);
  494. }
  495. }
  496. finally {
  497. if (restoreNullConnection) {
  498. command.Transaction = null;
  499. command.Connection = null;
  500. }
  501. }
  502. return rowsAddedToDataSet;
  503. }
  504. virtual protected IDataParameter GetBatchedParameter(int commandIdentifier, int parameterIndex) {
  505. // Called to retrieve a parameter from a specific bached command, the
  506. // first argument is the value that was returned by AddToBatch when it
  507. // was called for the command.
  508. throw ADP.NotSupported();
  509. }
  510. virtual protected bool GetBatchedRecordsAffected(int commandIdentifier, out int recordsAffected, out Exception error) { // SQLBU 412467
  511. // Called to retrieve the records affected from a specific batched command,
  512. // first argument is the value that was returned by AddToBatch when it
  513. // was called for the command.
  514. // default implementation always returns 1, derived classes override for otherwise
  515. // otherwise DbConcurrencyException will only be thrown if sum of all records in batch is 0
  516. // return 0 to cause Update to throw DbConcurrencyException
  517. recordsAffected = 1;
  518. error = null;
  519. return true;
  520. }
  521. [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
  522. override public IDataParameter[] GetFillParameters() { // V1.0.3300
  523. IDataParameter[] value = null;
  524. IDbCommand select = _IDbDataAdapter.SelectCommand;
  525. if (null != select) {
  526. IDataParameterCollection parameters = select.Parameters;
  527. if (null != parameters) {
  528. value = new IDataParameter[parameters.Count];
  529. parameters.CopyTo(value, 0);
  530. }
  531. }
  532. if (null == value) {
  533. value = new IDataParameter[0];
  534. }
  535. return value;
  536. }
  537. internal DataTableMapping GetTableMapping(DataTable dataTable) {
  538. DataTableMapping tableMapping = null;
  539. int index = IndexOfDataSetTable(dataTable.TableName);
  540. if (-1 != index) {
  541. tableMapping = TableMappings[index];
  542. }
  543. if (null == tableMapping) {
  544. if (System.Data.MissingMappingAction.Error == MissingMappingAction) {
  545. throw ADP.MissingTableMappingDestination(dataTable.TableName);
  546. }
  547. tableMapping = new DataTableMapping(dataTable.TableName, dataTable.TableName);
  548. }
  549. return tableMapping;
  550. }
  551. virtual protected void InitializeBatching() {
  552. // Called when batch updates are requested to prepare for processing
  553. // of a batch of commands.
  554. throw ADP.NotSupported();
  555. }
  556. virtual protected void OnRowUpdated(RowUpdatedEventArgs value) { // V1.0.3300
  557. }
  558. virtual protected void OnRowUpdating(RowUpdatingEventArgs value) { // V1.0.3300
  559. }
  560. private void ParameterInput(IDataParameterCollection parameters, StatementType typeIndex, DataRow row, DataTableMapping mappings) {
  561. Data.MissingMappingAction missingMapping = UpdateMappingAction;
  562. Data.MissingSchemaAction missingSchema = UpdateSchemaAction;
  563. foreach(IDataParameter parameter in parameters) {
  564. if ((null != parameter) && (0 != (ParameterDirection.Input & parameter.Direction))) {
  565. string columnName = parameter.SourceColumn;
  566. if (!ADP.IsEmpty(columnName)) {
  567. DataColumn dataColumn = mappings.GetDataColumn(columnName, null, row.Table, missingMapping, missingSchema);
  568. if (null != dataColumn) {
  569. DataRowVersion version = DbDataAdapter.GetParameterSourceVersion(typeIndex, parameter);
  570. parameter.Value = row[dataColumn, version];
  571. }
  572. else {
  573. parameter.Value = null;
  574. }
  575. DbParameter dbparameter = (parameter as DbParameter);
  576. if ((null != dbparameter) && dbparameter.SourceColumnNullMapping) {
  577. Debug.Assert(DbType.Int32 == parameter.DbType, "unexpected DbType");
  578. parameter.Value = ADP.IsNull(parameter.Value) ? ParameterValueNullValue : ParameterValueNonNullValue;
  579. }
  580. }
  581. }
  582. }
  583. }
  584. private void ParameterOutput(IDataParameter parameter, DataRow row, DataTableMapping mappings, MissingMappingAction missingMapping, MissingSchemaAction missingSchema) {
  585. if (0 != (ParameterDirection.Output & parameter.Direction)) {
  586. object value = parameter.Value;
  587. if (null != value) {
  588. // null means default, meaning we leave the current DataRow value alone
  589. string columnName = parameter.SourceColumn;
  590. if (!ADP.IsEmpty(columnName)) {
  591. DataColumn dataColumn = mappings.GetDataColumn(columnName, null, row.Table, missingMapping, missingSchema);
  592. if (null != dataColumn) {
  593. if (dataColumn.ReadOnly) {
  594. try {
  595. dataColumn.ReadOnly = false;
  596. row[dataColumn] = value;
  597. }
  598. finally {
  599. dataColumn.ReadOnly = true;
  600. }
  601. }
  602. else {
  603. row[dataColumn] = value;
  604. }
  605. }
  606. }
  607. }
  608. }
  609. }
  610. private void ParameterOutput(IDataParameterCollection parameters, DataRow row, DataTableMapping mappings) {
  611. Data.MissingMappingAction missingMapping = UpdateMappingAction;
  612. Data.MissingSchemaAction missingSchema = UpdateSchemaAction;
  613. foreach(IDataParameter parameter in parameters) {
  614. if (null != parameter) {
  615. ParameterOutput(parameter, row, mappings, missingMapping, missingSchema);
  616. }
  617. }
  618. }
  619. virtual protected void TerminateBatching() {
  620. // Called when batch updates are requested to cleanup after a batch
  621. // update has been completed.
  622. throw ADP.NotSupported();
  623. }
  624. override public int Update(DataSet dataSet) { // V1.0.3300
  625. //if (!TableMappings.Contains(DbDataAdapter.DefaultSourceTableName)) { // MDAC 59268
  626. // throw ADP.UpdateRequiresSourceTable(DbDataAdapter.DefaultSourceTableName);
  627. //}
  628. return Update(dataSet, DbDataAdapter.DefaultSourceTableName);
  629. }
  630. public int Update(DataRow[] dataRows) { // V1.0.3300
  631. IntPtr hscp;
  632. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Update|API> %d#, dataRows[]\n", ObjectID);
  633. try {
  634. int rowsAffected = 0;
  635. if (null == dataRows) {
  636. throw ADP.ArgumentNull("dataRows");
  637. }
  638. else if (0 != dataRows.Length) {
  639. DataTable dataTable = null;
  640. for (int i = 0; i < dataRows.Length; ++i) {
  641. if ((null != dataRows[i]) && (dataTable != dataRows[i].Table)) {
  642. if (null != dataTable) {
  643. throw ADP.UpdateMismatchRowTable(i);
  644. }
  645. dataTable = dataRows[i].Table;
  646. }
  647. }
  648. if (null != dataTable) {
  649. DataTableMapping tableMapping = GetTableMapping(dataTable);
  650. rowsAffected = Update(dataRows, tableMapping);
  651. }
  652. }
  653. return rowsAffected;
  654. }
  655. finally {
  656. Bid.ScopeLeave(ref hscp);
  657. }
  658. }
  659. public int Update(DataTable dataTable) { // V1.0.3300
  660. IntPtr hscp;
  661. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Update|API> %d#, dataTable", ObjectID);
  662. try {
  663. if (null == dataTable) {
  664. throw ADP.UpdateRequiresDataTable("dataTable");
  665. }
  666. DataTableMapping tableMapping = null;
  667. int index = IndexOfDataSetTable(dataTable.TableName);
  668. if (-1 != index) {
  669. tableMapping = TableMappings[index];
  670. }
  671. if (null == tableMapping) {
  672. if (System.Data.MissingMappingAction.Error == MissingMappingAction) {
  673. throw ADP.MissingTableMappingDestination(dataTable.TableName);
  674. }
  675. tableMapping = new DataTableMapping(DbDataAdapter.DefaultSourceTableName, dataTable.TableName);
  676. }
  677. return UpdateFromDataTable(dataTable, tableMapping);
  678. } finally {
  679. Bid.ScopeLeave(ref hscp);
  680. }
  681. }
  682. public int Update(DataSet dataSet, string srcTable) { // V1.0.3300
  683. IntPtr hscp;
  684. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Update|API> %d#, dataSet, srcTable='%ls'", ObjectID, srcTable);
  685. try {
  686. if (null == dataSet) {
  687. throw ADP.UpdateRequiresNonNullDataSet("dataSet");
  688. }
  689. if (ADP.IsEmpty(srcTable)) {
  690. throw ADP.UpdateRequiresSourceTableName("srcTable");
  691. }
  692. #if DEBUG
  693. //ADP.TraceDataSet("Update <" + srcTable + ">", dataSet);
  694. #endif
  695. int rowsAffected = 0;
  696. System.Data.MissingMappingAction missingMapping = UpdateMappingAction;
  697. DataTableMapping tableMapping = GetTableMappingBySchemaAction(srcTable, srcTable, UpdateMappingAction);
  698. Debug.Assert(null != tableMapping, "null TableMapping when MissingMappingAction.Error");
  699. // the ad-hoc scenario of no dataTable just returns
  700. // ad-hoc scenario is defined as MissingSchemaAction.Add or MissingSchemaAction.Ignore
  701. System.Data.MissingSchemaAction schemaAction = UpdateSchemaAction;
  702. DataTable dataTable = tableMapping.GetDataTableBySchemaAction(dataSet, schemaAction);
  703. if (null != dataTable) {
  704. rowsAffected = UpdateFromDataTable(dataTable, tableMapping);
  705. }
  706. else if (!HasTableMappings() || (-1 == TableMappings.IndexOf(tableMapping))) {
  707. //throw error since the user didn't explicitly map this tableName to Ignore.
  708. throw ADP.UpdateRequiresSourceTable(srcTable); // MDAC 72681
  709. }
  710. return rowsAffected;
  711. }
  712. finally {
  713. Bid.ScopeLeave(ref hscp);
  714. }
  715. }
  716. virtual protected int Update(DataRow[] dataRows, DataTableMapping tableMapping) { // V1.0.3300
  717. IntPtr hscp;
  718. Bid.ScopeEnter(out hscp, "<comm.DbDataAdapter.Update|API> %d#, dataRows[], tableMapping", ObjectID);
  719. try {
  720. Debug.Assert((null != dataRows) && (0 < dataRows.Length), "Update: bad dataRows");
  721. Debug.Assert(null != tableMapping, "Update: bad DataTableMapping");
  722. // If records were affected, increment row count by one - that is number of rows affected in dataset.
  723. int cumulativeDataRowsAffected = 0;
  724. IDbConnection[] connections = new IDbConnection[5]; // one for each statementtype
  725. ConnectionState[] connectionStates = new ConnectionState[5]; // closed by default (== 0)
  726. bool useSelectConnectionState = false; // MDAC 58710
  727. IDbCommand tmpcmd = _IDbDataAdapter.SelectCommand;
  728. if (null != tmpcmd) {
  729. connections[0] = tmpcmd.Connection;
  730. if (null != connections[0]) {
  731. connectionStates[0] = connections[0].State;
  732. useSelectConnectionState = true;
  733. }
  734. }
  735. int maxBatchCommands = Math.Min(UpdateBatchSize, dataRows.Length);
  736. if (maxBatchCommands < 1) { // batch size of zero indicates one batch, no matter how large...
  737. maxBatchCommands = dataRows.Length;
  738. }
  739. BatchCommandInfo[] batchCommands = new BatchCommandInfo[maxBatchCommands];
  740. DataRow[] rowBatch = new DataRow[maxBatchCommands];
  741. int commandCount = 0;
  742. // the outer try/finally is for closing any connections we may have opened
  743. try {
  744. try {
  745. if (1 != maxBatchCommands) {
  746. InitializeBatching();
  747. }
  748. StatementType statementType = StatementType.Select;
  749. IDbCommand dataCommand = null;
  750. // for each row which is either insert, update, or delete
  751. foreach(DataRow dataRow in dataRows) {
  752. if (null == dataRow) {
  753. continue; // foreach DataRow
  754. }
  755. bool isCommandFromRowUpdating = false;
  756. // obtain the appropriate command
  757. switch (dataRow.RowState) {
  758. case DataRowState.Detached:
  759. case DataRowState.Unchanged:
  760. continue; // foreach DataRow
  761. case DataRowState.Added:
  762. statementType = StatementType.Insert;
  763. dataCommand = _IDbDataAdapter.InsertCommand;
  764. break;
  765. case DataRowState.Deleted:
  766. statementType = StatementType.Delete;
  767. dataCommand = _IDbDataAdapter.DeleteCommand;
  768. break;
  769. case DataRowState.Modified:
  770. statementType = StatementType.Update;
  771. dataCommand = _IDbDataAdapter.UpdateCommand;
  772. break;
  773. default:
  774. Debug.Assert(false, "InvalidDataRowState");
  775. throw ADP.InvalidDataRowState(dataRow.RowState); // out of Update without completing batch
  776. }
  777. // setup the event to be raised
  778. RowUpdatingEventArgs rowUpdatingEvent = CreateRowUpdatingEvent(dataRow, dataCommand, statementType, tableMapping);
  779. // this try/catch for any exceptions during the parameter initialization
  780. try {
  781. dataRow.RowError = null; // MDAC 67185
  782. if (null != dataCommand) {
  783. // prepare the parameters for the user who then can modify them during OnRowUpdating
  784. ParameterInput(dataCommand.Parameters, statementType, dataRow, tableMapping);
  785. }
  786. }
  787. catch (Exception e) {
  788. //
  789. if (!ADP.IsCatchableExceptionType(e)) {
  790. throw;
  791. }
  792. ADP.TraceExceptionForCapture(e);
  793. rowUpdatingEvent.Errors = e;
  794. rowUpdatingEvent.Status = UpdateStatus.ErrorsOccurred;
  795. }
  796. OnRowUpdating(rowUpdatingEvent); // user may throw out of Update without completing batch
  797. IDbCommand tmpCommand = rowUpdatingEvent.Command;
  798. isCommandFromRowUpdating = (dataCommand != tmpCommand);
  799. dataCommand = tmpCommand;
  800. tmpCommand = null;
  801. // handle the status from RowUpdating event
  802. UpdateStatus rowUpdatingStatus = rowUpdatingEvent.Status;
  803. if (UpdateStatus.Continue != rowUpdatingStatus) {
  804. if (UpdateStatus.ErrorsOccurred == rowUpdatingStatus) {
  805. UpdatingRowStatusErrors(rowUpdatingEvent, dataRow);
  806. continue; // foreach DataRow
  807. }
  808. else if (UpdateStatus.SkipCurrentRow == rowUpdatingStatus) {
  809. if (DataRowState.Unchanged == dataRow.RowState) { // MDAC 66286
  810. cumulativeDataRowsAffected++;
  811. }
  812. continue; // foreach DataRow
  813. }
  814. else if (UpdateStatus.SkipAllRemainingRows == rowUpdatingStatus) {
  815. if (DataRowState.Unchanged == dataRow.RowState) { // MDAC 66286
  816. cumulativeDataRowsAffected++;
  817. }
  818. break; // execute existing batch and return
  819. }
  820. else {
  821. throw ADP.InvalidUpdateStatus(rowUpdatingStatus); // out of Update
  822. }
  823. }
  824. // else onward to Append/ExecuteNonQuery/ExecuteReader
  825. rowUpdatingEvent = null;
  826. RowUpdatedEventArgs rowUpdatedEvent = null;
  827. if (1 == maxBatchCommands) {
  828. if (null != dataCommand) {
  829. batchCommands[0].CommandIdentifier = 0;
  830. batchCommands[0].ParameterCount = dataCommand.Parameters.Count;
  831. batchCommands[0].StatementType = statementType;
  832. batchCommands[0].UpdatedRowSource = dataCommand.UpdatedRowSource;
  833. }
  834. batchCommands[0].Row = dataRow;
  835. rowBatch[0] = dataRow; // not doing a batch update, just simplifying code...
  836. commandCount = 1;
  837. }
  838. else {
  839. Exception errors = null;
  840. try {
  841. if (null != dataCommand) {
  842. if (0 == (UpdateRowSource.FirstReturnedRecord & dataCommand.UpdatedRowSource)) {
  843. // append the command to the commandset. If an exception
  844. // occurs, then the user must append and continue
  845. batchCommands[commandCount].CommandIdentifier = AddToBatch(dataCommand);
  846. batchCommands[commandCount].ParameterCount = dataCommand.Parameters.Count;
  847. batchCommands[commandCount].Row = dataRow;
  848. batchCommands[commandCount].StatementType = statementType;
  849. batchCommands[commandCount].UpdatedRowSource = dataCommand.UpdatedRowSource;
  850. rowBatch[commandCount] = dataRow;
  851. commandCount++;
  852. if (commandCount < maxBatchCommands) {
  853. continue; // foreach DataRow
  854. }
  855. // else onward execute the batch
  856. }
  857. else {
  858. // do not allow the expectation that returned results will be used
  859. errors = ADP.ResultsNotAllowedDuringBatch();
  860. }
  861. }
  862. else {
  863. // null Command will force RowUpdatedEvent with ErrorsOccured without completing batch
  864. errors = ADP.UpdateRequiresCommand(statementType, isCommandFromRowUpdating);
  865. }
  866. }
  867. catch (Exception e) { // try/catch for RowUpdatedEventArgs
  868. //
  869. if (!ADP.IsCatchableExceptionType(e)) {
  870. throw;
  871. }
  872. ADP.TraceExceptionForCapture(e);
  873. errors = e;
  874. }
  875. if (null != errors) {
  876. rowUpdatedEvent = CreateRowUpdatedEvent(dataRow, dataCommand, StatementType.Batch, tableMapping);
  877. rowUpdatedEvent.Errors = errors;
  878. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  879. OnRowUpdated(rowUpdatedEvent); // user may throw out of Update
  880. if (errors != rowUpdatedEvent.Errors) { // user set the error msg and we will use it
  881. for(int i = 0; i < batchCommands.Length; ++i) {
  882. batchCommands[i].Errors = null;
  883. }
  884. }
  885. cumulativeDataRowsAffected += UpdatedRowStatus(rowUpdatedEvent, batchCommands, commandCount);
  886. if (UpdateStatus.SkipAllRemainingRows == rowUpdatedEvent.Status) {
  887. break;
  888. }
  889. continue; // foreach datarow
  890. }
  891. }
  892. rowUpdatedEvent = CreateRowUpdatedEvent(dataRow, dataCommand, statementType, tableMapping);
  893. // this try/catch for any exceptions during the execution, population, output parameters
  894. try {
  895. if (1 != maxBatchCommands) {
  896. IDbConnection connection = DbDataAdapter.GetConnection1(this);
  897. ConnectionState state = UpdateConnectionOpen(connection, StatementType.Batch, connections, connectionStates, useSelectConnectionState);
  898. rowUpdatedEvent.AdapterInit(rowBatch);
  899. if (ConnectionState.Open == state) {
  900. UpdateBatchExecute(batchCommands, commandCount, rowUpdatedEvent);
  901. }
  902. else {
  903. // null Connection will force RowUpdatedEvent with ErrorsOccured without completing batch
  904. rowUpdatedEvent.Errors = ADP.UpdateOpenConnectionRequired(StatementType.Batch, false, state);
  905. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  906. }
  907. }
  908. else if (null != dataCommand) {
  909. IDbConnection connection = DbDataAdapter.GetConnection4(this, dataCommand, statementType, isCommandFromRowUpdating);
  910. ConnectionState state = UpdateConnectionOpen(connection, statementType, connections, connectionStates, useSelectConnectionState);
  911. if (ConnectionState.Open == state) {
  912. UpdateRowExecute(rowUpdatedEvent, dataCommand, statementType);
  913. batchCommands[0].RecordsAffected = rowUpdatedEvent.RecordsAffected;
  914. batchCommands[0].Errors = null;
  915. }
  916. else {
  917. // null Connection will force RowUpdatedEvent with ErrorsOccured without completing batch
  918. rowUpdatedEvent.Errors = ADP.UpdateOpenConnectionRequired(statementType, isCommandFromRowUpdating, state);
  919. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  920. }
  921. }
  922. else {
  923. // null Command will force RowUpdatedEvent with ErrorsOccured without completing batch
  924. rowUpdatedEvent.Errors = ADP.UpdateRequiresCommand(statementType, isCommandFromRowUpdating);
  925. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  926. }
  927. }
  928. catch (Exception e) { // try/catch for RowUpdatedEventArgs
  929. //
  930. if (!ADP.IsCatchableExceptionType(e)) {
  931. throw;
  932. }
  933. ADP.TraceExceptionForCapture(e);
  934. rowUpdatedEvent.Errors = e;
  935. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  936. }
  937. bool clearBatchOnSkipAll = (UpdateStatus.ErrorsOccurred == rowUpdatedEvent.Status);
  938. {
  939. Exception errors = rowUpdatedEvent.Errors;
  940. OnRowUpdated(rowUpdatedEvent); // user may throw out of Update
  941. // NOTE: the contents of rowBatch are now tainted...
  942. if (errors != rowUpdatedEvent.Errors) { // user set the error msg and we will use it
  943. for(int i = 0; i < batchCommands.Length; ++i) {
  944. batchCommands[i].Errors = null;
  945. }
  946. }
  947. }
  948. cumulativeDataRowsAffected += UpdatedRowStatus(rowUpdatedEvent, batchCommands, commandCount);
  949. if (UpdateStatus.SkipAllRemainingRows == rowUpdatedEvent.Status) {
  950. if (clearBatchOnSkipAll && 1 != maxBatchCommands) {
  951. ClearBatch();
  952. commandCount = 0;
  953. }
  954. break; // from update
  955. }
  956. if (1 != maxBatchCommands) {
  957. ClearBatch();
  958. commandCount = 0;
  959. }
  960. for(int i = 0; i < batchCommands.Length; ++i) {
  961. batchCommands[i] = default(BatchCommandInfo);
  962. }
  963. commandCount = 0;
  964. } // foreach DataRow
  965. // must handle the last batch
  966. if (1 != maxBatchCommands && 0 < commandCount) {
  967. RowUpdatedEventArgs rowUpdatedEvent = CreateRowUpdatedEvent(null, dataCommand, statementType, tableMapping);
  968. try {
  969. IDbConnection connection = DbDataAdapter.GetConnection1(this);
  970. ConnectionState state = UpdateConnectionOpen(connection, StatementType.Batch, connections, connectionStates, useSelectConnectionState);
  971. DataRow[] finalRowBatch = rowBatch;
  972. if (commandCount < rowBatch.Length) {
  973. finalRowBatch = new DataRow[commandCount];
  974. Array.Copy(rowBatch, finalRowBatch, commandCount);
  975. }
  976. rowUpdatedEvent.AdapterInit(finalRowBatch);
  977. if (ConnectionState.Open == state) {
  978. UpdateBatchExecute(batchCommands, commandCount, rowUpdatedEvent);
  979. }
  980. else {
  981. // null Connection will force RowUpdatedEvent with ErrorsOccured without completing batch
  982. rowUpdatedEvent.Errors = ADP.UpdateOpenConnectionRequired(StatementType.Batch, false, state);
  983. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  984. }
  985. }
  986. catch (Exception e) { // try/catch for RowUpdatedEventArgs
  987. //
  988. if (!ADP.IsCatchableExceptionType(e)) {
  989. throw;
  990. }
  991. ADP.TraceExceptionForCapture(e);
  992. rowUpdatedEvent.Errors = e;
  993. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  994. }
  995. Exception errors = rowUpdatedEvent.Errors;
  996. OnRowUpdated(rowUpdatedEvent); // user may throw out of Update
  997. // NOTE: the contents of rowBatch are now tainted...
  998. if (errors != rowUpdatedEvent.Errors) { // user set the error msg and we will use it
  999. for(int i = 0; i < batchCommands.Length; ++i) {
  1000. batchCommands[i].Errors = null;
  1001. }
  1002. }
  1003. cumulativeDataRowsAffected += UpdatedRowStatus(rowUpdatedEvent, batchCommands, commandCount);
  1004. }
  1005. }
  1006. finally {
  1007. if (1 != maxBatchCommands) {
  1008. TerminateBatching();
  1009. }
  1010. }
  1011. }
  1012. finally { // try/finally for connection cleanup
  1013. for(int i = 0; i < connections.Length; ++i) {
  1014. QuietClose(connections[i], connectionStates[i]);
  1015. }
  1016. }
  1017. return cumulativeDataRowsAffected;
  1018. }
  1019. finally {
  1020. Bid.ScopeLeave(ref hscp);
  1021. }
  1022. }
  1023. private void UpdateBatchExecute(BatchCommandInfo[] batchCommands, int commandCount, RowUpdatedEventArgs rowUpdatedEvent) {
  1024. try {
  1025. // the batch execution may succeed, partially succeed and throw an exception (or not), or totally fail
  1026. int recordsAffected = ExecuteBatch();
  1027. rowUpdatedEvent.AdapterInit(recordsAffected);
  1028. }
  1029. catch(DbException e) {
  1030. // an exception was thrown be but some part of the batch may have been succesfull
  1031. ADP.TraceExceptionForCapture(e);
  1032. rowUpdatedEvent.Errors = e;
  1033. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  1034. }
  1035. Data.MissingMappingAction missingMapping = UpdateMappingAction;
  1036. Data.MissingSchemaAction missingSchema = UpdateSchemaAction;
  1037. int checkRecordsAffected = 0;
  1038. bool hasConcurrencyViolation = false;
  1039. List<DataRow> rows = null;
  1040. // walk through the batch to build the sum of recordsAffected
  1041. // determine possible indivdual messages per datarow
  1042. // determine possible concurrency violations per datarow
  1043. // map output parameters to the datarow
  1044. for(int bc = 0; bc < commandCount; ++bc) {
  1045. BatchCommandInfo batchCommand = batchCommands[bc];
  1046. StatementType statementType = batchCommand.StatementType;
  1047. // default implementation always returns 1, derived classes must override
  1048. // otherwise DbConcurrencyException will only be thrown if sum of all records in batch is 0
  1049. int rowAffected;
  1050. if (GetBatchedRecordsAffected(batchCommand.CommandIdentifier, out rowAffected, out batchCommands[bc].Errors)) {
  1051. batchCommands[bc].RecordsAffected = rowAffected;
  1052. }
  1053. if ((null == batchCommands[bc].Errors) && batchCommands[bc].RecordsAffected.HasValue) {
  1054. // determine possible concurrency violations per datarow
  1055. if ((StatementType.Update == statementType) || (StatementType.Delete == statementType)) {
  1056. checkRecordsAffected++;
  1057. if (0 == rowAffected) {
  1058. if (null == rows) {
  1059. rows = new List<DataRow>();
  1060. }
  1061. batchCommands[bc].Errors = ADP.UpdateConcurrencyViolation(batchCommands[bc].StatementType, 0, 1, new DataRow[] { rowUpdatedEvent.Rows[bc] });
  1062. hasConcurrencyViolation = true;
  1063. rows.Add(rowUpdatedEvent.Rows[bc]);
  1064. }
  1065. }
  1066. // map output parameters to the datarow
  1067. if (((StatementType.Insert == statementType) || (StatementType.Update == statementType))
  1068. && (0 != (UpdateRowSource.OutputParameters & batchCommand.UpdatedRowSource)) && (0 != rowAffected)) // MDAC 71174
  1069. {
  1070. if (StatementType.Insert == statementType) { // MDAC 64199
  1071. // AcceptChanges for 'added' rows so backend generated keys that are returned
  1072. // propagte into the datatable correctly.
  1073. rowUpdatedEvent.Rows[bc].AcceptChanges();
  1074. }
  1075. for(int i = 0; i < batchCommand.ParameterCount; ++i) {
  1076. IDataParameter parameter = GetBatchedParameter(batchCommand.CommandIdentifier, i);
  1077. ParameterOutput(parameter, batchCommand.Row, rowUpdatedEvent.TableMapping, missingMapping, missingSchema);
  1078. }
  1079. }
  1080. }
  1081. }
  1082. if (null == rowUpdatedEvent.Errors) {
  1083. // Only error if RecordsAffect == 0, not -1. A value of -1 means no count was received from server,
  1084. // do not error in that situation (means 'set nocount on' was executed on server).
  1085. if (UpdateStatus.Continue == rowUpdatedEvent.Status) {
  1086. if ((0 < checkRecordsAffected) && ((0 == rowUpdatedEvent.RecordsAffected) || hasConcurrencyViolation)) {
  1087. // bug50526, an exception if no records affected and attempted an Update/Delete
  1088. Debug.Assert(null == rowUpdatedEvent.Errors, "Continue - but contains an exception");
  1089. DataRow[] rowsInError = (null != rows) ? rows.ToArray() : rowUpdatedEvent.Rows;
  1090. rowUpdatedEvent.Errors = ADP.UpdateConcurrencyViolation(StatementType.Batch, commandCount - rowsInError.Length, commandCount, rowsInError); // MDAC 55735
  1091. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  1092. }
  1093. }
  1094. }
  1095. }
  1096. private ConnectionState UpdateConnectionOpen(IDbConnection connection, StatementType statementType, IDbConnection[] connections, ConnectionState[] connectionStates, bool useSelectConnectionState) {
  1097. Debug.Assert(null != connection, "unexpected null connection");
  1098. Debug.Assert(null != connection, "unexpected null connection");
  1099. int index = (int)statementType;
  1100. if (connection != connections[index]) {
  1101. // if the user has changed the connection on the command object
  1102. // and we had opened that connection, close that connection
  1103. QuietClose(connections[index], connectionStates[index]);
  1104. connections[index] = connection;
  1105. connectionStates[index] = ConnectionState.Closed; // required, open may throw
  1106. QuietOpen(connection, out connectionStates[index]);
  1107. if (useSelectConnectionState && (connections[0] == connection)) {
  1108. connectionStates[index] = connections[0].State;
  1109. }
  1110. }
  1111. return connection.State;
  1112. }
  1113. private int UpdateFromDataTable(DataTable dataTable, DataTableMapping tableMapping) {
  1114. int rowsAffected = 0;
  1115. DataRow[] dataRows = ADP.SelectAdapterRows(dataTable, false);
  1116. if ((null != dataRows) && (0 < dataRows.Length)) {
  1117. rowsAffected = Update(dataRows, tableMapping);
  1118. }
  1119. return rowsAffected;
  1120. }
  1121. private void UpdateRowExecute(RowUpdatedEventArgs rowUpdatedEvent, IDbCommand dataCommand, StatementType cmdIndex) {
  1122. Debug.Assert(null != rowUpdatedEvent, "null rowUpdatedEvent");
  1123. Debug.Assert(null != dataCommand, "null dataCommand");
  1124. Debug.Assert(rowUpdatedEvent.Command == dataCommand, "dataCommand differs from rowUpdatedEvent");
  1125. bool insertAcceptChanges = true;
  1126. UpdateRowSource updatedRowSource = dataCommand.UpdatedRowSource;
  1127. if ((StatementType.Delete == cmdIndex) || (0 == (UpdateRowSource.FirstReturnedRecord & updatedRowSource))) {
  1128. int recordsAffected = dataCommand.ExecuteNonQuery(); // MDAC 88441
  1129. rowUpdatedEvent.AdapterInit(recordsAffected);
  1130. }
  1131. else if ((StatementType.Insert == cmdIndex) || (StatementType.Update == cmdIndex)) {
  1132. // we only care about the first row of the first result
  1133. using(IDataReader dataReader = dataCommand.ExecuteReader(CommandBehavior.SequentialAccess)) {
  1134. DataReaderContainer readerHandler = DataReaderContainer.Create(dataReader, ReturnProviderSpecificTypes);
  1135. try {
  1136. bool getData = false;
  1137. do {
  1138. // advance to the first row returning result set
  1139. // determined by actually having columns in the result set
  1140. if (0 < readerHandler.FieldCount) {
  1141. getData = true;
  1142. break;
  1143. }
  1144. } while (dataReader.NextResult());
  1145. if (getData && (0 != dataReader.RecordsAffected)) { // MDAC 71174
  1146. SchemaMapping mapping = new SchemaMapping(this, null, rowUpdatedEvent.Row.Table, readerHandler, false, SchemaType.Mapped, rowUpdatedEvent.TableMapping.SourceTable, true, null, null);
  1147. if ((null != mapping.DataTable) && (null != mapping.DataValues)) {
  1148. if (dataReader.Read()) {
  1149. if ((StatementType.Insert == cmdIndex) && insertAcceptChanges) { // MDAC 64199
  1150. rowUpdatedEvent.Row.AcceptChanges();
  1151. insertAcceptChanges = false;
  1152. }
  1153. mapping.ApplyToDataRow(rowUpdatedEvent.Row);
  1154. }
  1155. }
  1156. }
  1157. }
  1158. finally {
  1159. // using Close which can optimize its { while(dataReader.NextResult()); } loop
  1160. dataReader.Close();
  1161. // RecordsAffected is available after Close, but don't trust it after Dispose
  1162. int recordsAffected = dataReader.RecordsAffected;
  1163. rowUpdatedEvent.AdapterInit(recordsAffected);
  1164. }
  1165. }
  1166. }
  1167. else {
  1168. // StatementType.Select, StatementType.Batch
  1169. Debug.Assert(false, "unexpected StatementType");
  1170. }
  1171. // map the parameter results to the dataSet
  1172. if (((StatementType.Insert == cmdIndex) || (StatementType.Update == cmdIndex))
  1173. && (0 != (UpdateRowSource.OutputParameters & updatedRowSource)) && (0 != rowUpdatedEvent.RecordsAffected)) { // MDAC 71174
  1174. if ((StatementType.Insert == cmdIndex) && insertAcceptChanges) { // MDAC 64199
  1175. rowUpdatedEvent.Row.AcceptChanges();
  1176. }
  1177. ParameterOutput(dataCommand.Parameters, rowUpdatedEvent.Row, rowUpdatedEvent.TableMapping);
  1178. }
  1179. // Only error if RecordsAffect == 0, not -1. A value of -1 means no count was received from server,
  1180. // do not error in that situation (means 'set nocount on' was executed on server).
  1181. switch(rowUpdatedEvent.Status) {
  1182. case UpdateStatus.Continue:
  1183. switch(cmdIndex) {
  1184. case StatementType.Update:
  1185. case StatementType.Delete:
  1186. if (0 == rowUpdatedEvent.RecordsAffected) {
  1187. // bug50526, an exception if no records affected and attempted an Update/Delete
  1188. Debug.Assert(null == rowUpdatedEvent.Errors, "Continue - but contains an exception");
  1189. rowUpdatedEvent.Errors = ADP.UpdateConcurrencyViolation(cmdIndex, rowUpdatedEvent.RecordsAffected, 1, new DataRow[] { rowUpdatedEvent.Row }); // MDAC 55735
  1190. rowUpdatedEvent.Status = UpdateStatus.ErrorsOccurred;
  1191. }
  1192. break;
  1193. }
  1194. break;
  1195. }
  1196. }
  1197. private int UpdatedRowStatus(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, int commandCount) {
  1198. Debug.Assert(null != rowUpdatedEvent, "null rowUpdatedEvent");
  1199. int cumulativeDataRowsAffected = 0;
  1200. switch (rowUpdatedEvent.Status) {
  1201. case UpdateStatus.Continue:
  1202. cumulativeDataRowsAffected = UpdatedRowStatusContinue(rowUpdatedEvent, batchCommands, commandCount);
  1203. break; // return to foreach DataRow
  1204. case UpdateStatus.ErrorsOccurred:
  1205. cumulativeDataRowsAffected = UpdatedRowStatusErrors(rowUpdatedEvent, batchCommands, commandCount);
  1206. break; // no datarow affected if ErrorsOccured
  1207. case UpdateStatus.SkipCurrentRow:
  1208. case UpdateStatus.SkipAllRemainingRows: // cancel the Update method
  1209. cumulativeDataRowsAffected = UpdatedRowStatusSkip(batchCommands, commandCount);
  1210. break; // foreach DataRow without accepting changes on this row (but user may haved accepted chagnes for us)
  1211. default:
  1212. throw ADP.InvalidUpdateStatus(rowUpdatedEvent.Status);
  1213. } // switch RowUpdatedEventArgs.Status
  1214. return cumulativeDataRowsAffected;
  1215. }
  1216. private int UpdatedRowStatusContinue(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, int commandCount) {
  1217. Debug.Assert(null != batchCommands, "null batchCommands?");
  1218. int cumulativeDataRowsAffected = 0;
  1219. // 1. We delay accepting the changes until after we fire RowUpdatedEvent
  1220. // so the user has a chance to call RejectChanges for any given reason
  1221. // 2. If the DataSource return 0 records affected, its an indication that
  1222. // the command didn't take so we don't want to automatically
  1223. // AcceptChanges.
  1224. // With 'set nocount on' the count will be -1, accept changes in that case too.
  1225. // 3. Don't accept changes if no rows were affected, the user needs
  1226. // to know that there is a concurrency violation
  1227. // Only accept changes if the row is not already accepted, ie detached.
  1228. bool acdu = AcceptChangesDuringUpdate;
  1229. for (int i = 0; i < commandCount; i++) {
  1230. DataRow row = batchCommands[i].Row;
  1231. if ((null == batchCommands[i].Errors) && batchCommands[i].RecordsAffected.HasValue && (0 != batchCommands[i].RecordsAffected.Value)) {
  1232. Debug.Assert(null != row, "null dataRow?");
  1233. if (acdu) {
  1234. if (0 != ((DataRowState.Added|DataRowState.Deleted|DataRowState.Modified)&row.RowState)) {
  1235. row.AcceptChanges();
  1236. }
  1237. }
  1238. cumulativeDataRowsAffected++;
  1239. }
  1240. }
  1241. return cumulativeDataRowsAffected;
  1242. }
  1243. private int UpdatedRowStatusErrors(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, int commandCount) {
  1244. Debug.Assert(null != batchCommands, "null batchCommands?");
  1245. Exception errors = rowUpdatedEvent.Errors;
  1246. if (null == errors) {
  1247. // user changed status to ErrorsOccured without supplying an exception message
  1248. errors = ADP.RowUpdatedErrors();
  1249. rowUpdatedEvent.Errors = errors;
  1250. }
  1251. int affected = 0;
  1252. bool done = false;
  1253. string message = errors.Message;
  1254. for (int i = 0; i < commandCount; i++) {
  1255. DataRow row = batchCommands[i].Row;
  1256. Debug.Assert(null != row, "null dataRow?");
  1257. if (null != batchCommands[i].Errors) { // will exist if 0 == RecordsAffected
  1258. string rowMsg = batchCommands[i].Errors.Message;
  1259. if (String.IsNullOrEmpty(rowMsg)) {
  1260. rowMsg = message;
  1261. }
  1262. row.RowError += rowMsg;
  1263. done = true;
  1264. }
  1265. }
  1266. if (!done) { // all rows are in 'error'
  1267. for (int i = 0; i < commandCount; i++) {
  1268. DataRow row = batchCommands[i].Row;
  1269. // its possible a DBConcurrencyException exists and all rows have records affected
  1270. // via not overriding GetBatchedRecordsAffected or user setting the exception
  1271. row.RowError += message; // MDAC 65808
  1272. }
  1273. }
  1274. else {
  1275. affected = UpdatedRowStatusContinue(rowUpdatedEvent, batchCommands, commandCount);
  1276. }
  1277. if (!ContinueUpdateOnError) { // MDAC 66900
  1278. throw errors; // out of Update
  1279. }
  1280. return affected; // return the count of successful rows within the batch failure
  1281. }
  1282. private int UpdatedRowStatusSkip(BatchCommandInfo[] batchCommands, int commandCount){
  1283. Debug.Assert(null != batchCommands, "null batchCommands?");
  1284. int cumulativeDataRowsAffected = 0;
  1285. for (int i = 0; i < commandCount; i++) {
  1286. DataRow row = batchCommands[i].Row;
  1287. Debug.Assert(null != row, "null dataRow?");
  1288. if (0 != ((DataRowState.Detached|DataRowState.Unchanged) & row.RowState)) {
  1289. cumulativeDataRowsAffected++; // MDAC 66286
  1290. }
  1291. }
  1292. return cumulativeDataRowsAffected;
  1293. }
  1294. private void UpdatingRowStatusErrors(RowUpdatingEventArgs rowUpdatedEvent, DataRow dataRow) {
  1295. Debug.Assert(null != dataRow, "null dataRow");
  1296. Exception errors = rowUpdatedEvent.Errors;
  1297. if (null == errors) {
  1298. // user changed status to ErrorsOccured without supplying an exception message
  1299. errors = ADP.RowUpdatingErrors();
  1300. rowUpdatedEvent.Errors = errors;
  1301. }
  1302. string message = errors.Message;
  1303. dataRow.RowError += message; // MDAC 65808
  1304. if (!ContinueUpdateOnError) { // MDAC 66900
  1305. throw errors; // out of Update
  1306. }
  1307. }
  1308. static private IDbConnection GetConnection1(DbDataAdapter adapter) {
  1309. IDbCommand command = adapter._IDbDataAdapter.SelectCommand;
  1310. if (null == command) {
  1311. command = adapter._IDbDataAdapter.InsertCommand;
  1312. if (null == command) {
  1313. command = adapter._IDbDataAdapter.UpdateCommand;
  1314. if (null == command) {
  1315. command = adapter._IDbDataAdapter.DeleteCommand;
  1316. }
  1317. }
  1318. }
  1319. IDbConnection connection = null;
  1320. if (null != command) {
  1321. connection = command.Connection;
  1322. }
  1323. if (null == connection) {
  1324. throw ADP.UpdateConnectionRequired(StatementType.Batch, false);
  1325. }
  1326. return connection;
  1327. }
  1328. static private IDbConnection GetConnection3(DbDataAdapter adapter, IDbCommand command, string method) {
  1329. Debug.Assert(null != command, "GetConnection3: null command");
  1330. Debug.Assert(!ADP.IsEmpty(method), "missing method name");
  1331. IDbConnection connection = command.Connection;
  1332. if (null == connection) {
  1333. throw ADP.ConnectionRequired_Res(method);
  1334. }
  1335. return connection;
  1336. }
  1337. static private IDbConnection GetConnection4(DbDataAdapter adapter, IDbCommand command, StatementType statementType, bool isCommandFromRowUpdating) {
  1338. Debug.Assert(null != command, "GetConnection4: null command");
  1339. IDbConnection connection = command.Connection;
  1340. if (null == connection) {
  1341. throw ADP.UpdateConnectionRequired(statementType, isCommandFromRowUpdating);
  1342. }
  1343. return connection;
  1344. }
  1345. static private DataRowVersion GetParameterSourceVersion(StatementType statementType, IDataParameter parameter) {
  1346. switch (statementType) {
  1347. case StatementType.Insert: return DataRowVersion.Current; // ignores parameter.SourceVersion
  1348. case StatementType.Update: return parameter.SourceVersion;
  1349. case StatementType.Delete: return DataRowVersion.Original; // ignores parameter.SourceVersion
  1350. case StatementType.Select:
  1351. case StatementType.Batch:
  1352. throw ADP.UnwantedStatementType(statementType);
  1353. default:
  1354. throw ADP.InvalidStatementType(statementType);
  1355. }
  1356. }
  1357. static private void QuietClose(IDbConnection connection, ConnectionState originalState) {
  1358. // close the connection if:
  1359. // * it was closed on first use and adapter has opened it, AND
  1360. // * provider's implementation did not ask to keep this connection open
  1361. if ((null != connection) && (ConnectionState.Closed == originalState)) {
  1362. // we don't have to check the current connection state because
  1363. // it is supposed to be safe to call Close multiple times
  1364. connection.Close();
  1365. }
  1366. }
  1367. // QuietOpen needs to appear in the try {} finally { QuietClose } block
  1368. // otherwise a possibility exists that an exception may be thrown, i.e. ThreadAbortException
  1369. // where we would Open the connection and not close it
  1370. static private void QuietOpen(IDbConnection connection, out ConnectionState originalState) {
  1371. Debug.Assert(null != connection, "QuietOpen: null connection");
  1372. originalState = connection.State;
  1373. if (ConnectionState.Closed == originalState) {
  1374. connection.Open();
  1375. }
  1376. }
  1377. }
  1378. }