上一篇中我们可以看出Fill方法最后都调用FillFromCommand和FillFromReader方法。
那我们接着探讨FillFromCommand方法
private int FillFromCommand(object data, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
{
IDbConnection connection1 = DbDataAdapter.GetConnection(command, "Fill");//返回connection
ConnectionState state1 = ConnectionState.Open;
if (MissingSchemaAction.AddWithKey == base.MissingSchemaAction)
//添加必需的列和主键信息以完成架构。
{
behavior |= CommandBehavior.KeyInfo;
}
int num1 = 0;
try
{
try
{
DbDataAdapter.QuietOpen(connection1, out state1);//打开连接
using (IDataReader reader1 = command.ExecuteReader(behavior | CommandBehavior.SequentialAccess))//读取数据
{
if (data is DataTable)
{
num1 = this.Fill((DataTable) data, reader1);//这个方法我们已经
//说过它最后调用FillFromReader(dataTable, null, dataReader, 0, 0, null, null);
}
else
{
num1 = this.Fill((DataSet) data, srcTable, reader1, startRecord, maxRecords);
//这个方法最后调用 return this.FillFromReader(dataSet, srcTable, dataReader,
//startRecord, maxRecords, null, null);
}
}
}
finally
{
DbDataAdapter.QuietClose(connection1, state1);//关闭连接
}
}
catch
{
throw;
}
return num1;
}
这是就不难看出FillFromCommand其实也是在调用了FillFromReader,也就是说Fill方法的所有操作将由FillFromReader方法来完成。
看看FillFromReader方法:
internal int FillFromReader(object data, string srcTable, IDataReader dataReader, int startRecord, int maxRecords, DataColumn parentChapterColumn, object parentChapterValue)
{
int num1 = 0;
int num2 = 0;//schemaCount
Label_0004:
if (0 < dataReader.FieldCount)
{
SchemaMapping mapping1 = this.FillSchemaMappingTry(data, srcTable, dataReader, num2, parentChapterColumn, parentChapterValue);//创建结构映射
num2++;
if (((mapping1 != null) && (mapping1.DataValues != null)) && (mapping1.DataTable != null))
{
try
{
mapping1.DataTable.BeginLoadData();//加载数据时关闭通知、索引维护和约束
try
{
if ((1 == num2) && ((0 < startRecord) || (0 < maxRecords)))
{
num1 = this.FillLoadDataRowChunk(mapping1, startRecord, maxRecords);//填充从
//startRecord开始的maxRecords条记录,并返回记录个数
}
else
{
int num3 = this.FillLoadDataRow(mapping1);//填充数据
if (1 == num2)
{
num1 = num3;
}
}
}
finally
{
mapping1.DataTable.EndLoadData();//数据加载后打开通知、索引维护和约束。
}
}
catch
{
throw;
}
}
}
if (!this.FillNextResult(dataReader))//判断dataReader中是否还有记录
{
return num1;
}
goto Label_0004;
}
其实到这里我们已经对fill有个总体了解,首先不管fill重载多少次它都是最后调用FillFromReader方法,在FillFromReader中先创建结构映射,然后填充数据。
接着看看结构影射,这个很复杂,请朋友们多多指教。
//结构影射
private SchemaMapping FillSchemaMappingTry(object data, string srcTable, IDataReader dataReader, int schemaCount, DataColumn parentChapterColumn, object parentChapterValue)
{
SchemaMapping mapping1 = null;
if (this.hasFillErrorHandler)
{
try
{
mapping1 = this.FillSchemaMapping(data, srcTable, dataReader, schemaCount, parentChapterColumn, parentChapterValue);
}
catch (Exception exception1)
{
this.FillErrorHandler(exception1, null, null);
}
}
else
{
mapping1 = this.FillSchemaMapping(data, srcTable, dataReader, schemaCount, parentChapterColumn, parentChapterValue);
}
return mapping1;
}
//FillSchemaMapping
private SchemaMapping FillSchemaMapping(object data, string srcTable, IDataReader dataReader, int schemaCount, DataColumn parentChapterColumn, object parentChapterValue)
{
SchemaMapping mapping1 = new SchemaMapping(this, dataReader, MissingSchemaAction.AddWithKey == base.MissingSchemaAction);//创建SchemaMapping实例
//成员名称 说明
//MissingSchemaAction:
// Add 添加必需的列以完成架构。
// AddWithKey 添加必需的列和主键信息以完成架构。有关如何将主键信息添加到 DataTable 的更多信息,请参见 FillSchema。
// Error 如果缺少指定的列映射,则生成 InvalidOperationException。
// Ignore 忽略额外列。
string text1 = null;
if (data is DataTable)
{
mapping1.DataTable = (DataTable) data;
}
else
{
mapping1.DataSet = (DataSet) data;
text1 = DbDataAdapter.GetSourceTableName(srcTable, schemaCount);//DataSet中要创建表的名称
/**
*internal static string GetSourceTableName(string srcTable, int index)
*{
* if (index == 0)
* {
* return srcTable;
*}
* return (srcTable + index.ToString());
* }
**/
}
mapping1.SetupSchema(SchemaType.Mapped, text1, true, parentChapterColumn, parentChapterValue);//
/*
*
* public enum SchemaType
*{
* // Fields
* Mapped = 2, 将任何现有的表映射应用到传入架构,用转换的架构配置 DataSet。
* Source = 1 忽略 DataAdapter 上的任何表映射。使用传入架构配置 DataSet,而不应用任何转换。
* }
**/
return mapping1;
}
//SchemaMapping
internal SchemaMapping(DbDataAdapter adapter, IDataReader dataReader, bool keyInfo)
{
this.adapter = adapter;
this.dataReader = dataReader;
if (keyInfo)//如果必须添加列和主键,就用dataReader的GetSchemaTable()返回一个DataTable
{
this.schemaTable = dataReader.GetSchemaTable();//返回一个 DataTable,它描述 SqlDataReader 的列元数据。可以查看msdn
}
}
//SetupSchema 创建
internal void SetupSchema(SchemaType schemaType, string sourceTableName, bool gettingData, DataColumn parentChapterColumn, object parentChapterValue)
{
MissingMappingAction action1;
MissingSchemaAction action2;
if (SchemaType.Mapped == schemaType)
{
action1 = this.adapter.MissingMappingAction;//确定传入数据没有匹配的表或列时需要执行的操作
action2 = this.adapter.MissingSchemaAction;//确定现有 DataSet 架构与传入数据不匹配时需要执行的操作。
if (!ADP.IsEmpty(sourceTableName))
{
this.tableMapping = this.adapter.GetTableMappingBySchemaAction(sourceTableName, sourceTableName, action1);//创建源表和 DataTable 之间的主映射。
goto Label_016D;
}
if (this.dataTable == null)
{
goto Label_016D;
}
int num1 = this.adapter.IndexOfDataSetTable(this.dataTable.TableName);
if (-1 != num1)
{
this.tableMapping = this.adapter.TableMappings[num1];
goto Label_016D;
}
switch (action1)
{
case MissingMappingAction.Passthrough://创建源列或源表,并使用其原始名称将其添加到 DataSet。
{
this.tableMapping = new DataTableMapping(this.dataTable.TableName, this.dataTable.TableName);
goto Label_016D;
}
case MissingMappingAction.Ignore://忽略没有映射的列或表。
{
this.tableMapping = null;
goto Label_016D;
}
case MissingMappingAction.Error://缺少指定的列映射,则生成 InvalidOperationException
{
throw ADP.MissingTableMappingDestination(this.dataTable.TableName);
}
}
throw ADP.InvalidMappingAction((int) action1);
}
if (SchemaType.Source != schemaType)
{
throw ADP.InvalidSchemaType((int) schemaType);
}
action1 = MissingMappingAction.Passthrough;
action2 = MissingSchemaAction.Add;
if (!ADP.IsEmpty(sourceTableName))
{
this.tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(null, sourceTableName, sourceTableName, action1);
}
else if (this.dataTable != null)
{
int num2 = this.adapter.IndexOfDataSetTable(this.dataTable.TableName);
if (-1 != num2)
{
this.tableMapping = this.adapter.TableMappings[num2];
}
else
{
this.tableMapping = new DataTableMapping(this.dataTable.TableName, this.dataTable.TableName);////创建源表和 DataTable 之间的主映射。
}
}
Label_016D:
if (this.tableMapping != null)
{
if (this.dataTable == null)
{
this.dataTable = this.tableMapping.GetDataTableBySchemaAction(this.dataSet, action2);使用action2的 值获取DataSet的当前 DataTable。可以查看msdn
if (this.dataTable == null)
{
return;
}
}
if (this.schemaTable == null)
{
this.SetupSchemaWithoutKeyInfo(action1, action2, gettingData, parentChapterColumn, parentChapterValue);
}
else
{
this.SetupSchemaWithKeyInfo(action1, action2, gettingData, parentChapterColumn, parentChapterValue);
}
}
}
//FillLoadDataRowChunk
private int FillLoadDataRowChunk(SchemaMapping mapping, int startRecord, int maxRecords)
{
IDataReader reader1 = mapping.DataReader;
while (0 < startRecord) //把DataReader调整到读startRecord记录的位置
{ //如果DataReader的总的记录数小于startRecord,就返回0
if (!reader1.Read()) //其实在这里也可以看出要在DataReader中定位,只有去边历DataReader
{ //到这里我们也可以明白fill方法其实也是用DataReader一条条读出来的
return 0;
}
startRecord--;
}
int num1 = 0;
if (0 >= maxRecords)
{
goto Label_0062;
}
bool flag1 = base.AcceptChangesDuringFill;//指示在Fill操作过程中,在将 AcceptChanges 添加到 DataTable 之后是否针对 DataRow 调用它。可以在msdn中的DataAdapter 类中查看
goto Label_0054;
Label_002B:
try
{
mapping.LoadDataRow(this.hasFillErrorHandler, flag1);//装载数据
num1++;
}
catch (Exception exception1)
{
this.FillErrorHandler(exception1, mapping.DataTable, mapping.DataValues);
}
Label_0054:
if (num1 >= maxRecords)//如果载入的记录数已经满足条件,就退出,否则继续
{
goto Label_006A;
}
if (reader1.Read())//继续读取数据
{
goto Label_002B;
}
goto Label_006A;
Label_0062:
num1 = this.FillLoadDataRow(mapping);
Label_006A:
return num1;
}
到这里fill方法大概就完了,其实看来fill方法就是首先创建映射,然后在用datareader把数据填充进来.其中我忽略了好多东西比如它的异常处理,还有好多细节,请各位朋友多多指教,在这里列出来也是和各位朋友探讨。