在周公的博客上看到了他写的这篇博文,忍不住转载过来了。
说明
在之前周公曾写过针对不同数据库的数据库访问通用类,如针对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文件中摘取出来的:
- <system.data>
- <DbProviderFactories>
- <addname="OdbcDataProvider"invariant="System.Data.Odbc"description=".NetFrameworkDataProviderforOdbc"type="System.Data.Odbc.OdbcFactory,System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
- <addname="OleDbDataProvider"invariant="System.Data.OleDb"description=".NetFrameworkDataProviderforOleDb"type="System.Data.OleDb.OleDbFactory,System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
- <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"/>
- <addname="SqlClientDataProvider"invariant="System.Data.SqlClient"description=".NetFrameworkDataProviderforSqlServer"type="System.Data.SqlClient.SqlClientFactory,System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
- <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"/>
- </DbProviderFactories>
- </system.data>
我们可以看到每个节点的组成很固定,都有name、invariant、description、type四个属性,它们的作用分别如下:
name:数据提供程序的可识别名称。
invariant:可以以编程方式用于引用数据提供程序的名称。
description:数据提供程序的可识别描述。
type:工厂类的完全限定名,它包含用于实例化该对象的足够的信息。
注意在全局范围类不同存在相同的invariant值,比如说在machine.config中已经定义了,就不能在自己的config文件中重复定义。此外,在type中Version、Culture及PublicKeyToken也不是必须的。
刚刚已经说了程序在获取配置信息时会首先从距离自己层次关系最近的config文件查询起,一直到machine.config文件为止,那么我们可以在自己的config文件中定义非微软提供的数据库访问类库信息,在这里周公也提供了绝大部分非微软提供的数据库访问类库信息,如下:
- <addname="SQLiteDataProvider"invariant="System.Data.SQLite"description=".NetFrameworkDataProviderforSQLite"type="System.Data.SQLite.SQLiteFactory,System.Data.SQLite"/>
- <addname="InformixDataProvider"invariant="IBM.Data.Informix"description=".NetFrameworkDataProviderforInformix"type="IBM.Data.Informix.IfxFactory,IBM.Data.Informix"/>
- <addname="DB2DataProvider"invariant="IBM.Data.DB2.iSeries"description=".NetFrameworkDataProviderforDB2iSeries"type="IBM.Data.DB2.iSeries.DB2Factory,IBM.Data.DB2.iSeries"/>
- <addname="FirebirdDataProvider"invariant="FirebirdSql.Data.FirebirdClient"description="Firebird"type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory,FirebirdSql.Data.FirebirdClient"/>
- <addname="OracleDataProvider"invariant="Oracle.DataAccess.Client"description=".NetFrameworkDataProviderforOracle"type="Oracle.DataAccess.Client.OracleClientFactory,Oracle.DataAccess"/>
- <addname="PostgreSQLDataProviderDataProvider"invariant="Npgsql"description=".NetFrameworkDataProviderforPostgreSQL"type="Npgsql.NpgsqlFactory,System.Data"/>
当然,也并不是在每次开发的时候都需要添加这些信息,如果本机安装了对应的程序集,那么就无需配置,除此之外,对于根本不会访问的数据库类型也不必添加对应的程序集信息。
代码实现
- usingSystem;
- usingSystem.Collections.Generic;
- usingSystem.Text;
- usingSystem.Data;
- usingSystem.Data.Common;
- usingSystem.Reflection;
- usingSystem.Text.RegularExpressions;
- publicsealedclassDbUtility
- {
- publicstringConnectionString{get;privateset;}
- privateDbProviderFactoryproviderFactory;
- publicDbUtility(stringconnectionString,DbProviderTypeproviderType)
- {
- ConnectionString=connectionString;
- providerFactory=ProviderFactory.GetDbProviderFactory(providerType);
- if(providerFactory==null)
- {
- thrownewArgumentException("Can'tloadDbProviderFactoryforgivenvalueofproviderType");
- }
- }
- publicintExecuteNonQuery(stringsql,IList<DbParameter>parameters)
- {
- returnExecuteNonQuery(sql,parameters,CommandType.Text);
- }
- publicintExecuteNonQuery(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
- {
- using(DbCommandcommand=CreateDbCommand(sql,parameters,commandType))
- {
- command.Connection.Open();
- intaffectedRows=command.ExecuteNonQuery();
- command.Connection.Close();
- returnaffectedRows;
- }
- }
- publicDbDataReaderExecuteReader(stringsql,IList<DbParameter>parameters)
- {
- returnExecuteReader(sql,parameters,CommandType.Text);
- }
- publicDbDataReaderExecuteReader(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
- {
- DbCommandcommand=CreateDbCommand(sql,parameters,commandType);
- command.Connection.Open();
- returncommand.ExecuteReader(CommandBehavior.CloseConnection);
- }
- publicDataTableExecuteDataTable(stringsql,IList<DbParameter>parameters)
- {
- returnExecuteDataTable(sql,parameters,CommandType.Text);
- }
- publicDataTableExecuteDataTable(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
- {
- using(DbCommandcommand=CreateDbCommand(sql,parameters,commandType))
- {
- using(DbDataAdapteradapter=providerFactory.CreateDataAdapter())
- {
- adapter.SelectCommand=command;
- DataTabledata=newDataTable();
- adapter.Fill(data);
- returndata;
- }
- }
- }
- publicObjectExecuteScalar(stringsql,IList<DbParameter>parameters)
- {
- returnExecuteScalar(sql,parameters,CommandType.Text);
- }
- publicObjectExecuteScalar(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
- {
- using(DbCommandcommand=CreateDbCommand(sql,parameters,commandType))
- {
- command.Connection.Open();
- objectresult=command.ExecuteScalar();
- command.Connection.Close();
- returnresult;
- }
- }
- privateDbCommandCreateDbCommand(stringsql,IList<DbParameter>parameters,CommandTypecommandType)
- {
- DbConnectionconnection=providerFactory.CreateConnection();
- DbCommandcommand=providerFactory.CreateCommand();
- connection.ConnectionString=ConnectionString;
- command.CommandText=sql;
- command.CommandType=commandType;
- command.Connection=connection;
- if(!(parameters==null||parameters.Count==0))
- {
- foreach(DbParameterparameterinparameters)
- {
- command.Parameters.Add(parameter);
- }
- }
- returncommand;
- }
- }
- publicenumDbProviderType:byte
- {
- SqlServer,
- MySql,
- SQLite,
- Oracle,
- ODBC,
- OleDb,
- Firebird,
- PostgreSql,
- DB2,
- Informix,
- SqlServerCe
- }
- publicclassProviderFactory
- {
- privatestaticDictionary<DbProviderType,string>providerInvariantNames=newDictionary<DbProviderType,string>();
- privatestaticDictionary<DbProviderType,DbProviderFactory>providerFactoies=newDictionary<DbProviderType,DbProviderFactory>(20);
- staticProviderFactory()
- {
- providerInvariantNames.Add(DbProviderType.SqlServer,"System.Data.SqlClient");
- providerInvariantNames.Add(DbProviderType.OleDb,"System.Data.OleDb");
- providerInvariantNames.Add(DbProviderType.ODBC,"System.Data.ODBC");
- providerInvariantNames.Add(DbProviderType.Oracle,"Oracle.DataAccess.Client");
- providerInvariantNames.Add(DbProviderType.MySql,"MySql.Data.MySqlClient");
- providerInvariantNames.Add(DbProviderType.SQLite,"System.Data.SQLite");
- providerInvariantNames.Add(DbProviderType.Firebird,"FirebirdSql.Data.Firebird");
- providerInvariantNames.Add(DbProviderType.PostgreSql,"Npgsql");
- providerInvariantNames.Add(DbProviderType.DB2,"IBM.Data.DB2.iSeries");
- providerInvariantNames.Add(DbProviderType.Informix,"IBM.Data.Informix");
- providerInvariantNames.Add(DbProviderType.SqlServerCe,"System.Data.SqlServerCe");
- }
- publicstaticstringGetProviderInvariantName(DbProviderTypeproviderType)
- {
- returnproviderInvariantNames[providerType];
- }
- publicstaticDbProviderFactoryGetDbProviderFactory(DbProviderTypeproviderType)
- {
- if(!providerFactoies.ContainsKey(providerType))
- {
- providerFactoies.Add(providerType,ImportDbProviderFactory(providerType));
- }
- returnproviderFactoies[providerType];
- }
- privatestaticDbProviderFactoryImportDbProviderFactory(DbProviderTypeproviderType)
- {
- stringproviderName=providerInvariantNames[providerType];
- DbProviderFactoryfactory=null;
- try
- {
- factory=DbProviderFactories.GetFactory(providerName);
- }
- catch(ArgumentExceptione)
- {
- factory=null;
- }
- returnfactory;
- }
- }
用法举例,访问SQLite数据库:
- stringconnectionString=@"DataSource=D:\VS2008\NetworkTime\CrawlApplication\CrawlApplication.db3";
- stringsql="SELECT*FROMWeibo_MediaorderbyIddesclimit0,20000";
- DbUtilitydb=newDbUtility(connectionString,DbProviderType.SQLite);
- DataTabledata=db.ExecuteDataTable(sql,null);
- DbDataReaderreader=db.ExecuteReader(sql,null);
- reader.Close();
用法举例,访问MySQL:
- stringconnectionString=@"Server=localhost;Database=crawldb;Uid=root;Pwd=root;Port=3306;";
- stringsql="SELECT*FROMWeibo_MediaorderbyIddesclimit0,20000";
- DbUtilitydb=newDbUtility(connectionString,DbProviderType.MySql);
- DataTabledata=db.ExecuteDataTable(sql,null);
- DbDataReaderreader=db.ExecuteReader(sql,null);
- reader.Close();
从上面的代码可以看出,使用这个通用的数据库通用类和以前针对特定的数据库通用类没有什么区别,这一切都是DbProviderFactory这个类的功劳。
总结
设计模式在编程中对于应对变化确实非常有用,因为在ADO.NET中存在着这么一个工厂模式,轻而易举地就解决了针对不同数据库访问的问题。
周公
2011-07-20
分享到:
相关推荐
**ADO.NET**(ActiveX Data Objects .NET)是Microsoft为.NET框架提供的数据访问技术,它允许开发人员使用.NET语言如C#、VB.NET等与各种数据库进行交互。ADO.NET 2.0在原有的基础上进行了大量的改进和增强,引入了...
书中深入剖析了ado.net的本质,探索了类、接口、属性和方法的工作原理,同时还为其他数据访问api(包括oledb,ado,odbc和jdbc)的程序员,提供了有价值的参考材料。本书适合具有一定数据库基础的开发人员阅读,也可...
ADO.NET通用数据库访问类是开发者为了简化数据库操作而设计的抽象层,它封装了基本的数据库连接、命令执行、数据读取等功能,使得在不同数据库间切换时无需大量修改代码。 在给定的代码示例中,我们看到一个名为`...
ADO.NET的架构设计充分体现了微软对数据访问机制的深刻理解。它主要由两个核心组件构成:“Connection”对象和“Command”对象。“Connection”用于建立与数据源的通信通道,而“Command”则用于执行SQL语句或存储...
- **ADO.NET体系结构概览**:描述了ADO.NET的核心组件,包括数据提供程序和数据集(DataSet)。 ##### 3. 第2章:.NET数据提供程序 - **SQL Server数据提供程序**:具体分析了如何使用针对SQL Server的特定数据提供...
ADO(ActiveX Data Objects)是基于COM(Component Object Model)技术的数据访问接口,主要用于客户端与数据库的交互,而ADO.NET是.NET Framework的一部分,它构建在.NET体系架构上,提供了一种全新的数据访问模型...
第3部分 ASP.NET数据库程序设计 第6章 ADO.NET数据访问接口,介绍了ADO.NET的结构、ADO.NET的常用对象,以及3种重要的数据控件。 第7章 使用ADO.NET操作SQL Server数据库,介绍了SQL Server的...
ASP.NET和C#是微软.NET框架的一部分,常用于构建Web应用程序,可以利用ADO.NET接口与DB2进行数据交互。Java,作为一种跨平台的语言,也有JDBC(Java Database Connectivity)用于连接DB2。Perl则常用于数据处理和...
##### ADO.NET体系结构 ADO.NET是.NET框架中的数据访问技术,它通过.NET数据提供者(如SQL Server .Net数据提供者、OLE DB .Net数据提供者、ODBC .Net数据提供者)支持不同类型的数据库。每个数据提供者包含四个...
中间层包括一些重要的系统服务,如ADO.Net,XML类,组件模型,安全性等,这些服务在总架构的控制之下,可以在各处通用,而且调用方式与语言无关。顶层主要提供给程序开发者开发Window窗体和WEB表单,WEB服务、应用...
Asp.NET的控件和MVC模式可以用于构建表示层,而ADO.NET或Entity Framework可能用于数据访问。 【开发工具】 开发Asp.NET应用通常使用Visual Studio IDE,它提供了丰富的集成开发环境,支持代码编辑、调试、部署等。...
在.NET环境中,ADO.NET或Entity Framework等技术用于数据库操作,提供高效的数据存取和事务处理能力。 四、用户界面 UI部分通常采用Windows Forms或WPF进行开发,提供友好的图形化操作界面。利用.NET框架的控件库...
这份试卷涵盖了.NET Framework 的多个核心知识点,包括CLR(Common Language Runtime)、编程模型、安全机制、ADO.NET、ASP.NET以及.NET Framework的基础架构。 一、填空题 1. .NET 平台的五个组成部分除了工具...
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 本章...
数据库访问方面,ADO.NET是.NET框架中的重要组件,它提供了与SQL Server、Oracle等数据库交互的接口。Entity Framework作为ORM(对象关系映射)框架,可以简化数据库操作,使开发者能以面向对象的方式处理数据。 ...
第11章 C#数据库编程与ADO.NET ADO.NET体系结构 数据提供程序 数据集DataSet 第12章 C#Web应用程序开发与ASP.NET Web Form ASP.NET的工作原理 使用ASP.NET 配置ASP.NET 第13章 文件与...
接着,利用ADO.NET或LINQ to SQL技术与数据库交互,实现商品信息的存储与检索。最后,通过身份验证和授权机制确保用户数据的安全。 #### 五、结论 综上所述,ASP.NET作为一种成熟的Web应用程序开发框架,在电子...