浅析ado.net获取数据库元数据信息 DeriveParameters
写这个文章源于早先对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就是封装了该方法的调用:
public static void DeriveParameters(SqlCommand command)2
{3
SqlConnection.SqlClientPermission.Demand();4
if (command == null)5
{6
// throw an exception7
}8
command.DeriveParameters();9
}来看一下SqlCommand的DeriveParameters方法:
internal void DeriveParameters()2
{3

4
// Validate command type(is storedprocedure?) and command info5

6

7
// Retrieve command text detail8
string[] txtCommand = ADP.ParseProcedureName(this.CommandText);9

10
SqlCommand cmdDeriveCommand = 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 = new SqlCommand(this.cmdText, this.Connection);23
}24
else25
{26
cmdDeriveCommand = new SqlCommand(this.cmdText, this.Connection);27
}28
cmdDeriveCommand.CommandType = CommandType.StoredProcedure;29
cmdDeriveCommand.Parameters.Add(new SqlParameter("@procedure_name", SqlDbType.NVarChar, 0xff));30
cmdDeriveCommand.Parameters[0].Value = txtCommand[3];31
ArrayList parms = new ArrayList();32
try33
{34
try35
{36
using (SqlDataReader drParam = cmdDeriveCommand.ExecuteReader())37
{38
SqlParameter parameter = null;39
while (drParam.Read())40
{41
parameter = new SqlParameter();42
parameter.ParameterName = (string) drParam["PARAMETER_NAME"];43
parameter.SqlDbType = MetaType.GetSqlDbTypeFromOleDbType((short) drParam["DATA_TYPE"], (string) drParam["TYPE_NAME"]);44
object len = drParam["CHARACTER_MAXIMUM_LENGTH"];45
if (len is int)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
// throw an exception that current storedprocedure does not exist72
}73
74
this.Parameters.Clear();75
foreach (object parm in parms)76
{77
this._parameters.Add(parm);78
}79
}ADP.ParseProcedureName其实就是获取存储过程命令的细节信息,有兴趣的可以反编译来看看。
纵观整个方法,有效性验证-〉获取命令字符串-〉执行查询-〉填充参数列表-〉返回。应该是非常简洁明朗的,最多也就是在数据库Query的阶段需要有一个来回,其他操作根本就谈不上有什么复杂度,而且也不存在大数据的对象,对性能的损耗谈不上多巨大。
下面来看看FillSchema的处理过程
FillSchema方法
这个部分因为代码比较多,所以我就抽关键的部分来看一下。
首先,FillSchema是DataAdapter类定义的一个方法,而具体实现则是在该类的子类DBDataAdapter中完成的(SqlDataAdapter继承于DBDataAdapter)。
通过反编译,可以发现FillSchema的关键处理步骤是在其调用私有方法FillSchemaFromCommand来完成的。简单看一下该方法体的内容:
private DataTable[] FillSchemaFromCommand(object data, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)2
{3
IDbConnection connection = DbDataAdapter.GetConnection(command, "FillSchema");4
ConnectionState state = ConnectionState.Open;5
DataTable[] arrTables = new DataTable[0];6
try7
{8
try9
{10
DbDataAdapter.QuietOpen(connection, out state);11
using (IDataReader reader = command.ExecuteReader((behavior | CommandBehavior.SchemaOnly) | CommandBehavior.KeyInfo))12
{13
if (reader == null)14
{15
return arrTables;16
}17
int tblIndex = 0;18
while (true)19
{20
if (0 < reader.FieldCount)21
{22
try23
{24
string txtTableName = null;25
SchemaMapping mapping = new SchemaMapping(this, reader, true);26
if (data is DataTable)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
DataTable currentTable = 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
return arrTables;50
}51
}52
}53
}54
finally55
{56
DbDataAdapter.QuietClose(connection, state);57
}58
}59
catch60
{61
throw;62
}63
return arrTables;64
}首先,该操作含有一个数据库的Query操作,这里其实是调用DBDataAdapter的SelectCommand的对象,执行一次查询,然后遍历查询返回的所有表,每遍历到一个表的时候,通过该表的信息实例化一个SchemaMapping对象,再有该对象创建为DataSet/DataTable创建架构信息。
这里,DataSet/DataTable是作为参数提供的,整个处理过程,首先必然的需要完成一次查询操作,由于使用IDataReader,所以在查询之后的所有操作期间,连接是保持着的,这一定程度上占用了一些资源(也可以说这些资源还不算太昂贵);其次,实例化一个SchemaMapping对象(该对象是内部类,我在MSDN上没有查到相关介绍性资料),我简单看了一下这个类的代码,在我看来,它的处理过程应该是占据了整个过程蛮大一部分资源的,这方面属于个人见解。
由于我的认识上的有限,也为了保证文章的内容无误导,暂且说到这里。这个方法的进一步讨论希望留给有兴趣的朋友。
总结
以上是我对这两个方法认识方面简单的一个概括,其实从上面的描述,也打消了我原先认为的这两个方法在获取元数据上有本质的差别。个人认为,之所以获取结构性元数据的消耗大,是因为获取逻辑的繁琐以及使用的对象的庞大,而参数信息相对而言完全属于轻量级的东西,所以所谓性能上的差异并非因为获取机制的本质差异引起的。
浅析ado.net获取数据库元数据信息 DeriveParameters的更多相关文章
- mysql数据库连接池使用(三)数据库元数据信息反射数据库获取数据库信息
1.1. mysql数据库连接池使用(三)数据库元数据信息反射数据库获取数据库信息 有时候我们想要获取到数据库的基本信息,当前程序连接的那个数据库,数据库的版本信息,数据库中有哪些表,表中都有什么字段 ...
- java 实现视频转换通用工具类:获取视频元数据信息(一)
java 做视频转换主要用到开源的ffmpeg或者mencoder,还要有MP4Box. 注:由于平时都没有时间写博客,所以思路我就不写了,有问题问我,不一定马上回复. 详细介绍: ffmpeg:ht ...
- INFORMATION_SCHEMA获取数据库的信息
简介 information_schema这张数据表保存了MySQL服务器所有数据库的信息.如数据库名,数据库的表,表栏的数据类型与访问权限等.再简单点,这台mysql服务器上,到底有哪些数据库.各个 ...
- SQL SERVER获取数据库文件信息
MS SQL SERVER 获取当前数据库文件等信息,适用于多个版本: SELECT dbf.file_id AS FileID , dbf.name AS [FileName] , s.fi ...
- Delphi中客户端获取数据库更新信息(更新条数)
1.SQL语句 from tb where xxx='XXX') //不存在,则插入数据 begin insert into tb(xxx) values('XXX') //这里自己定义,插入或更新都 ...
- Exploiting second-order SQL injection 利用二阶注入获取数据库版本信息 SQL Injection Attacks and Defense Second Edition
w SQL Injection Attacks and Defense Second Edition Exploiting second-order SQL injection Virtually ...
- 【java 获取数据库信息】获取MySQL或其他数据库的详细信息
1.首先是 通过数据库获取数据表的详细列信息 package com.sxd.mysqlInfo.test; import java.sql.Connection; import java.sql.D ...
- java获取数据库数据表的元数据
Connction conn; DatabaseMetaData dmd=conn.getMetaData();//获取数据库元数据 PreparedStatment ps; ps.getParame ...
- JDBC 元数据信息 getMetaData()
数据库元数据信息: import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.SQLExcept ...
随机推荐
- centos7安装mplayer以及出现的各种问题
首先,centos7默认的视频播放器基本不能用,这里我们选择mplayer作为视频播放器. 安装的过程,痛并快乐着....... 首先我们去mplayer的官网下载需要的文件,http://www.m ...
- uc/os任务创建
问题描述: uc/os中任务创建 问题解决: 创建一个任务,任务从无到有.任务创建函数分两种, 一种是基本的创建函数OSTaskCreate, 另一种是扩展的任务创建函数OSTaskCrea ...
- 返回canceled 代码 的原因
ajax 不支持跨域操作jsonp,才导致返回canceled 代码. 解决的办法就是 <script>标签.jquery 也为我们提供了$.Ajax()方法或$.getScript()方 ...
- WCF 在VS中,添加服务引用,地址输入http://ip/Service.svc,点击前往,提示错误,内容如下:
WCF的service端的webconfig如下: <?xml version="1.0"?> <configuration> <system.ser ...
- [BEC][hujiang] Lesson02 Unit1:Working life ---Reading
2 1.1Working Life p7 reading attitudes to work Question6: 对于Attitude问题 1 I be willing/ unwilling to ...
- struts2 action中传递两个参数到url
<action name="outInDetail" class="formManage_outInDetailAction"> <resul ...
- 李洪强iOS开发拓展篇—UIDynamic(重力行为+碰撞检测)
iOS开发拓展篇—UIDynamic(重力行为+碰撞检测) 一.重力行为 说明:给定重力方向.加速度,让物体朝着重力方向掉落 1.方法 (1)UIGravityBehavior的初始化 - (inst ...
- 初识MyBatis
ORM:对象关系映射,它只是一种规则. 像MyBatis,Hibernate对jdbc进行了封装. 第一章 回顾JDBC开发 1.优点:简单易学,上手快,非常灵活构建SQL(自己写的),效率高.2.缺 ...
- 99. Recover Binary Search Tree
题目: Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without chan ...
- RxJava学习( 二)
1) Scheduler 的 API (一) 在RxJava 中,Scheduler ——调度器,相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程.RxJava 已经内置了 ...