写这个文章源于早先对ADO.Net获取数据库元数据上的认识,去年我在阅读ADO.Net Core Reference的时候曾经注意过DataSet的FillSchema的这个方法。这方面,在我之前的随笔中提到过Typed DataSet,而FillSchem与WriteXmlSchema的结合使用可以获得数据库的表结构架构,从而使用相应工具生成强类型的DataSet。但是我记得作者建议在具体应用开发中尽量少用FillSchema这个方法,因为出于性能考虑,其一般只适合作为测试过程中的一个方法。
当时我的理解就是,这是一个获取数据库元数据的一个方便的方法,但是由于其对性能的影响,因此通常应用中比较少用。而在我后面的开发中也未曾有机会接触这个方法。
今年早先1月份的时候看DAAB,注意到其封装的DataCommand对象提供了动态获取存储过程信息的支持:DeriveParameters。当时我的第一印象是,这也是获取数据库的“元数据”,因为之前有过FillSchema对性能影响上的认识,我当时就产生了一个问号:这样做适合吗?自动填充Command对象的Parameter集合,会影响应用程序的性能吗?
就此我也请教过M$的专家,给我的回答是两者机制不同,后者对性能影响不大。
昨日翻倒年初对这个问题疑惑而提的一篇帖子,突然很想进一步找找这两中方法的区别之处,简单了解了一下,以下做个简单的归纳。
DeriveParameters方法
先说简单的一个。DeriveParameters是SqlCommandBuilder类的一个公共方法,提供一个SqlCommannd的参数,该Command对象作为获取到的Parameters的存放容器。其实SqlCommand本身就有一个DeriveParameters的方法,但是它是内部方法,而SqlCommandBuilder.DeriveParameters就是封装了该方法的调用:
publicstaticvoidDeriveParameters(SqlCommandcommand)2


{3
SqlConnection.SqlClientPermission.Demand();4
if(command==null)5


{6
//throwanexception7
}8
command.DeriveParameters();9
}
来看一下SqlCommand的DeriveParameters方法:
internalvoidDeriveParameters()2


{3


4
//Validatecommandtype(isstoredprocedure?)andcommandinfo5


6

7
//Retrievecommandtextdetail8
string[]txtCommand=ADP.ParseProcedureName(this.CommandText);9

10
SqlCommandcmdDeriveCommand=null;11

12
this.cmdText="sp_procedure_params_rowset";13
if(txtCommand[1]!=null)14


{15
this.cmdText="["+txtCommand[1]+"].."+this.cmdText;16

17
if(txtCommand[0]!=null)18


{19
this.cmdText=txtCommand[0]+"."+this.cmdText;20
}21

22
cmdDeriveCommand=newSqlCommand(this.cmdText,this.Connection);23
}24
else25


{26
cmdDeriveCommand=newSqlCommand(this.cmdText,this.Connection);27
}28
cmdDeriveCommand.CommandType=CommandType.StoredProcedure;29
cmdDeriveCommand.Parameters.Add(newSqlParameter("@procedure_name",SqlDbType.NVarChar,0xff));30
cmdDeriveCommand.Parameters[0].Value=txtCommand[3];31
ArrayListparms=newArrayList();32
try33


{34
try35


{36
using(SqlDataReaderdrParam=cmdDeriveCommand.ExecuteReader())37


{38
SqlParameterparameter=null;39
while(drParam.Read())40


{41
parameter=newSqlParameter();42
parameter.ParameterName=(string)drParam["PARAMETER_NAME"];43
parameter.SqlDbType=MetaType.GetSqlDbTypeFromOleDbType((short)drParam["DATA_TYPE"],(string)drParam["TYPE_NAME"]);44
objectlen=drParam["CHARACTER_MAXIMUM_LENGTH"];45
if(lenisint)46


{47
parameter.Size=(int)len;48
}49
parameter.Direction=this.ParameterDirectionFromOleDbDirection((short)drParam["PARAMETER_TYPE"]);50
if(parameter.SqlDbType==SqlDbType.Decimal)51


{52
parameter.Scale=(byte)(((short)drParam["NUMERIC_SCALE"])&0xff);53
parameter.Precision=(byte)(((short)drParam["NUMERIC_PRECISION"])&0xff);54
}55
parms.Add(parameter);56
}57
}58
}59
finally60


{61
cmdDeriveCommand.Connection=null;62
}63
}64
catch65


{66
throw;67
}68

69
if(params.Count==0)70


{71
//throwanexceptionthatcurrentstoredproceduredoesnotexist72
}73

74
this.Parameters.Clear();75
foreach(objectparminparms)76


{77
this._parameters.Add(parm);78
}79
}
ADP.ParseProcedureName其实就是获取存储过程命令的细节信息,有兴趣的可以反编译来看看。
纵观整个方法,有效性验证-〉获取命令字符串-〉执行查询-〉填充参数列表-〉返回。应该是非常简洁明朗的,最多也就是在数据库Query的阶段需要有一个来回,其他操作根本就谈不上有什么复杂度,而且也不存在大数据的对象,对性能的损耗谈不上多巨大。
下面来看看FillSchema的处理过程
FillSchema方法
这个部分因为代码比较多,所以我就抽关键的部分来看一下。
首先,FillSchema是DataAdapter类定义的一个方法,而具体实现则是在该类的子类DBDataAdapter中完成的(SqlDataAdapter继承于DBDataAdapter)。
通过反编译,可以发现FillSchema的关键处理步骤是在其调用私有方法FillSchemaFromCommand来完成的。简单看一下该方法体的内容:
privateDataTable[]FillSchemaFromCommand(objectdata,SchemaTypeschemaType,IDbCommandcommand,stringsrcTable,CommandBehaviorbehavior)2


{3
IDbConnectionconnection=DbDataAdapter.GetConnection(command,"FillSchema");4
ConnectionStatestate=ConnectionState.Open;5
DataTable[]arrTables=newDataTable[0];6
try7


{8
try9


{10
DbDataAdapter.QuietOpen(connection,outstate);11
using(IDataReaderreader=command.ExecuteReader((behavior|CommandBehavior.SchemaOnly)|CommandBehavior.KeyInfo))12


{13
if(reader==null)14


{15
returnarrTables;16
}17
inttblIndex=0;18
while(true)19


{20
if(0<reader.FieldCount)21


{22
try23


{24
stringtxtTableName=null;25
SchemaMappingmapping=newSchemaMapping(this,reader,true);26
if(dataisDataTable)27


{28
mapping.DataTable=(DataTable)data;29
}30
else31


{32
mapping.DataSet=(DataSet)data;33
txtTableName=DbDataAdapter.GetSourceTableName(srcTable,tblIndex);34
}35
mapping.SetupSchema(schemaType,txtTableName,false,null,null);36
DataTablecurrentTable=mapping.DataTable;37
if(currentTable!=null)38


{39
arrTables=DbDataAdapter.AddDataTableToArray(arrTables,currentTable);40
}41
}42
finally43


{44
tblIndex++;45
}46
}47
if(!reader.NextResult())48


{49
returnarrTables;50
}51
}52
}53
}54
finally55


{56
DbDataAdapter.QuietClose(connection,state);57
}58
}59
catch60
ADO.Net元数据获取
本文对比分析了ADO.Net中DeriveParameters与FillSchema两种获取数据库元数据的方法,详细介绍了这两种方法的工作原理及其对应用程序性能的影响。
9140

被折叠的 条评论
为什么被折叠?



