写这个文章源于早先对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上没有查到相关介绍性资料),我简单看了一下这个类的代码,在我看来,它的处理过程应该是占据了整个过程蛮大一部分资源的,这方面属于个人见解。

由于我的认识上的有限,也为了保证文章的内容无误导,暂且说到这里。这个方法的进一步讨论希望留给有兴趣的朋友。

总结

以上是我对这两个方法认识方面简单的一个概括,其实从上面的描述,也打消了我原先认为的这两个方法在获取元数据上有本质的差别。个人认为,之所以获取结构性元数据的消耗大,是因为获取逻辑的繁琐以及使用的对象的庞大,而参数信息相对而言完全属于轻量级的东西,所以所谓性能上的差异并非因为获取机制的本质差异引起的。

最新文章

  1. Elasticssearch学习教程
  2. asp.net数据控件遍历 获取当前索引
  3. java异常架构图 和几个面试题
  4. Openjudge 1.3-04 垂直直方图
  5. 数据库 基础篇3(mysql语法)
  6. YTU 3019: 螺旋方阵
  7. undefined index : HTTP_RAW_POST_DATA
  8. Web---Cookie技术(显示用户上次登录的时间、显示用户最近浏览的若干个图片(按比例缩放))
  9. Unity 网络斗地主 牌的一些算法
  10. Android Listview异步动态加载网络图片
  11. SPOJ TWOPATHS Two Paths
  12. Java Web基础入门
  13. spring事务机制
  14. L0 Regularization
  15. netty(一) netty有哪几部分构成
  16. 【WPF】 Behavior
  17. java 随机日期
  18. H5多媒体
  19. Android 编程 AMapLocationClientOption 类中的 setMockEnable (高德地图 com.amap.api.location.AMapLocationClientOption 中的类)
  20. 一个查看Cookie的便捷工具——EditThisCookie

热门文章

  1. 拼音操作工具类 - PinyinUtil.java
  2. COUNT(*)与COUNT(列名)的区别(转)
  3. [JavaScript] js 判断闰年
  4. spoj 95
  5. MySql Error: Can&#39;t update table in stored function/trigger
  6. Nagios Apache报Internal Server Error错误的解决方法
  7. MyBatis的CRUD操作
  8. 213. House Robber II
  9. Nginx+uWSGI或fastcgi部署Django项目
  10. Struts中的 saveToken的方法