1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.ComponentModel;
6 using System.Data.ProviderBase;
7 using System.Diagnostics;
8 using System.Globalization;
9 
10 namespace System.Data.Common
11 {
12     public class DataAdapter : Component, IDataAdapter
13     {
14         private static readonly object s_eventFillError = new object();
15 
16         private bool _acceptChangesDuringUpdate = true;
17         private bool _acceptChangesDuringUpdateAfterInsert = true;
18         private bool _continueUpdateOnError = false;
19         private bool _hasFillErrorHandler = false;
20         private bool _returnProviderSpecificTypes = false;
21 
22         private bool _acceptChangesDuringFill = true;
23         private LoadOption _fillLoadOption;
24 
25         private MissingMappingAction _missingMappingAction = System.Data.MissingMappingAction.Passthrough;
26         private MissingSchemaAction _missingSchemaAction = System.Data.MissingSchemaAction.Add;
27         private DataTableMappingCollection _tableMappings;
28 
29         private static int s_objectTypeCount; // Bid counter
30         internal readonly int _objectID = System.Threading.Interlocked.Increment(ref s_objectTypeCount);
31 
32 #if DEBUG
33         // if true, we are asserting that the caller has provided a select command
34         // which should not return an empty result set
35         private bool _debugHookNonEmptySelectCommand = false;
36 #endif
37 
38         [Conditional("DEBUG")]
AssertReaderHandleFieldCount(DataReaderContainer readerHandler)39         private void AssertReaderHandleFieldCount(DataReaderContainer readerHandler)
40         {
41 #if DEBUG
42             Debug.Assert(!_debugHookNonEmptySelectCommand || readerHandler.FieldCount > 0, "Scenario expects non-empty results but no fields reported by reader");
43 #endif
44         }
45 
46         [Conditional("DEBUG")]
AssertSchemaMapping(SchemaMapping mapping)47         private void AssertSchemaMapping(SchemaMapping mapping)
48         {
49 #if DEBUG
50             if (_debugHookNonEmptySelectCommand)
51             {
52                 Debug.Assert(mapping != null && mapping.DataValues != null && mapping.DataTable != null, "Debug hook specifies that non-empty results are not expected");
53             }
54 #endif
55         }
56 
DataAdapter()57         protected DataAdapter() : base()
58         {
59             GC.SuppressFinalize(this);
60         }
61 
DataAdapter(DataAdapter from)62         protected DataAdapter(DataAdapter from) : base()
63         {
64             CloneFrom(from);
65         }
66 
67         [DefaultValue(true)]
68         public bool AcceptChangesDuringFill
69         {
70             get
71             {
72                 return _acceptChangesDuringFill;
73             }
74             set
75             {
76                 _acceptChangesDuringFill = value;
77             }
78         }
79 
80         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeAcceptChangesDuringFill()81         public virtual bool ShouldSerializeAcceptChangesDuringFill()
82         {
83             return (0 == _fillLoadOption);
84         }
85 
86         [DefaultValue(true)]
87         public bool AcceptChangesDuringUpdate
88         {
89             get { return _acceptChangesDuringUpdate; }
90             set { _acceptChangesDuringUpdate = value; }
91         }
92 
93         [DefaultValue(false)]
94         public bool ContinueUpdateOnError
95         {
96             get { return _continueUpdateOnError; }
97             set { _continueUpdateOnError = value; }
98         }
99 
100         [RefreshProperties(RefreshProperties.All)]
101         public LoadOption FillLoadOption
102         {
103             get
104             {
105                 LoadOption fillLoadOption = _fillLoadOption;
106                 return ((0 != fillLoadOption) ? _fillLoadOption : LoadOption.OverwriteChanges);
107             }
108             set
109             {
110                 switch (value)
111                 {
112                     case 0: // to allow simple resetting
113                     case LoadOption.OverwriteChanges:
114                     case LoadOption.PreserveChanges:
115                     case LoadOption.Upsert:
116                         _fillLoadOption = value;
117                         break;
118                     default:
119                         throw ADP.InvalidLoadOption(value);
120                 }
121             }
122         }
123 
124         [EditorBrowsable(EditorBrowsableState.Never)]
ResetFillLoadOption()125         public void ResetFillLoadOption()
126         {
127             _fillLoadOption = 0;
128         }
129 
130         [EditorBrowsable(EditorBrowsableState.Never)]
ShouldSerializeFillLoadOption()131         public virtual bool ShouldSerializeFillLoadOption() => 0 != _fillLoadOption;
132 
133         [DefaultValue(System.Data.MissingMappingAction.Passthrough)]
134         public MissingMappingAction MissingMappingAction
135         {
136             get { return _missingMappingAction; }
137             set
138             {
139                 switch (value)
140                 {
141                     case MissingMappingAction.Passthrough:
142                     case MissingMappingAction.Ignore:
143                     case MissingMappingAction.Error:
144                         _missingMappingAction = value;
145                         break;
146                     default:
147                         throw ADP.InvalidMissingMappingAction(value);
148                 }
149             }
150         }
151 
152         [DefaultValue(Data.MissingSchemaAction.Add)]
153         public MissingSchemaAction MissingSchemaAction
154         {
155             get { return _missingSchemaAction; }
156             set
157             {
158                 switch (value)
159                 {
160                     case MissingSchemaAction.Add:
161                     case MissingSchemaAction.Ignore:
162                     case MissingSchemaAction.Error:
163                     case MissingSchemaAction.AddWithKey:
164                         _missingSchemaAction = value;
165                         break;
166                     default:
167                         throw ADP.InvalidMissingSchemaAction(value);
168                 }
169             }
170         }
171 
172         internal int ObjectID => _objectID;
173 
174         [DefaultValue(false)]
175         public virtual bool ReturnProviderSpecificTypes
176         {
177             get { return _returnProviderSpecificTypes; }
178             set { _returnProviderSpecificTypes = value; }
179         }
180 
181         [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
182         public DataTableMappingCollection TableMappings
183         {
184             get
185             {
186                 DataTableMappingCollection mappings = _tableMappings;
187                 if (null == mappings)
188                 {
189                     mappings = CreateTableMappings();
190                     if (null == mappings)
191                     {
192                         mappings = new DataTableMappingCollection();
193                     }
194                     _tableMappings = mappings;
195                 }
196                 return mappings; // constructed by base class
197             }
198         }
199 
200         ITableMappingCollection IDataAdapter.TableMappings => TableMappings;
201 
ShouldSerializeTableMappings()202         protected virtual bool ShouldSerializeTableMappings() => true;
203 
HasTableMappings()204         protected bool HasTableMappings() => ((null != _tableMappings) && (0 < TableMappings.Count));
205 
206         public event FillErrorEventHandler FillError
207         {
208             add
209             {
210                 _hasFillErrorHandler = true;
211                 Events.AddHandler(s_eventFillError, value);
212             }
213             remove
214             {
215                 Events.RemoveHandler(s_eventFillError, value);
216             }
217         }
218 
219         [Obsolete("CloneInternals() has been deprecated.  Use the DataAdapter(DataAdapter from) constructor.  http://go.microsoft.com/fwlink/?linkid=14202")]
CloneInternals()220         protected virtual DataAdapter CloneInternals()
221         {
222             DataAdapter clone = (DataAdapter)Activator.CreateInstance(GetType(), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, null, CultureInfo.InvariantCulture, null);
223             clone.CloneFrom(this);
224             return clone;
225         }
226 
CloneFrom(DataAdapter from)227         private void CloneFrom(DataAdapter from)
228         {
229             _acceptChangesDuringUpdate = from._acceptChangesDuringUpdate;
230             _acceptChangesDuringUpdateAfterInsert = from._acceptChangesDuringUpdateAfterInsert;
231             _continueUpdateOnError = from._continueUpdateOnError;
232             _returnProviderSpecificTypes = from._returnProviderSpecificTypes;
233             _acceptChangesDuringFill = from._acceptChangesDuringFill;
234             _fillLoadOption = from._fillLoadOption;
235             _missingMappingAction = from._missingMappingAction;
236             _missingSchemaAction = from._missingSchemaAction;
237 
238             if ((null != from._tableMappings) && (0 < from.TableMappings.Count))
239             {
240                 DataTableMappingCollection parameters = TableMappings;
241                 foreach (object parameter in from.TableMappings)
242                 {
243                     parameters.Add((parameter is ICloneable) ? ((ICloneable)parameter).Clone() : parameter);
244                 }
245             }
246         }
247 
CreateTableMappings()248         protected virtual DataTableMappingCollection CreateTableMappings()
249         {
250             DataCommonEventSource.Log.Trace("<comm.DataAdapter.CreateTableMappings|API> {0}", ObjectID);
251             return new DataTableMappingCollection();
252         }
253 
Dispose(bool disposing)254         protected override void Dispose(bool disposing)
255         {
256             if (disposing)
257             {
258                 // release mananged objects
259                 _tableMappings = null;
260             }
261             // release unmanaged objects
262 
263             base.Dispose(disposing); // notify base classes
264         }
265 
FillSchema(DataSet dataSet, SchemaType schemaType)266         public virtual DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType)
267         {
268             throw ADP.NotSupported();
269         }
270 
FillSchema(DataSet dataSet, SchemaType schemaType, string srcTable, IDataReader dataReader)271         protected virtual DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType, string srcTable, IDataReader dataReader)
272         {
273             long logScopeId = DataCommonEventSource.Log.EnterScope("<comm.DataAdapter.FillSchema|API> {0}, dataSet, schemaType={1}, srcTable, dataReader", ObjectID, schemaType);
274             try
275             {
276                 if (null == dataSet)
277                 {
278                     throw ADP.ArgumentNull(nameof(dataSet));
279                 }
280                 if ((SchemaType.Source != schemaType) && (SchemaType.Mapped != schemaType))
281                 {
282                     throw ADP.InvalidSchemaType(schemaType);
283                 }
284                 if (string.IsNullOrEmpty(srcTable))
285                 {
286                     throw ADP.FillSchemaRequiresSourceTableName(nameof(srcTable));
287                 }
288                 if ((null == dataReader) || dataReader.IsClosed)
289                 {
290                     throw ADP.FillRequires(nameof(dataReader));
291                 }
292                 // user must Close/Dispose of the dataReader
293                 object value = FillSchemaFromReader(dataSet, null, schemaType, srcTable, dataReader);
294                 return (DataTable[])value;
295             }
296             finally
297             {
298                 DataCommonEventSource.Log.ExitScope(logScopeId);
299             }
300         }
301 
FillSchema(DataTable dataTable, SchemaType schemaType, IDataReader dataReader)302         protected virtual DataTable FillSchema(DataTable dataTable, SchemaType schemaType, IDataReader dataReader)
303         {
304             long logScopeId = DataCommonEventSource.Log.EnterScope("<comm.DataAdapter.FillSchema|API> {0}, dataTable, schemaType, dataReader", ObjectID);
305             try
306             {
307                 if (null == dataTable)
308                 {
309                     throw ADP.ArgumentNull(nameof(dataTable));
310                 }
311                 if ((SchemaType.Source != schemaType) && (SchemaType.Mapped != schemaType))
312                 {
313                     throw ADP.InvalidSchemaType(schemaType);
314                 }
315                 if ((null == dataReader) || dataReader.IsClosed)
316                 {
317                     throw ADP.FillRequires(nameof(dataReader));
318                 }
319                 // user must Close/Dispose of the dataReader
320                 // user will have to call NextResult to access remaining results
321                 object value = FillSchemaFromReader(null, dataTable, schemaType, null, dataReader);
322                 return (DataTable)value;
323             }
324             finally
325             {
326                 DataCommonEventSource.Log.ExitScope(logScopeId);
327             }
328         }
329 
FillSchemaFromReader(DataSet dataset, DataTable datatable, SchemaType schemaType, string srcTable, IDataReader dataReader)330         internal object FillSchemaFromReader(DataSet dataset, DataTable datatable, SchemaType schemaType, string srcTable, IDataReader dataReader)
331         {
332             DataTable[] dataTables = null;
333             int schemaCount = 0;
334             do
335             {
336                 DataReaderContainer readerHandler = DataReaderContainer.Create(dataReader, ReturnProviderSpecificTypes);
337 
338                 AssertReaderHandleFieldCount(readerHandler);
339                 if (0 >= readerHandler.FieldCount)
340                 {
341                     continue;
342                 }
343                 string tmp = null;
344                 if (null != dataset)
345                 {
346                     tmp = DataAdapter.GetSourceTableName(srcTable, schemaCount);
347                     schemaCount++; // don't increment if no SchemaTable ( a non-row returning result )
348                 }
349 
350                 SchemaMapping mapping = new SchemaMapping(this, dataset, datatable, readerHandler, true, schemaType, tmp, false, null, null);
351 
352                 if (null != datatable)
353                 {
354                     // do not read remaining results in single DataTable case
355                     return mapping.DataTable;
356                 }
357                 else if (null != mapping.DataTable)
358                 {
359                     if (null == dataTables)
360                     {
361                         dataTables = new DataTable[1] { mapping.DataTable };
362                     }
363                     else
364                     {
365                         dataTables = DataAdapter.AddDataTableToArray(dataTables, mapping.DataTable);
366                     }
367                 }
368             } while (dataReader.NextResult()); // FillSchema does not capture errors for FillError event
369 
370             object value = dataTables;
371             if ((null == value) && (null == datatable))
372             {
373                 value = Array.Empty<DataTable>();
374             }
375             return value; // null if datatable had no results
376         }
377 
Fill(DataSet dataSet)378         public virtual int Fill(DataSet dataSet)
379         {
380             throw ADP.NotSupported();
381         }
382 
Fill(DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)383         protected virtual int Fill(DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
384         {
385             long logScopeId = DataCommonEventSource.Log.EnterScope("<comm.DataAdapter.Fill|API> {0}, dataSet, srcTable, dataReader, startRecord, maxRecords", ObjectID);
386             try
387             {
388                 if (null == dataSet)
389                 {
390                     throw ADP.FillRequires(nameof(dataSet));
391                 }
392                 if (string.IsNullOrEmpty(srcTable))
393                 {
394                     throw ADP.FillRequiresSourceTableName(nameof(srcTable));
395                 }
396                 if (null == dataReader)
397                 {
398                     throw ADP.FillRequires(nameof(dataReader));
399                 }
400                 if (startRecord < 0)
401                 {
402                     throw ADP.InvalidStartRecord(nameof(startRecord), startRecord);
403                 }
404                 if (maxRecords < 0)
405                 {
406                     throw ADP.InvalidMaxRecords(nameof(maxRecords), maxRecords);
407                 }
408                 if (dataReader.IsClosed)
409                 {
410                     return 0;
411                 }
412                 // user must Close/Dispose of the dataReader
413                 DataReaderContainer readerHandler = DataReaderContainer.Create(dataReader, ReturnProviderSpecificTypes);
414                 return FillFromReader(dataSet, null, srcTable, readerHandler, startRecord, maxRecords, null, null);
415             }
416             finally
417             {
418                 DataCommonEventSource.Log.ExitScope(logScopeId);
419             }
420         }
421 
Fill(DataTable dataTable, IDataReader dataReader)422         protected virtual int Fill(DataTable dataTable, IDataReader dataReader)
423         {
424             DataTable[] dataTables = new DataTable[] { dataTable };
425             return Fill(dataTables, dataReader, 0, 0);
426         }
427 
Fill(DataTable[] dataTables, IDataReader dataReader, int startRecord, int maxRecords)428         protected virtual int Fill(DataTable[] dataTables, IDataReader dataReader, int startRecord, int maxRecords)
429         {
430             long logScopeId = DataCommonEventSource.Log.EnterScope("<comm.DataAdapter.Fill|API> {0}, dataTables[], dataReader, startRecord, maxRecords", ObjectID);
431             try
432             {
433                 ADP.CheckArgumentLength(dataTables, nameof(dataTables));
434                 if ((null == dataTables) || (0 == dataTables.Length) || (null == dataTables[0]))
435                 {
436                     throw ADP.FillRequires("dataTable");
437                 }
438                 if (null == dataReader)
439                 {
440                     throw ADP.FillRequires(nameof(dataReader));
441                 }
442                 if ((1 < dataTables.Length) && ((0 != startRecord) || (0 != maxRecords)))
443                 {
444                     throw ADP.NotSupported(); // FillChildren is not supported with FillPage
445                 }
446 
447                 int result = 0;
448                 bool enforceContraints = false;
449                 DataSet commonDataSet = dataTables[0].DataSet;
450                 try
451                 {
452                     if (null != commonDataSet)
453                     {
454                         enforceContraints = commonDataSet.EnforceConstraints;
455                         commonDataSet.EnforceConstraints = false;
456                     }
457                     for (int i = 0; i < dataTables.Length; ++i)
458                     {
459                         Debug.Assert(null != dataTables[i], "null DataTable Fill");
460 
461                         if (dataReader.IsClosed)
462                         {
463 #if DEBUG
464                             Debug.Assert(!_debugHookNonEmptySelectCommand, "Debug hook asserts data reader should be open");
465 #endif
466                             break;
467                         }
468                         DataReaderContainer readerHandler = DataReaderContainer.Create(dataReader, ReturnProviderSpecificTypes);
469                         AssertReaderHandleFieldCount(readerHandler);
470                         if (readerHandler.FieldCount <= 0)
471                         {
472                             if (i == 0)
473                             {
474                                 bool lastFillNextResult;
475                                 do
476                                 {
477                                     lastFillNextResult = FillNextResult(readerHandler);
478                                 }
479                                 while (lastFillNextResult && readerHandler.FieldCount <= 0);
480                                 if (!lastFillNextResult)
481                                 {
482                                     break;
483                                 }
484                             }
485                             else
486                             {
487                                 continue;
488                             }
489                         }
490                         if ((0 < i) && !FillNextResult(readerHandler))
491                         {
492                             break;
493                         }
494                         // user must Close/Dispose of the dataReader
495                         // user will have to call NextResult to access remaining results
496                         int count = FillFromReader(null, dataTables[i], null, readerHandler, startRecord, maxRecords, null, null);
497                         if (0 == i)
498                         {
499                             result = count;
500                         }
501                     }
502                 }
503                 catch (ConstraintException)
504                 {
505                     enforceContraints = false;
506                     throw;
507                 }
508                 finally
509                 {
510                     if (enforceContraints)
511                     {
512                         commonDataSet.EnforceConstraints = true;
513                     }
514                 }
515                 return result;
516             }
517             finally
518             {
519                 DataCommonEventSource.Log.ExitScope(logScopeId);
520             }
521         }
522 
FillFromReader(DataSet dataset, DataTable datatable, string srcTable, DataReaderContainer dataReader, int startRecord, int maxRecords, DataColumn parentChapterColumn, object parentChapterValue)523         internal int FillFromReader(DataSet dataset, DataTable datatable, string srcTable, DataReaderContainer dataReader, int startRecord, int maxRecords, DataColumn parentChapterColumn, object parentChapterValue)
524         {
525             int rowsAddedToDataSet = 0;
526             int schemaCount = 0;
527             do
528             {
529                 AssertReaderHandleFieldCount(dataReader);
530                 if (0 >= dataReader.FieldCount)
531                 {
532                     continue; // loop to next result
533                 }
534 
535                 SchemaMapping mapping = FillMapping(dataset, datatable, srcTable, dataReader, schemaCount, parentChapterColumn, parentChapterValue);
536                 schemaCount++; // don't increment if no SchemaTable ( a non-row returning result )
537 
538                 AssertSchemaMapping(mapping);
539 
540                 if (null == mapping)
541                 {
542                     continue; // loop to next result
543                 }
544                 if (null == mapping.DataValues)
545                 {
546                     continue; // loop to next result
547                 }
548                 if (null == mapping.DataTable)
549                 {
550                     continue; // loop to next result
551                 }
552                 mapping.DataTable.BeginLoadData();
553                 try
554                 {
555                     // startRecord and maxRecords only apply to the first resultset
556                     if ((1 == schemaCount) && ((0 < startRecord) || (0 < maxRecords)))
557                     {
558                         rowsAddedToDataSet = FillLoadDataRowChunk(mapping, startRecord, maxRecords);
559                     }
560                     else
561                     {
562                         int count = FillLoadDataRow(mapping);
563 
564                         if (1 == schemaCount)
565                         {
566                             // only return LoadDataRow count for first resultset
567                             // not secondary or chaptered results
568                             rowsAddedToDataSet = count;
569                         }
570                     }
571                 }
572                 finally
573                 {
574                     mapping.DataTable.EndLoadData();
575                 }
576                 if (null != datatable)
577                 {
578                     break; // do not read remaining results in single DataTable case
579                 }
580             } while (FillNextResult(dataReader));
581 
582             return rowsAddedToDataSet;
583         }
584 
FillLoadDataRowChunk(SchemaMapping mapping, int startRecord, int maxRecords)585         private int FillLoadDataRowChunk(SchemaMapping mapping, int startRecord, int maxRecords)
586         {
587             DataReaderContainer dataReader = mapping.DataReader;
588 
589             while (0 < startRecord)
590             {
591                 if (!dataReader.Read())
592                 {
593                     // there are no more rows on first resultset
594                     return 0;
595                 }
596                 --startRecord;
597             }
598 
599             int rowsAddedToDataSet = 0;
600             if (0 < maxRecords)
601             {
602                 while ((rowsAddedToDataSet < maxRecords) && dataReader.Read())
603                 {
604                     if (_hasFillErrorHandler)
605                     {
606                         try
607                         {
608                             mapping.LoadDataRowWithClear();
609                             rowsAddedToDataSet++;
610                         }
611                         catch (Exception e) when (ADP.IsCatchableExceptionType(e))
612                         {
613                             ADP.TraceExceptionForCapture(e);
614                             OnFillErrorHandler(e, mapping.DataTable, mapping.DataValues);
615                         }
616                     }
617                     else
618                     {
619                         mapping.LoadDataRow();
620                         rowsAddedToDataSet++;
621                     }
622                 }
623                 // skip remaining rows of the first resultset
624             }
625             else
626             {
627                 rowsAddedToDataSet = FillLoadDataRow(mapping);
628             }
629             return rowsAddedToDataSet;
630         }
631 
FillLoadDataRow(SchemaMapping mapping)632         private int FillLoadDataRow(SchemaMapping mapping)
633         {
634             int rowsAddedToDataSet = 0;
635             DataReaderContainer dataReader = mapping.DataReader;
636             if (_hasFillErrorHandler)
637             {
638                 while (dataReader.Read())
639                 { // read remaining rows of first and subsequent resultsets
640                     try
641                     {
642                         // only try-catch if a FillErrorEventHandler is registered so that
643                         // in the default case we get the full callstack from users
644                         mapping.LoadDataRowWithClear();
645                         rowsAddedToDataSet++;
646                     }
647                     catch (Exception e) when (ADP.IsCatchableExceptionType(e))
648                     {
649                         ADP.TraceExceptionForCapture(e);
650                         OnFillErrorHandler(e, mapping.DataTable, mapping.DataValues);
651                     }
652                 }
653             }
654             else
655             {
656                 while (dataReader.Read())
657                 {
658                     // read remaining rows of first and subsequent resultset
659                     mapping.LoadDataRow();
660                     rowsAddedToDataSet++;
661                 }
662             }
663             return rowsAddedToDataSet;
664         }
665 
FillMappingInternal(DataSet dataset, DataTable datatable, string srcTable, DataReaderContainer dataReader, int schemaCount, DataColumn parentChapterColumn, object parentChapterValue)666         private SchemaMapping FillMappingInternal(DataSet dataset, DataTable datatable, string srcTable, DataReaderContainer dataReader, int schemaCount, DataColumn parentChapterColumn, object parentChapterValue)
667         {
668             bool withKeyInfo = (Data.MissingSchemaAction.AddWithKey == MissingSchemaAction);
669             string tmp = null;
670             if (null != dataset)
671             {
672                 tmp = DataAdapter.GetSourceTableName(srcTable, schemaCount);
673             }
674             return new SchemaMapping(this, dataset, datatable, dataReader, withKeyInfo, SchemaType.Mapped, tmp, true, parentChapterColumn, parentChapterValue);
675         }
676 
FillMapping(DataSet dataset, DataTable datatable, string srcTable, DataReaderContainer dataReader, int schemaCount, DataColumn parentChapterColumn, object parentChapterValue)677         private SchemaMapping FillMapping(DataSet dataset, DataTable datatable, string srcTable, DataReaderContainer dataReader, int schemaCount, DataColumn parentChapterColumn, object parentChapterValue)
678         {
679             SchemaMapping mapping = null;
680             if (_hasFillErrorHandler)
681             {
682                 try
683                 {
684                     // only try-catch if a FillErrorEventHandler is registered so that
685                     // in the default case we get the full callstack from users
686                     mapping = FillMappingInternal(dataset, datatable, srcTable, dataReader, schemaCount, parentChapterColumn, parentChapterValue);
687                 }
688                 catch (Exception e) when (ADP.IsCatchableExceptionType(e))
689                 {
690                     ADP.TraceExceptionForCapture(e);
691                     OnFillErrorHandler(e, null, null);
692                 }
693             }
694             else
695             {
696                 mapping = FillMappingInternal(dataset, datatable, srcTable, dataReader, schemaCount, parentChapterColumn, parentChapterValue);
697             }
698             return mapping;
699         }
700 
FillNextResult(DataReaderContainer dataReader)701         private bool FillNextResult(DataReaderContainer dataReader)
702         {
703             bool result = true;
704             if (_hasFillErrorHandler)
705             {
706                 try
707                 {
708                     // only try-catch if a FillErrorEventHandler is registered so that
709                     // in the default case we get the full callstack from users
710                     result = dataReader.NextResult();
711                 }
712                 catch (Exception e) when (ADP.IsCatchableExceptionType(e))
713                 {
714                     ADP.TraceExceptionForCapture(e);
715                     OnFillErrorHandler(e, null, null);
716                 }
717             }
718             else
719             {
720                 result = dataReader.NextResult();
721             }
722             return result;
723         }
724 
725         [EditorBrowsable(EditorBrowsableState.Advanced)]
GetFillParameters()726         public virtual IDataParameter[] GetFillParameters() => Array.Empty<IDataParameter>();
727 
GetTableMappingBySchemaAction(string sourceTableName, string dataSetTableName, MissingMappingAction mappingAction)728         internal DataTableMapping GetTableMappingBySchemaAction(string sourceTableName, string dataSetTableName, MissingMappingAction mappingAction)
729         {
730             return DataTableMappingCollection.GetTableMappingBySchemaAction(_tableMappings, sourceTableName, dataSetTableName, mappingAction);
731         }
732 
IndexOfDataSetTable(string dataSetTable)733         internal int IndexOfDataSetTable(string dataSetTable)
734         {
735             if (null != _tableMappings)
736             {
737                 return TableMappings.IndexOfDataSetTable(dataSetTable);
738             }
739             return -1;
740         }
741 
OnFillError(FillErrorEventArgs value)742         protected virtual void OnFillError(FillErrorEventArgs value)
743         {
744             ((FillErrorEventHandler)Events[s_eventFillError])?.Invoke(this, value);
745         }
746 
OnFillErrorHandler(Exception e, DataTable dataTable, object[] dataValues)747         private void OnFillErrorHandler(Exception e, DataTable dataTable, object[] dataValues)
748         {
749             FillErrorEventArgs fillErrorEvent = new FillErrorEventArgs(dataTable, dataValues);
750             fillErrorEvent.Errors = e;
751             OnFillError(fillErrorEvent);
752 
753             if (!fillErrorEvent.Continue)
754             {
755                 if (null != fillErrorEvent.Errors)
756                 {
757                     throw fillErrorEvent.Errors;
758                 }
759                 throw e;
760             }
761         }
762 
Update(DataSet dataSet)763         public virtual int Update(DataSet dataSet)
764         {
765             throw ADP.NotSupported();
766         }
767 
768         // used by FillSchema which returns an array of datatables added to the dataset
AddDataTableToArray(DataTable[] tables, DataTable newTable)769         private static DataTable[] AddDataTableToArray(DataTable[] tables, DataTable newTable)
770         {
771             for (int i = 0; i < tables.Length; ++i)
772             { // search for duplicates
773                 if (tables[i] == newTable)
774                 {
775                     return tables; // duplicate found
776                 }
777             }
778             DataTable[] newTables = new DataTable[tables.Length + 1]; // add unique data table
779             for (int i = 0; i < tables.Length; ++i)
780             {
781                 newTables[i] = tables[i];
782             }
783             newTables[tables.Length] = newTable;
784             return newTables;
785         }
786 
787         // dynamically generate source table names
GetSourceTableName(string srcTable, int index)788         private static string GetSourceTableName(string srcTable, int index)
789         {
790             //if ((null != srcTable) && (0 <= index) && (index < srcTable.Length)) {
791             if (0 == index)
792             {
793                 return srcTable; //[index];
794             }
795             return srcTable + index.ToString(System.Globalization.CultureInfo.InvariantCulture);
796         }
797     }
798 
799     internal sealed class LoadAdapter : DataAdapter
800     {
LoadAdapter()801         internal LoadAdapter() { }
802 
FillFromReader(DataTable[] dataTables, IDataReader dataReader, int startRecord, int maxRecords)803         internal int FillFromReader(DataTable[] dataTables, IDataReader dataReader, int startRecord, int maxRecords)
804         {
805             return Fill(dataTables, dataReader, startRecord, maxRecords);
806         }
807     }
808 }
809