`
hududanyzd
  • 浏览: 772098 次
文章分类
社区版块
存档分类
最新评论

利用ADO.NET的体系架构打造通用的数据库访问通用类

 
阅读更多

在周公的博客上看到了他写的这篇博文,忍不住转载过来了。

说明
在之前周公曾写过针对不同数据库的数据库访问通用类,如针对SQLite的、针对Access的、针对Oracle的、针对SQL Server的。总结了这些通用类的通用方法,其实无非就是针对不同类型的数据库创建Connection、Command、DataAdapter及DataReader,然后对外提供范围ExecuteTable(),ExecuteDataReader、ExecuteScalar()及ExecuteNonQuery()方法,有了这四个方法我们就可以完成针对数据库的所有操作了。在之前周公就曾经想过将这些数据库访问通用类提炼出来,写成一个针对各种数据库通用的数据库通用类,按照一般的方法写的话就需要收集常用的数据库访问类库才能编译,如果用反射的办法虽然也可以解决问题,但是周公又不愿意代码里到处都是反射的代码,在针对.NET Framework进行分析的基础上,写成了今天的数据库访问通用类。


分析
请先看下图:

在System.Data.Common命名空间下定义了针对所有数据库的Connection、Command、DataAdapter及DataReader对象的抽象类,分别是DbConnection、DbCommand、DbDataAdapter及DbDataReader,在这些抽象类中定义了针对所有数据库的通用方法和属性,不光是在.NET Framework中微软提供的针对ODBC、OleDB、Oracle、SQL Server类中如此,在微软未提供、由数据库厂商提供的ADO.NET类也是如此(假如有一天你自己也开发了一个数据库,为了提供给.NET开发人员使用,也应该遵循这个规定)。除此之外,在System.Data.Common命名空间下还提供了两个类,一个是DbProviderFactories,另一个是DbProviderFactory,DbProviderFactories类提供的方法有两个(包括一个重载形式),GetFactoryClasses()方法返回在系统中注册的DbProviderFactory类(如果在系统中注册了,就会在machine.config中的<configuration><system.data><DbProviderFactories>下添加针对这个数据库的相关信息),GetFactory()的两个重载方法都是返回一个指定的DbProviderFactor抽象类,在DbProviderFactory抽象类中又定义了创建DbConnection、DbCommand、DbDataAdapter及DbDataReader的方法。而不同的数据库访问类程序集中又都提供了对DbProviderFactory这个抽象类的实现(包括所有由数据库厂商提供的ADO.NET类)。所以我们要解决的问题是如何创建针对不同数据库的DbProviderFactory这个抽象类的实现。


解决
我们知道machine.config是所有config文件的鼻祖,包括web.config和app.config,程序在获取配置信息时会首先从距离自己层次关系最近的config文件查询起,一直到machine.config文件为止。那么我们就首先从自己的config文件做章。
下面的一段代码是从machine.config文件中摘取出来的:

  1. <system.data>
  2. <DbProviderFactories>
  3. <addname="OdbcDataProvider"invariant="System.Data.Odbc"description=".NetFrameworkDataProviderforOdbc"type="System.Data.Odbc.OdbcFactory,System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
  4. <addname="OleDbDataProvider"invariant="System.Data.OleDb"description=".NetFrameworkDataProviderforOleDb"type="System.Data.OleDb.OleDbFactory,System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
  5. <addname="OracleClientDataProvider"invariant="System.Data.OracleClient"description=".NetFrameworkDataProviderforOracle"type="System.Data.OracleClient.OracleClientFactory,System.Data.OracleClient,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
  6. <addname="SqlClientDataProvider"invariant="System.Data.SqlClient"description=".NetFrameworkDataProviderforSqlServer"type="System.Data.SqlClient.SqlClientFactory,System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
  7. <addname="MicrosoftSQLServerCompactDataProvider"invariant="System.Data.SqlServerCe.3.5"description=".NETFrameworkDataProviderforMicrosoftSQLServerCompact"type="System.Data.SqlServerCe.SqlCeProviderFactory,System.Data.SqlServerCe,Version=3.5.0.0,Culture=neutral,PublicKeyToken=89845dcd8080cc91"/>
  8. </DbProviderFactories>
  9. </system.data>


我们可以看到每个节点的组成很固定,都有name、invariant、description、type四个属性,它们的作用分别如下:
name:数据提供程序的可识别名称。
invariant:可以以编程方式用于引用数据提供程序的名称。
description:数据提供程序的可识别描述。
type:工厂类的完全限定名,它包含用于实例化该对象的足够的信息。
注意在全局范围类不同存在相同的invariant值,比如说在machine.config中已经定义了,就不能在自己的config文件中重复定义。此外,在type中Version、Culture及PublicKeyToken也不是必须的。
刚刚已经说了程序在获取配置信息时会首先从距离自己层次关系最近的config文件查询起,一直到machine.config文件为止,那么我们可以在自己的config文件中定义非微软提供的数据库访问类库信息,在这里周公也提供了绝大部分非微软提供的数据库访问类库信息,如下:

  1. <addname="SQLiteDataProvider"invariant="System.Data.SQLite"description=".NetFrameworkDataProviderforSQLite"type="System.Data.SQLite.SQLiteFactory,System.Data.SQLite"/>
  2. <addname="InformixDataProvider"invariant="IBM.Data.Informix"description=".NetFrameworkDataProviderforInformix"type="IBM.Data.Informix.IfxFactory,IBM.Data.Informix"/>
  3. <addname="DB2DataProvider"invariant="IBM.Data.DB2.iSeries"description=".NetFrameworkDataProviderforDB2iSeries"type="IBM.Data.DB2.iSeries.DB2Factory,IBM.Data.DB2.iSeries"/>
  4. <addname="FirebirdDataProvider"invariant="FirebirdSql.Data.FirebirdClient"description="Firebird"type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory,FirebirdSql.Data.FirebirdClient"/>
  5. <addname="OracleDataProvider"invariant="Oracle.DataAccess.Client"description=".NetFrameworkDataProviderforOracle"type="Oracle.DataAccess.Client.OracleClientFactory,Oracle.DataAccess"/>
  6. <addname="PostgreSQLDataProviderDataProvider"invariant="Npgsql"description=".NetFrameworkDataProviderforPostgreSQL"type="Npgsql.NpgsqlFactory,System.Data"/>


当然,也并不是在每次开发的时候都需要添加这些信息,如果本机安装了对应的程序集,那么就无需配置,除此之外,对于根本不会访问的数据库类型也不必添加对应的程序集信息。
代码实现

  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Text;
  4. usingSystem.Data;
  5. usingSystem.Data.Common;
  6. usingSystem.Reflection;
  7. usingSystem.Text.RegularExpressions;
  8. ///<summary>
  9. ///通用数据库访问类,封装了对数据库的常见操作
  10. ///作者:周公
  11. ///日期:2011-07-18
  12. ///博客地址:http://blog.csdn.net/zhoufoxcn或http://zhoufoxcn.blog.51cto.com
  13. ///说明:(1)任何人都可以免费使用,请尽量保持此段说明。
  14. ///(2)这个版本还不是最终版本,有任何意见或建议请到http://weibo.com/zhoufoxcn处留言。
  15. ///</summary>
  16. publicsealedclassDbUtility
  17. {
  18. publicstringConnectionString{get;privateset;}
  19. privateDbProviderFactoryproviderFactory;
  20. ///<summary>
  21. ///构造函数
  22. ///</summary>
  23. ///<paramname="connectionString">数据库连接字符串</param>
  24. ///<paramname="providerType">数据库类型枚举,参见<paramrefname="providerType"/></param>
  25. publicDbUtility(stringconnectionString,DbProviderTypeproviderType)
  26. {
  27. ConnectionString=connectionString;
  28. providerFactory=ProviderFactory.GetDbProviderFactory(providerType);
  29. if(providerFactory==null)
  30. {
  31. thrownewArgumentException("Can'tloadDbProviderFactoryforgivenvalueofproviderType");
  32. }
  33. }
  34. ///<summary>
  35. ///对数据库执行增删改操作,返回受影响的行数。
  36. ///</summary>
  37. ///<paramname="sql">要执行的增删改的SQL语句</param>
  38. ///<paramname="parameters">执行增删改语句所需要的参数</param>
  39. ///<returns></returns>
  40. publicintExecuteNonQuery(stringsql,IList<DbParameter>parameters)
  41. {
  42. returnExecuteNonQuery(sql,parameters,CommandType.Text);
  43. }
  44. ///<summary>
  45. ///对数据库执行增删改操作,返回受影响的行数。
  46. ///</summary>
  47. ///<paramname="sql">要执行的增删改的SQL语句</param>
  48. ///<paramname="parameters">执行增删改语句所需要的参数</param>
  49. ///<paramname="commandType">执行的SQL语句的类型</param>
  50. ///<returns></returns>
  51. publicintExecuteNonQuery(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
  52. {
  53. using(DbCommandcommand=CreateDbCommand(sql,parameters,commandType))
  54. {
  55. command.Connection.Open();
  56. intaffectedRows=command.ExecuteNonQuery();
  57. command.Connection.Close();
  58. returnaffectedRows;
  59. }
  60. }
  61. ///<summary>
  62. ///执行一个查询语句,返回一个关联的DataReader实例
  63. ///</summary>
  64. ///<paramname="sql">要执行的查询语句</param>
  65. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  66. ///<returns></returns>
  67. publicDbDataReaderExecuteReader(stringsql,IList<DbParameter>parameters)
  68. {
  69. returnExecuteReader(sql,parameters,CommandType.Text);
  70. }
  71. ///<summary>
  72. ///执行一个查询语句,返回一个关联的DataReader实例
  73. ///</summary>
  74. ///<paramname="sql">要执行的查询语句</param>
  75. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  76. ///<paramname="commandType">执行的SQL语句的类型</param>
  77. ///<returns></returns>
  78. publicDbDataReaderExecuteReader(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
  79. {
  80. DbCommandcommand=CreateDbCommand(sql,parameters,commandType);
  81. command.Connection.Open();
  82. returncommand.ExecuteReader(CommandBehavior.CloseConnection);
  83. }
  84. ///<summary>
  85. ///执行一个查询语句,返回一个包含查询结果的DataTable
  86. ///</summary>
  87. ///<paramname="sql">要执行的查询语句</param>
  88. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  89. ///<returns></returns>
  90. publicDataTableExecuteDataTable(stringsql,IList<DbParameter>parameters)
  91. {
  92. returnExecuteDataTable(sql,parameters,CommandType.Text);
  93. }
  94. ///<summary>
  95. ///执行一个查询语句,返回一个包含查询结果的DataTable
  96. ///</summary>
  97. ///<paramname="sql">要执行的查询语句</param>
  98. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  99. ///<paramname="commandType">执行的SQL语句的类型</param>
  100. ///<returns></returns>
  101. publicDataTableExecuteDataTable(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
  102. {
  103. using(DbCommandcommand=CreateDbCommand(sql,parameters,commandType))
  104. {
  105. using(DbDataAdapteradapter=providerFactory.CreateDataAdapter())
  106. {
  107. adapter.SelectCommand=command;
  108. DataTabledata=newDataTable();
  109. adapter.Fill(data);
  110. returndata;
  111. }
  112. }
  113. }
  114. ///<summary>
  115. ///执行一个查询语句,返回查询结果的第一行第一列
  116. ///</summary>
  117. ///<paramname="sql">要执行的查询语句</param>
  118. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  119. ///<returns></returns>
  120. publicObjectExecuteScalar(stringsql,IList<DbParameter>parameters)
  121. {
  122. returnExecuteScalar(sql,parameters,CommandType.Text);
  123. }
  124. ///<summary>
  125. ///执行一个查询语句,返回查询结果的第一行第一列
  126. ///</summary>
  127. ///<paramname="sql">要执行的查询语句</param>
  128. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  129. ///<paramname="commandType">执行的SQL语句的类型</param>
  130. ///<returns></returns>
  131. publicObjectExecuteScalar(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
  132. {
  133. using(DbCommandcommand=CreateDbCommand(sql,parameters,commandType))
  134. {
  135. command.Connection.Open();
  136. objectresult=command.ExecuteScalar();
  137. command.Connection.Close();
  138. returnresult;
  139. }
  140. }
  141. ///<summary>
  142. ///创建一个DbCommand对象
  143. ///</summary>
  144. ///<paramname="sql">要执行的查询语句</param>
  145. ///<paramname="parameters">执行SQL查询语句所需要的参数</param>
  146. ///<paramname="commandType">执行的SQL语句的类型</param>
  147. ///<returns></returns>
  148. privateDbCommandCreateDbCommand(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
  149. {
  150. DbConnectionconnection=providerFactory.CreateConnection();
  151. DbCommandcommand=providerFactory.CreateCommand();
  152. connection.ConnectionString=ConnectionString;
  153. command.CommandText=sql;
  154. command.CommandType=commandType;
  155. command.Connection=connection;
  156. if(!(parameters==null||parameters.Count==0))
  157. {
  158. foreach(DbParameterparameterinparameters)
  159. {
  160. command.Parameters.Add(parameter);
  161. }
  162. }
  163. returncommand;
  164. }
  165. }
  166. ///<summary>
  167. ///数据库类型枚举
  168. ///</summary>
  169. publicenumDbProviderType:byte
  170. {
  171. SqlServer,
  172. MySql,
  173. SQLite,
  174. Oracle,
  175. ODBC,
  176. OleDb,
  177. Firebird,
  178. PostgreSql,
  179. DB2,
  180. Informix,
  181. SqlServerCe
  182. }
  183. ///<summary>
  184. ///DbProviderFactory工厂类
  185. ///</summary>
  186. publicclassProviderFactory
  187. {
  188. privatestaticDictionary<DbProviderType,string>providerInvariantNames=newDictionary<DbProviderType,string>();
  189. privatestaticDictionary<DbProviderType,DbProviderFactory>providerFactoies=newDictionary<DbProviderType,DbProviderFactory>(20);
  190. staticProviderFactory()
  191. {
  192. //加载已知的数据库访问类的程序集
  193. providerInvariantNames.Add(DbProviderType.SqlServer,"System.Data.SqlClient");
  194. providerInvariantNames.Add(DbProviderType.OleDb,"System.Data.OleDb");
  195. providerInvariantNames.Add(DbProviderType.ODBC,"System.Data.ODBC");
  196. providerInvariantNames.Add(DbProviderType.Oracle,"Oracle.DataAccess.Client");
  197. providerInvariantNames.Add(DbProviderType.MySql,"MySql.Data.MySqlClient");
  198. providerInvariantNames.Add(DbProviderType.SQLite,"System.Data.SQLite");
  199. providerInvariantNames.Add(DbProviderType.Firebird,"FirebirdSql.Data.Firebird");
  200. providerInvariantNames.Add(DbProviderType.PostgreSql,"Npgsql");
  201. providerInvariantNames.Add(DbProviderType.DB2,"IBM.Data.DB2.iSeries");
  202. providerInvariantNames.Add(DbProviderType.Informix,"IBM.Data.Informix");
  203. providerInvariantNames.Add(DbProviderType.SqlServerCe,"System.Data.SqlServerCe");
  204. }
  205. ///<summary>
  206. ///获取指定数据库类型对应的程序集名称
  207. ///</summary>
  208. ///<paramname="providerType">数据库类型枚举</param>
  209. ///<returns></returns>
  210. publicstaticstringGetProviderInvariantName(DbProviderTypeproviderType)
  211. {
  212. returnproviderInvariantNames[providerType];
  213. }
  214. ///<summary>
  215. ///获取指定类型的数据库对应的DbProviderFactory
  216. ///</summary>
  217. ///<paramname="providerType">数据库类型枚举</param>
  218. ///<returns></returns>
  219. publicstaticDbProviderFactoryGetDbProviderFactory(DbProviderTypeproviderType)
  220. {
  221. //如果还没有加载,则加载该DbProviderFactory
  222. if(!providerFactoies.ContainsKey(providerType))
  223. {
  224. providerFactoies.Add(providerType,ImportDbProviderFactory(providerType));
  225. }
  226. returnproviderFactoies[providerType];
  227. }
  228. ///<summary>
  229. ///加载指定数据库类型的DbProviderFactory
  230. ///</summary>
  231. ///<paramname="providerType">数据库类型枚举</param>
  232. ///<returns></returns>
  233. privatestaticDbProviderFactoryImportDbProviderFactory(DbProviderTypeproviderType)
  234. {
  235. stringproviderName=providerInvariantNames[providerType];
  236. DbProviderFactoryfactory=null;
  237. try
  238. {
  239. //从全局程序集中查找
  240. factory=DbProviderFactories.GetFactory(providerName);
  241. }
  242. catch(ArgumentExceptione)
  243. {
  244. factory=null;
  245. }
  246. returnfactory;
  247. }
  248. }


用法举例,访问SQLite数据库:

  1. stringconnectionString=@"DataSource=D:\VS2008\NetworkTime\CrawlApplication\CrawlApplication.db3";
  2. stringsql="SELECT*FROMWeibo_MediaorderbyIddesclimit0,20000";
  3. DbUtilitydb=newDbUtility(connectionString,DbProviderType.SQLite);
  4. DataTabledata=db.ExecuteDataTable(sql,null);
  5. DbDataReaderreader=db.ExecuteReader(sql,null);
  6. reader.Close();

用法举例,访问MySQL:

  1. stringconnectionString=@"Server=localhost;Database=crawldb;Uid=root;Pwd=root;Port=3306;";
  2. stringsql="SELECT*FROMWeibo_MediaorderbyIddesclimit0,20000";
  3. DbUtilitydb=newDbUtility(connectionString,DbProviderType.MySql);
  4. DataTabledata=db.ExecuteDataTable(sql,null);
  5. DbDataReaderreader=db.ExecuteReader(sql,null);
  6. reader.Close();

从上面的代码可以看出,使用这个通用的数据库通用类和以前针对特定的数据库通用类没有什么区别,这一切都是DbProviderFactory这个类的功劳。
总结
设计模式在编程中对于应对变化确实非常有用,因为在ADO.NET中存在着这么一个工厂模式,轻而易举地就解决了针对不同数据库访问的问题。

周公
2011-07-20



分享到:
评论

相关推荐

    ADO.NET通用数据库访问类

    主要为大家介绍了ADO.NET通用数据库访问类,利用ADO.NET的体系架构打造通用的数据库访问通用类,感兴趣的小伙伴们可以参考一下

    ADO.NET本质论.pdf

    书中深入剖析了ado.net的本质,探索了类、接口、属性和方法的工作原理,同时还为其他数据访问api(包括oledb,ado,odbc和jdbc)的程序员,提供了有价值的参考材料。本书适合具有一定数据库基础的开发人员阅读,也可...

    《ASP.NET程序设计实用教程》配套资料-cd, ppt

     第3部分 ASP.NET数据库程序设计  第6章 ADO.NET数据访问接口,介绍了ADO.NET的结构、ADO.NET的常用对象,以及3种重要的数据控件。  第7章 使用ADO.NET操作SQL Server数据库,介绍了SQL Server的...

    asp.net知识库

    DbHelperV2 - Teddy的通用数据库访问组件设计和思考 也论该不该在项目中使用存储过程代替SQL语句 如何使数据库中的表更有弹性,更易于扩展 存储过程——天使还是魔鬼 如何获取MSSQLServer,Oracel,Access中的数据字典...

    ASP.NET在线测评系统

    中间层包括一些重要的系统服务,如ADO.Net,XML类,组件模型,安全性等,这些服务在总架构的控制之下,可以在各处通用,而且调用方式与语言无关。顶层主要提供给程序开发者开发Window窗体和WEB表单,WEB服务、应用...

    亮剑.NET深入体验与实战精要2

    6.3.7 项目案例:通用自定义XML配置类 263 6.4 ADO.NET与XML 266 6.4.1 读XML文档到DataSet 266 6.4.2 DataSet转为XML文档 267 6.5 项目案例1:实现网站的RSS应用 267 6.6 项目案例2:在线实现RSS阅读器 270 本章...

    ASP.NET Night Words

    15.1 数据库通用类 287 15.1.1 sql server数据库访问 15.1.1 通用类 288 15.1.2 using关键字的用法 293 15.2 三层架构的定义及代码示例 294 15.3 三层架构的特点 309 15.4 对三层架构的一点扩充 309 15.5 ...

    Visual C# 2010程序设计教程PPT

    第11章 C#数据库编程与ADO.NET ADO.NET体系结构 数据提供程序 数据集DataSet 第12章 C#Web应用程序开发与ASP.NET Web Form ASP.NET的工作原理 使用ASP.NET 配置ASP.NET 第13章 文件与...

    亮剑.NET深入体验与实战精要3

    6.3.7 项目案例:通用自定义XML配置类 263 6.4 ADO.NET与XML 266 6.4.1 读XML文档到DataSet 266 6.4.2 DataSet转为XML文档 267 6.5 项目案例1:实现网站的RSS应用 267 6.6 项目案例2:在线实现RSS阅读器 270 本章...

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    10.2.2 ado.net的基本名称空间和类 398 10.2.3 ado.net组件 399 10.3 .net数据提供程序 400 10.3.1 connection对象 400 10.3.2 command对象 401 10.3.3 通过command对象使用存储过程 402 10.3.4 ...

    JAVA上百实例源码以及开源项目

     Java波浪文字,一个利用Java处理字符的实例,可以设置运动方向参数,显示文本的字符数组,高速文本颜色,显示字体的 FontMetrics对象,得到Graphics实例,得到Image实例,填充颜色数组数据,初始化颜色数组。...

    JAVA上百实例源码以及开源项目源代码

    日历表格面板 [ConfigLine.java] 控制条类 [RoundBox.java] 限定选择控件 [MonthMaker.java] 月份表算法类 [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接...

Global site tag (gtag.js) - Google Analytics