Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / Data / System / Data / ProviderBase / SchemaMapping.cs / 1 / SchemaMapping.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// [....]
//-----------------------------------------------------------------------------
namespace System.Data.ProviderBase {
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
sealed internal class SchemaMapping {
// DataColumns match in length and name order as the DataReader, no chapters
private const int MapExactMatch = 0;
// DataColumns has different length, but correct name order as the DataReader, no chapters
private const int MapDifferentSize = 1;
// DataColumns may have different length, but a differant name ordering as the DataReader, no chapters
private const int MapReorderedValues = 2;
// DataColumns may have different length, but correct name order as the DataReader, with chapters
private const int MapChapters = 3;
// DataColumns may have different length, but a differant name ordering as the DataReader, with chapters
private const int MapChaptersReordered = 4;
// map xml string data to DataColumn with DataType=typeof(SqlXml)
private const int SqlXml = 1;
// map xml string data to DataColumn with DataType=typeof(XmlDocument)
private const int XmlDocument = 2;
private readonly DataSet _dataSet; // the current dataset, may be null if we are only filling a DataTable
private DataTable _dataTable; // the current DataTable, should never be null
private readonly DataAdapter _adapter;
private readonly DataReaderContainer _dataReader;
private readonly DataTable _schemaTable; // will be null if Fill without schema
private readonly DataTableMapping _tableMapping;
// unique (generated) names based from DataReader.GetName(i)
private readonly string[] _fieldNames;
private readonly object[] _readerDataValues;
private object[] _mappedDataValues; // array passed to dataRow.AddUpdate(), if needed
private int[] _indexMap; // index map that maps dataValues -> _mappedDataValues, if needed
private bool[] _chapterMap; // which DataReader indexes have chapters
private int[] _xmlMap; // map which value in _readerDataValues to convert to a Xml datatype, (SqlXml/XmlDocument)
private int _mappedMode; // modes as described as above
private int _mappedLength;
private readonly LoadOption _loadOption;
internal SchemaMapping(DataAdapter adapter, DataSet dataset, DataTable datatable, DataReaderContainer dataReader, bool keyInfo,
SchemaType schemaType, string sourceTableName, bool gettingData,
DataColumn parentChapterColumn, object parentChapterValue) {
Debug.Assert(null != adapter, "adapter");
Debug.Assert(null != dataReader, "dataReader");
Debug.Assert(0 < dataReader.FieldCount, "FieldCount");
Debug.Assert(null != dataset || null != datatable, "SchemaMapping - null dataSet");
Debug.Assert(SchemaType.Mapped == schemaType || SchemaType.Source == schemaType, "SetupSchema - invalid schemaType");
_dataSet = dataset; // setting DataSet implies chapters are supported
_dataTable = datatable; // setting only DataTable, not DataSet implies chapters are not supported
_adapter = adapter;
_dataReader = dataReader;
if (keyInfo) {
_schemaTable = dataReader.GetSchemaTable();
}
if (adapter.ShouldSerializeFillLoadOption()) {
_loadOption = adapter.FillLoadOption;
}
else if (adapter.AcceptChangesDuringFill) {
_loadOption = (LoadOption)4; // true
}
else {
_loadOption = (LoadOption)5; //false
}
MissingMappingAction mappingAction;
MissingSchemaAction schemaAction;
if (SchemaType.Mapped == schemaType) {
mappingAction = _adapter.MissingMappingAction;
schemaAction = _adapter.MissingSchemaAction;
if (!ADP.IsEmpty(sourceTableName)) { // MDAC 66034
_tableMapping = _adapter.GetTableMappingBySchemaAction(sourceTableName, sourceTableName, mappingAction);
}
else if (null != _dataTable) {
int index = _adapter.IndexOfDataSetTable(_dataTable.TableName);
if (-1 != index) {
_tableMapping = _adapter.TableMappings[index];
}
else {
switch (mappingAction) {
case MissingMappingAction.Passthrough:
_tableMapping = new DataTableMapping(_dataTable.TableName, _dataTable.TableName);
break;
case MissingMappingAction.Ignore:
_tableMapping = null;
break;
case MissingMappingAction.Error:
throw ADP.MissingTableMappingDestination(_dataTable.TableName);
default:
throw ADP.InvalidMissingMappingAction(mappingAction);
}
}
}
}
else if (SchemaType.Source == schemaType) {
mappingAction = System.Data.MissingMappingAction.Passthrough;
schemaAction = Data.MissingSchemaAction.Add;
if (!ADP.IsEmpty(sourceTableName)) { // MDAC 66034
_tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(null, sourceTableName, sourceTableName, mappingAction);
}
else if (null != _dataTable) {
int index = _adapter.IndexOfDataSetTable(_dataTable.TableName); // MDAC 66034
if (-1 != index) {
_tableMapping = _adapter.TableMappings[index];
}
else {
_tableMapping = new DataTableMapping(_dataTable.TableName, _dataTable.TableName);
}
}
}
else {
throw ADP.InvalidSchemaType(schemaType);
}
if (null != _tableMapping) {
if (null == _dataTable) {
_dataTable = _tableMapping.GetDataTableBySchemaAction(_dataSet, schemaAction);
}
if (null != _dataTable) {
_fieldNames = GenerateFieldNames(dataReader);
if (null == _schemaTable) {
_readerDataValues = SetupSchemaWithoutKeyInfo(mappingAction, schemaAction, gettingData, parentChapterColumn, parentChapterValue);
}
else {
_readerDataValues = SetupSchemaWithKeyInfo(mappingAction, schemaAction, gettingData, parentChapterColumn, parentChapterValue);
}
}
// else (null == _dataTable) which means ignore (mapped to nothing)
}
}
internal DataReaderContainer DataReader {
get {
return _dataReader;
}
}
internal DataTable DataTable {
get {
return _dataTable;
}
}
internal object[] DataValues {
get {
return _readerDataValues;
}
}
internal void ApplyToDataRow(DataRow dataRow) {
DataColumnCollection columns = dataRow.Table.Columns;
_dataReader.GetValues(_readerDataValues);
object[] mapped = GetMappedValues();
bool[] readOnly = new bool[mapped.Length];
for (int i = 0; i < readOnly.Length; ++i) {
readOnly[i] = columns[i].ReadOnly;
}
try {
try {
// allow all columns to be written to
for (int i = 0; i < readOnly.Length; ++i) {
if (0 == columns[i].Expression.Length) { // WebData 110773
columns[i].ReadOnly = false;
}
}
for(int i = 0; i < mapped.Length; ++i) {
if (null != mapped[i]) { // MDAC 72659
dataRow[i] = mapped[i];
}
}
}
finally { // ReadOnly
// reset readonly flag on all columns
for (int i = 0; i < readOnly.Length; ++i) {
if (0 == columns[i].Expression.Length) { // WebData 110773
columns[i].ReadOnly = readOnly[i];
}
}
}
}
finally { // FreeDataRowChapters
if (null != _chapterMap) {
FreeDataRowChapters();
}
}
}
private void MappedChapterIndex() { // mode 4
int length = _mappedLength;
for (int i = 0; i < length; i++) {
int k = _indexMap[i];
if (0 <= k) {
_mappedDataValues[k] = _readerDataValues[i]; // from reader to dataset
if (_chapterMap[i]) {
_mappedDataValues[k] = null; // InvalidCast from DataReader to AutoIncrement DataColumn
}
}
}
}
private void MappedChapter() { // mode 3
int length = _mappedLength;
for (int i = 0; i < length; i++) {
_mappedDataValues[i] = _readerDataValues[i]; // from reader to dataset
if (_chapterMap[i]) {
_mappedDataValues[i] = null; // InvalidCast from DataReader to AutoIncrement DataColumn
}
}
}
private void MappedIndex() { // mode 2
Debug.Assert(_mappedLength == _indexMap.Length, "incorrect precomputed length");
int length = _mappedLength;
for (int i = 0; i < length; i++) {
int k = _indexMap[i];
if (0 <= k) {
_mappedDataValues[k] = _readerDataValues[i]; // from reader to dataset
}
}
}
private void MappedValues() { // mode 1
Debug.Assert(_mappedLength == Math.Min(_readerDataValues.Length, _mappedDataValues.Length), "incorrect precomputed length");
int length = _mappedLength;
for (int i = 0; i < length; ++i) {
_mappedDataValues[i] = _readerDataValues[i]; // from reader to dataset
};
}
private object[] GetMappedValues() { // mode 0
if (null != _xmlMap) {
for(int i = 0; i < _xmlMap.Length; ++i) {
if (0 != _xmlMap[i]) {
// get the string/SqlString xml value
string xml = _readerDataValues[i] as string;
if ((null == xml) && (_readerDataValues[i] is System.Data.SqlTypes.SqlString)) {
System.Data.SqlTypes.SqlString x = (System.Data.SqlTypes.SqlString)_readerDataValues[i];
if (!x.IsNull) {
xml = x.Value;
}
else {
switch(_xmlMap[i]) {
case SqlXml:
// map strongly typed SqlString.Null to SqlXml.Null
_readerDataValues[i] = System.Data.SqlTypes.SqlXml.Null;
break;
default:
_readerDataValues[i] = DBNull.Value;
break;
}
}
}
if (null != xml) {
switch(_xmlMap[i]) {
case SqlXml: // turn string into a SqlXml value for DataColumn
System.Xml.XmlReaderSettings settings = new System.Xml.XmlReaderSettings();
settings.ConformanceLevel = System.Xml.ConformanceLevel.Fragment;
System.Xml.XmlReader reader = System.Xml.XmlReader.Create(new System.IO.StringReader(xml), settings, (string)null);
_readerDataValues[i] = new System.Data.SqlTypes.SqlXml(reader);
break;
case XmlDocument: // turn string into XmlDocument value for DataColumn
System.Xml.XmlDocument document = new System.Xml.XmlDocument();
document.LoadXml(xml);
_readerDataValues[i] = document;
break;
}
// default: let value fallthrough to DataSet which may fail with ArgumentException
}
}
}
}
switch(_mappedMode) {
default:
case MapExactMatch:
Debug.Assert(0 == _mappedMode, "incorrect mappedMode");
Debug.Assert((null == _chapterMap) && (null == _indexMap) && (null == _mappedDataValues), "incorrect MappedValues");
return _readerDataValues; // from reader to dataset
case MapDifferentSize:
Debug.Assert((null == _chapterMap) && (null == _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
MappedValues();
break;
case MapReorderedValues:
Debug.Assert((null == _chapterMap) && (null != _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
MappedIndex();
break;
case MapChapters:
Debug.Assert((null != _chapterMap) && (null == _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
MappedChapter();
break;
case MapChaptersReordered:
Debug.Assert((null != _chapterMap) && (null != _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
MappedChapterIndex();
break;
}
return _mappedDataValues;
}
internal void LoadDataRowWithClear() {
// for FillErrorEvent to ensure no values leftover from previous row
for (int i = 0; i < _readerDataValues.Length; ++i) {
_readerDataValues[i] = null;
}
LoadDataRow();
}
internal void LoadDataRow() {
try {
_dataReader.GetValues(_readerDataValues);
object[] mapped = GetMappedValues();
DataRow dataRow;
switch(_loadOption) {
case LoadOption.OverwriteChanges:
case LoadOption.PreserveChanges:
case LoadOption.Upsert:
dataRow = _dataTable.LoadDataRow(mapped, _loadOption);
break;
case (LoadOption)4: // true
dataRow = _dataTable.LoadDataRow(mapped, true);
break;
case (LoadOption)5: // false
dataRow = _dataTable.LoadDataRow(mapped, false);
break;
default:
Debug.Assert(false, "unexpected LoadOption");
throw ADP.InvalidLoadOption(_loadOption);
}
if ((null != _chapterMap) && (null != _dataSet)) {
LoadDataRowChapters(dataRow); // MDAC 70772
}
}
finally {
if (null != _chapterMap) {
FreeDataRowChapters(); // MDAC 71900
}
}
}
private void FreeDataRowChapters() {
for(int i = 0; i < _chapterMap.Length; ++i) {
if (_chapterMap[i]) {
IDisposable disposable = (_readerDataValues[i] as IDisposable);
if (null != disposable) {
_readerDataValues[i] = null;
disposable.Dispose();
}
}
}
}
internal int LoadDataRowChapters(DataRow dataRow) {
int datarowadded = 0;
int rowLength = _chapterMap.Length;
for(int i = 0; i < rowLength; ++i) {
if (_chapterMap[i]) {
object readerValue = _readerDataValues[i];
if ((null != readerValue) && !Convert.IsDBNull(readerValue)) { // MDAC 70441
_readerDataValues[i] = null;
using (IDataReader nestedReader = (IDataReader) readerValue) {
if (!nestedReader.IsClosed) {
Debug.Assert(null != _dataSet, "if chapters, then Fill(DataSet,...) not Fill(DataTable,...)");
object parentChapterValue;
DataColumn parentChapterColumn;
if (null == _indexMap) {
parentChapterColumn = _dataTable.Columns[i];
parentChapterValue = dataRow[parentChapterColumn];
}
else {
parentChapterColumn = _dataTable.Columns[_indexMap[i]];
parentChapterValue = dataRow[parentChapterColumn];
}
// correct on Fill, not FillFromReader
string chapterTableName = _tableMapping.SourceTable + _fieldNames[i]; // MDAC 70908
DataReaderContainer readerHandler = DataReaderContainer.Create(nestedReader, _dataReader.ReturnProviderSpecificTypes);
datarowadded += _adapter.FillFromReader(_dataSet, null, chapterTableName, readerHandler, 0, 0, parentChapterColumn, parentChapterValue);
}
}
}
}
}
return datarowadded;
}
private int[] CreateIndexMap(int count, int index) {
int[] values = new int[count];
for (int i = 0; i < index; ++i) {
values[i] = i;
}
return values;
}
private static string[] GenerateFieldNames(DataReaderContainer dataReader) {
string[] fieldNames = new string[dataReader.FieldCount];
for(int i = 0; i < fieldNames.Length; ++i) {
fieldNames[i] = dataReader.GetName(i);
}
ADP.BuildSchemaTableInfoTableNames(fieldNames);
return fieldNames;
}
private DataColumn[] ResizeColumnArray(DataColumn[] rgcol, int len) {
Debug.Assert(rgcol != null, "invalid call to ResizeArray");
Debug.Assert(len <= rgcol.Length, "invalid len passed to ResizeArray");
DataColumn[] tmp = new DataColumn[len];
Array.Copy(rgcol, tmp, len);
return tmp;
}
private void AddItemToAllowRollback(ref List