`
380071587
  • 浏览: 446057 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

利用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文件中摘取出来的:

<system.data>
	<DbProviderFactories>
		<add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
		<add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
		<add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
		<add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
		<add name="Microsoft SQL Server Compact Data Provider" invariant="System.Data.SqlServerCe.3.5" description=".NET Framework Data Provider for Microsoft SQL Server Compact" 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文件中定义非微软提供的数据库访问类库信息,在这里周公也提供了绝大部分非微软提供的数据库访问类库信息,如下:

<add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
<add name="Informix Data Provider" invariant="IBM.Data.Informix" description=".Net Framework Data Provider for Informix" type="IBM.Data.Informix.IfxFactory, IBM.Data.Informix" />
<add name="DB2 Data Provider" invariant="IBM.Data.DB2.iSeries" description=".Net Framework Data Provider for DB2 iSeries" type="IBM.Data.DB2.iSeries.DB2Factory, IBM.Data.DB2.iSeries" />
<add name="Firebird Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description="Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient"/>
<add name="Oracle Data Provider" invariant="Oracle.DataAccess.Client" description=".Net Framework Data Provider for Oracle" type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess" />
<add name="PostgreSQL Data Provider Data Provider" invariant="Npgsql" description=".Net Framework Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, System.Data" />


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

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Text.RegularExpressions;

/// <summary>
/// 通用数据库访问类,封装了对数据库的常见操作
/// 作者:周公
/// 日期:2011-07-18
/// 博客地址:http://blog.csdn.net/zhoufoxcn 或http://zhoufoxcn.blog.51cto.com
/// 说明:(1)任何人都可以免费使用,请尽量保持此段说明。
///      (2)这个版本还不是最终版本,有任何意见或建议请到http://weibo.com/zhoufoxcn处留言。
/// </summary>
public sealed class DbUtility
{
	public string ConnectionString { get; private set; }
	private DbProviderFactory providerFactory;
	/// <summary>
	/// 构造函数
	/// </summary>
	/// <param name="connectionString">数据库连接字符串</param>
	/// <param name="providerType">数据库类型枚举,参见<paramref name="providerType"/></param>
	public DbUtility(string connectionString,DbProviderType providerType)
	{
		ConnectionString = connectionString;
		providerFactory = ProviderFactory.GetDbProviderFactory(providerType);
		if (providerFactory == null)
		{
			throw new ArgumentException("Can't load DbProviderFactory for given value of providerType");
		}
	}
	/// <summary>   
	/// 对数据库执行增删改操作,返回受影响的行数。   
	/// </summary>   
	/// <param name="sql">要执行的增删改的SQL语句</param>   
	/// <param name="parameters">执行增删改语句所需要的参数</param>
	/// <returns></returns>  
	public int ExecuteNonQuery(string sql,IList<DbParameter> parameters)
	{
		return ExecuteNonQuery(sql, parameters, CommandType.Text);
	}
	/// <summary>   
	/// 对数据库执行增删改操作,返回受影响的行数。   
	/// </summary>   
	/// <param name="sql">要执行的增删改的SQL语句</param>   
	/// <param name="parameters">执行增删改语句所需要的参数</param>
	/// <param name="commandType">执行的SQL语句的类型</param>
	/// <returns></returns>
	public int ExecuteNonQuery(string sql,IList<DbParameter> parameters, CommandType commandType)
	{
		using (DbCommand command = CreateDbCommand(sql, parameters, commandType))
		{
			command.Connection.Open();
			int affectedRows=command.ExecuteNonQuery();
			command.Connection.Close();
			return affectedRows;
		}
	}
	/// <summary>   
	/// 执行一个查询语句,返回一个关联的DataReader实例   
	/// </summary>   
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>
	/// <returns></returns> 
	public DbDataReader ExecuteReader(string sql, IList<DbParameter> parameters)
	{
		return ExecuteReader(sql, parameters, CommandType.Text);
	}
	/// <summary>   
	/// 执行一个查询语句,返回一个关联的DataReader实例   
	/// </summary>   
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>
	/// <param name="commandType">执行的SQL语句的类型</param>
	/// <returns></returns> 
	public DbDataReader ExecuteReader(string sql, IList<DbParameter> parameters, CommandType commandType)
	{
		DbCommand command = CreateDbCommand(sql, parameters, commandType);
		command.Connection.Open();
		return command.ExecuteReader(CommandBehavior.CloseConnection);
	}
	/// <summary>   
	/// 执行一个查询语句,返回一个包含查询结果的DataTable   
	/// </summary>   
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>
	/// <returns></returns>
	public DataTable ExecuteDataTable(string sql, IList<DbParameter> parameters)
	{
		return ExecuteDataTable(sql, parameters, CommandType.Text);
	}
	/// <summary>   
	/// 执行一个查询语句,返回一个包含查询结果的DataTable   
	/// </summary>   
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>
	/// <param name="commandType">执行的SQL语句的类型</param>
	/// <returns></returns>
	public DataTable ExecuteDataTable(string sql, IList<DbParameter> parameters, CommandType commandType)
	{
		using (DbCommand command = CreateDbCommand(sql, parameters, commandType))
		{
			using (DbDataAdapter adapter = providerFactory.CreateDataAdapter())
			{
				adapter.SelectCommand = command;
				DataTable data = new DataTable();
				adapter.Fill(data);
				return data;
			}
		}
	}
	/// <summary>   
	/// 执行一个查询语句,返回查询结果的第一行第一列   
	/// </summary>   
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>   
	/// <returns></returns>   
	public Object ExecuteScalar(string sql, IList<DbParameter> parameters)
	{
		return ExecuteScalar(sql, parameters, CommandType.Text);
	}

	/// <summary>   
	/// 执行一个查询语句,返回查询结果的第一行第一列   
	/// </summary>   
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>   
	/// <param name="commandType">执行的SQL语句的类型</param>
	/// <returns></returns>   
	public Object ExecuteScalar(string sql, IList<DbParameter> parameters,CommandType commandType)
	{
		using (DbCommand command = CreateDbCommand(sql, parameters, commandType))
		{
			command.Connection.Open();
			object result = command.ExecuteScalar();
			command.Connection.Close();
			return result;
		}
	}
	/// <summary>
	/// 创建一个DbCommand对象
	/// </summary>
	/// <param name="sql">要执行的查询语句</param>   
	/// <param name="parameters">执行SQL查询语句所需要的参数</param>
	/// <param name="commandType">执行的SQL语句的类型</param>
	/// <returns></returns>
	private DbCommand CreateDbCommand(string sql, IList<DbParameter> parameters, CommandType commandType)
	{
		DbConnection connection=providerFactory.CreateConnection();
		DbCommand command = providerFactory.CreateCommand();
		connection.ConnectionString = ConnectionString;
		command.CommandText = sql;
		command.CommandType = commandType;
		command.Connection = connection;
		if (!(parameters == null || parameters.Count == 0))
		{
			foreach (DbParameter parameter in parameters)
			{
				command.Parameters.Add(parameter);
			}
		}
		return command;
	}
}
/// <summary>
/// 数据库类型枚举
/// </summary>
public enum DbProviderType:byte
{
	SqlServer,
	MySql,
	SQLite,
	Oracle,
	ODBC,
	OleDb,
	Firebird,
	PostgreSql,
	DB2,
	Informix,
	SqlServerCe
}
/// <summary>
/// DbProviderFactory工厂类
/// </summary>
public class ProviderFactory
{
	private static Dictionary<DbProviderType, string> providerInvariantNames = new Dictionary<DbProviderType, string>();
	private static Dictionary<DbProviderType, DbProviderFactory> providerFactoies = new Dictionary<DbProviderType, DbProviderFactory>(20);
	static ProviderFactory()
	{
		//加载已知的数据库访问类的程序集
		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");
	}
	/// <summary>
	/// 获取指定数据库类型对应的程序集名称
	/// </summary>
	/// <param name="providerType">数据库类型枚举</param>
	/// <returns></returns>
	public static string GetProviderInvariantName(DbProviderType providerType)
	{
		return providerInvariantNames[providerType];
	}
	/// <summary>
	/// 获取指定类型的数据库对应的DbProviderFactory
	/// </summary>
	/// <param name="providerType">数据库类型枚举</param>
	/// <returns></returns>
	public static DbProviderFactory GetDbProviderFactory(DbProviderType providerType)
	{
		//如果还没有加载,则加载该DbProviderFactory
		if (!providerFactoies.ContainsKey(providerType))
		{
			providerFactoies.Add(providerType, ImportDbProviderFactory(providerType));
		}
		return providerFactoies[providerType];
	}
	/// <summary>
	/// 加载指定数据库类型的DbProviderFactory
	/// </summary>
	/// <param name="providerType">数据库类型枚举</param>
	/// <returns></returns>
	private static DbProviderFactory ImportDbProviderFactory(DbProviderType providerType)
	{
		string providerName = providerInvariantNames[providerType];
		DbProviderFactory factory = null;
		try
		{
			//从全局程序集中查找
			factory = DbProviderFactories.GetFactory(providerName);
		}
		catch (ArgumentException e)
		{
			factory = null;
		}
		return factory;
	}
}


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

string connectionString = @"Data Source=D:\VS2008\NetworkTime\CrawlApplication\CrawlApplication.db3";
string sql = "SELECT * FROM Weibo_Media order by Id desc limit 0,20000";
DbUtility db = new DbUtility(connectionString, DbProviderType.SQLite);
DataTable data = db.ExecuteDataTable(sql, null);
DbDataReader reader = db.ExecuteReader(sql, null);
reader.Close();

用法举例,访问MySQL:

string connectionString = @"Server=localhost;Database=crawldb;Uid=root;Pwd=root;Port=3306;";
string sql = "SELECT * FROM Weibo_Media order by Id desc limit 0,20000";
DbUtility db = new DbUtility(connectionString, DbProviderType.MySql);
DataTable data = db.ExecuteDataTable(sql, null);
DbDataReader reader = db.ExecuteReader(sql, null);
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