浅析ado.net获取数据库元数据信息
写这个文章源于早先对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就是封装了该方法的调用:
2{
3 SqlConnection.SqlClientPermission.Demand();
4 if (command == null)
5 {
6 // throw an exception
7 }
8 command.DeriveParameters();
9}
来看一下SqlCommand的DeriveParameters方法:
2{
3
4 // Validate command type(is storedprocedure?) and command info
5
6
7 // Retrieve command text detail
8 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 else
25 {
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 try
33 {
34 try
35 {
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 finally
60 {
61 cmdDeriveCommand.Connection = null;
62 }
63 }
64 catch
65 {
66 throw;
67 }
68
69 if (params.Count == 0)
70 {
71 // throw an exception that current storedprocedure does not exist
72 }
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来完成的。简单看一下该方法体的内容:
2{
3 IDbConnection connection = DbDataAdapter.GetConnection(command, "FillSchema");
4 ConnectionState state = ConnectionState.Open;
5 DataTable[] arrTables = new DataTable[0];
6 try
7 {
8 try
9 {
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 try
23 {
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 else
31 {
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 finally
43 {
44 tblIndex++;
45 }
46 }
47 if (!reader.NextResult())
48 {
49 return arrTables;
50 }
51 }
52 }
53 }
54 finally
55 {
56 DbDataAdapter.QuietClose(connection, state);
57 }
58 }
59 catch
60 {
61 throw;
62 }
63 return arrTables;
64}
首先,该操作含有一个数据库的Query操作,这里其实是调用DBDataAdapter的SelectCommand的对象,执行一次查询,然后遍历查询返回的所有表,每遍历到一个表的时候,通过该表的信息实例化一个SchemaMapping对象,再有该对象创建为DataSet/DataTable创建架构信息。
这里,DataSet/DataTable是作为参数提供的,整个处理过程,首先必然的需要完成一次查询操作,由于使用IDataReader,所以在查询之后的所有操作期间,连接是保持着的,这一定程度上占用了一些资源(也可以说这些资源还不算太昂贵);其次,实例化一个SchemaMapping对象(该对象是内部类,我在MSDN上没有查到相关介绍性资料),我简单看了一下这个类的代码,在我看来,它的处理过程应该是占据了整个过程蛮大一部分资源的,这方面属于个人见解。
由于我的认识上的有限,也为了保证文章的内容无误导,暂且说到这里。这个方法的进一步讨论希望留给有兴趣的朋友。
总结
以上是我对这两个方法认识方面简单的一个概括,其实从上面的描述,也打消了我原先认为的这两个方法在获取元数据上有本质的差别。个人认为,之所以获取结构性元数据的消耗大,是因为获取逻辑的繁琐以及使用的对象的庞大,而参数信息相对而言完全属于轻量级的东西,所以所谓性能上的差异并非因为获取机制的本质差异引起的。
浅析ado.net获取数据库元数据信息相关推荐
- jdbc获取数据库元数据,获取数据库列表,获取数据库基本信息,获取指定数据库中的表信息,获取指定表中的字段信息
jdbc获取数据库元数据 package cn.itcast.metadata.test;import org.junit.Before; import org.junit.Test;import j ...
- MySQL获取数据库元数据相关命令:DESC、SHOW、INFORMATION_SCHEMA、mysqlshow、mysqldump
MySQL提供了多种获取数据库元数据(即有关数据库的信息与它里面的各种对象)的方式: DESC/EXPLAIN用来查看表信息 各种SHOW语句,例如SHOW DATABASES或SHOW TABLES ...
- 查询Master下的系统表和系统视图获取数据库的信息和简单的渗透测试
在SQL中可以通过查询Master下的系统表(sys)和系统视图(information_schema)获取数据库的信息.SQL2000和SQL2005的结构略有不同. 系统表结构参考系统表详细说明. ...
- python获取数据库查询的元数据_Python数据库、MySQL存储引擎、使用分区表、更改表结构、获取数据库元数据...
:1. 数据库基本操作 1.1 创建数据库 - CREATE DATABASE test; #创建数据库 - GRANT ALL ON test.* to user(s); #为指定用户(或所有用户) ...
- 一键获取数据库整体信息脚本
一键获取数据库整体信息脚本 将脚本内容放spooldb.sql中,在sqlplus中执行,相关信息会自动生成5个文件,其中addm是最近一小时文件,ash是最近半小时文件,而awr文件是最近一小时和最 ...
- sql 获取数据库字段信息_使用DBATools获取SQL数据库详细信息
sql 获取数据库字段信息 In the series of articles on DBATools, (see TOC at the bottom) we are exploring useful ...
- C#获取数据库表信息,列信息
获取表的信息: conn.Open();string[] restrictions = new string[4];restrictions[1] = "dbo"; DataTab ...
- 使用OLE方式获取数据库架构信息
一般连接SQL数据库的连接字串: data source=[SERVERNAME];user id=[USERNAME];password=[PASSWORD];database=[DATABASEN ...
- mysql 别名 metadata_获取数据库元数据:DatabaseMetaData与ParameterMetaData与ResultSetMetaData...
(1)Meta-Data:关于数据的数据,在此是有关数据库和数据库表等数据库对象的信息: 通过Connection的getMetaData()方法获得包含数据库元数据的DatabaseMetaData ...
最新文章
- 网上订票抢票攻略(亲测)
- Drainage Ditches POJ1273
- 宿主不能访问本机虚拟机中的web服务器
- chr sqlserver_SQLServer常用函数对比
- org.springframework.hateoas.mvc.ControllerLinkBuilder之ClassNotFoundException的错误
- html树形结构_数据结构-线性表.md
- C#winform遍历控件判断控件类型
- C/C++程序员面试宝典-2
- AC日记——单词倒排 1.7 28
- Take it easy
- 诗歌集《触摸世界》30首摘录,对未来我是认真的
- vs code 让界面占满全屏的快捷键
- 行走在网格之间:微博用户关系模型
- idea overlays文件夹_怎样把cyanogenmod移植到你自己的设备
- Linux挂载逻辑卷
- pytorch加载VGG16及进行fine-tuning训练
- Linux常用文件目录指令(实操)
- Xposed框架动态调试第三方APP—实战演示
- Java8--20道关于Stream流的题目练习
- 努力是为了不辜负自己
热门文章
- 计算机网络工程实用技术考试,计算机网络实用技术期中考试复习题.doc
- 小R SLAM机器人树莓派3b+ ROS kinetic 环境搭建笔记
- Python——字符串大小写转化
- cordova版本更新_ionic4 APP版本更新
- 解决uni-app ios唤起扫码操作,总是要刷新才可以唤起的问题
- libcurl选项CURLOPT_WRITEDATA中的“坑”
- Django Python:完整的BUNDLE + Django真实项目2021
- Go 分布式学习利器(14)-- Go语言的错误处理
- leetcode-135 分发糖果
- Linux进程管理: 多进程编程