写这个文章源于早先对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就是封装了该方法的调用:

1public static void DeriveParameters(SqlCommand command)
2{
3      SqlConnection.SqlClientPermission.Demand();
4      if (command == null)
5      {
6            // throw an exception
7      }
8      command.DeriveParameters();
9}

来看一下SqlCommand的DeriveParameters方法:

 1internal void 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来完成的。简单看一下该方法体的内容:

 1private 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      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获取数据库元数据信息 DeriveParameters的更多相关文章

  1. mysql数据库连接池使用(三)数据库元数据信息反射数据库获取数据库信息

    1.1. mysql数据库连接池使用(三)数据库元数据信息反射数据库获取数据库信息 有时候我们想要获取到数据库的基本信息,当前程序连接的那个数据库,数据库的版本信息,数据库中有哪些表,表中都有什么字段 ...

  2. java 实现视频转换通用工具类:获取视频元数据信息(一)

    java 做视频转换主要用到开源的ffmpeg或者mencoder,还要有MP4Box. 注:由于平时都没有时间写博客,所以思路我就不写了,有问题问我,不一定马上回复. 详细介绍: ffmpeg:ht ...

  3. INFORMATION_SCHEMA获取数据库的信息

    简介 information_schema这张数据表保存了MySQL服务器所有数据库的信息.如数据库名,数据库的表,表栏的数据类型与访问权限等.再简单点,这台mysql服务器上,到底有哪些数据库.各个 ...

  4. SQL SERVER获取数据库文件信息

        MS SQL SERVER 获取当前数据库文件等信息,适用于多个版本: SELECT dbf.file_id AS FileID , dbf.name AS [FileName] , s.fi ...

  5. Delphi中客户端获取数据库更新信息(更新条数)

    1.SQL语句 from tb where xxx='XXX') //不存在,则插入数据 begin insert into tb(xxx) values('XXX') //这里自己定义,插入或更新都 ...

  6. 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 ...

  7. 【java 获取数据库信息】获取MySQL或其他数据库的详细信息

    1.首先是 通过数据库获取数据表的详细列信息 package com.sxd.mysqlInfo.test; import java.sql.Connection; import java.sql.D ...

  8. java获取数据库数据表的元数据

    Connction conn; DatabaseMetaData dmd=conn.getMetaData();//获取数据库元数据 PreparedStatment ps; ps.getParame ...

  9. JDBC 元数据信息 getMetaData()

    数据库元数据信息: import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.SQLExcept ...

随机推荐

  1. Linux必学的60个命令

    inux必学的60个命令Linux提供了大量的命令,利用它可以有效地完成大量的工作,如磁盘操作.文件存取.目录操作.进程管理.文件权限设定等.所以,在Linux系统上工作离不开使用系统提供的命令.要想 ...

  2. Educational Codeforces Round 5 B

    Problem B:http://codeforces.com/contest/616/problem/B B. Dinner with Emma 题意:一对夫妻要去餐厅吃晚饭,Emma 想去最豪华( ...

  3. Monad学习

    这是观看Cousera上的课程<Principles of Reactive Programming>中week1里的Monad一节所做的笔记. What is a Monad? What ...

  4. Injection Attacks-Log 注入

    日志注入(也称日志文件注入) 很多应用都维护着一系列面向授权用户.通过 HTML 界面展示的日志,因而成为了攻击者的首要目标,这些攻击者试图伪装其他攻击.误导日志读者,甚至对阅读和分析日志监测应用的用 ...

  5. 盘点 OSX 上最佳的 DevOps 工具

    [编者按]对于运维人员来说,他们往往需要各种各样的工具来应对工作需求,近日 Dustin Collins 通过「The Best DevOps Tools on OSX」一文对 OSX 平台上的工具进 ...

  6. iOS 10 使用相机及相簿闪退的问题修正

    http://www.cnblogs.com/onechen/p/5935579.html

  7. P73、面试题9:斐波那契数列

    题目一:写一个函数,输入n,求斐波那契数列(Fibonacci)数列的第n项,斐波那契数列的定义如下: f(n) = {0  n = 0;  1   n = 1;  f(n-1)+f(n-2)  n& ...

  8. dup和dup2函数以及管道的实现

    疑问:管道应该不是这样实现的,因为这要求修改程序的代码 dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符.它们经常用来重定向进程的stdin.stdout和stderr. ...

  9. Xcode中的iOS工程模板

    1. Application类型 我们大部分的开发工作都是从使用Application类型模板创建iOS程序开始的.该类型共包含7个模板,具体如下所示. Master-Detail Applicati ...

  10. Android开发经验记录

    一.    代码规范 定一个规范的主要目的,是为了让不同的开发人员写的代码能保持一致性,方便别人看自己的代码.另外,对个人来说,也能起到让自己看着舒服的作用. 1.      基本 * 使用UTF-8 ...