一、引言
ADO.NET为应用程序开发人员提供了一种全新的数据库访问机制,它使得数据库编程变得相当容易。然而,在运用ADO.NET进行数据库编程时,开发人员往往会因为不注意某些细节问题而使得应用程序的可扩展性很差,也即某个数据库应用程序只能应用于某个特定类型的数据库,而不能和更多的其他类型的数据库进行交互或是移植到其它数据库平台下。本文将深入研究如何运用ADO.NET中的接口技术来实现通用数据库编程技术并构建通用数据库访问层。
二、ADO.NET体系结构
ADO.NET是由一系列的数据库相关类和接口组成的,它的基石是XML技术,所以通过运用ADO.NET技术应用程序不仅能访问关系型数据库中的数据,而且还能访问层次化的XML数据。ADO.NET为应用程序提供了两种数据访问的模式:连接模式(Connected Mode)和非连接模式(Disconnected Mode)。运用过ADO技术的开发人员对前一种模式应该是非常熟悉的,而后一种模式则是ADO.NET才具有的。相比于传统的数据库访问模式,非连接的模式为应用程序提供了更大的可升级性和灵活性。在该模式下,一旦应用程序从数据源中获得所需的数据,它就断开与原数据源的连接,并将获得的数据以XML的形式存放在主存中。在应用程序处理完数据后,它再取得与原数据源的连接并完成数据的更新工作。
ADO.NET中的DataSet类是非连接模式的核心,数据集对象(DataSet)是以XML的形式存放数据。应用程序既可以从一个数据库中获取一个数据集对象,也可以从一个XML数据流中获取一个数据集对象。而从用户的角度来看,数据源在哪里并不重要,也是无需关心的。这样一个统一的编程模型就可被运用于任何使用了数据集对象的应用程序。
ADO.NET体系结构中还有一个非常重要的部分就是数据提供者对象(Data Provider),它是访问数据库的必备条件。通过它应用程序可以产生相应的数据集对象;同时它还提供了连接模式下的数据库访问支持。图1描述了ADO.NET总体的体系结构。
图1 ADO.NET的体系结构
三、数据提供者对象
本文研究的是通用数据库编程并如何运用该技术实现通用数据库访问层,因此要从ADO.NET体系结构中的数据提供者对象入手,使得一个应用程序具有访问多个不同类型数据库的能力。ADO.NET中的数据提供者对象包括数据库连接接口(IDbConnection)、数据库命令接口(IDbCommand)、数据读取器接口(IDataReader)和数据适配器接口(IDbDataAdapter)等不同种类的接口。通过这些接口,应用程序可以访问数据库、执行相关的命令操作并获取相应结果,获取的结果可以是以XML数据的形式存放在数据集对象中,也可以是直接被应用程序所使用。目前,微软的.NET Framework已经是1.1版本了,所以其中的ADO.NET支持了更广泛的数据提供者对象:一种为SQL Server数据提供者对象,它是专门应用于MS SQL Server数据库的,所以性能得到了优化;一种为OleDb数据提供者对象,它可以通过COM层和OLE DB进行交互;一种为ODBC数据提供者对象,它可以直接跟ODBC数据源进行交互;最后一种Oracle数据提供者对象,它是专门针对Oracle数据库的,所以性能上也得到了不少优化。与这四种数据提供者对象相关联的类的前缀分别为:Sql、OleDb、Odbc以及Oracle,而与其相关联的命名空间则分别为:System.Data.SqlClient、System.Data.OleDb、System.Data.Odbc以及System.Data.OracleClient。在实际的开发中,开发人员可以根据需要选择相应类型的数据提供者对象。不过为了使应用程序具有通用性,开发人员应通过使用数据提供者对象的接口而并非某个特定类型的数据提供者对象来实现数据库的访问操作,这样就可以实现通用数据库编程并构建通用数据库访问层了。
下面的表格列举了各种接口的名称以及相应的描述:
接口名称 |
描述 |
IDbConnection |
数据库连接接口,代表了到数据源的一个连接。 |
IDbCommand |
数据库命令接口,代表了对数据源进行操作的一系列SQL语句或命令对象。 |
IDataReader |
数据读取器接口,在连接的模式下访问数据源,以向前只读的方式获取数据。 |
IDbDataAdatpter |
数据适配器接口,在非连接的模式下工作,作为数据源和数据集对象之间的桥梁。 |
IDbTransaction |
数据事务处理接口,能实现对数据源的事务处理操作。 |
IDataParameter |
数据参数接口,代表了对应于数据库命令对象的一系列参数对象。 |
表1 各种接口名称以及其相应描述
下面的表格列举了四种不同类型的数据提供者对象中的各个相互并行的类:
SQL Server |
OleDb |
ODBC |
Oracle |
SqlConnection |
OleDbConnection |
OdbcConnection |
OracleConnection |
SqlCommand |
OleDbCommand |
OdbcCommand |
OracleCommand |
SqlDataReader |
OleDbDataReader |
OdbcDataReader |
OracleDataReader |
SqlDataAdapter |
OleDbDataAdapter |
OdbcDataAdapter |
OracleDataAdapter |
SqlTransaction |
OleDbTransaction |
OdbcTransaction |
OracleTransaction |
SqlDataParameter |
OleDbDataParameter |
OdbcDataParameter |
OracleDataParameter |
表2 四种类型的数据提供者对象中的类
四、构建通用数据库访问层
数据提供者对象构成了ADO.NET的基础,通过实现其中的各种不同的接口,构建通用数据库访问层并非难事。一般来说,运用数据提供者对象访问并更新数据的操作会包含以下几个步骤:
1. 运用数据库连接对象建立和数据源的连接。
2. 根据上面建立的数据库连接对象创建一个数据库命令对象以执行特定的操作。
3. 执行数据库命令对象或创建并执行数据适配器对象,以返回数据读取器对象(连接模式)或填充数据集对象(非连接模式)或取得其他相应结果。
4. 数据处理完毕后,通过数据库命令对象或数据适配器对象将处理结果更新到数据源。
5. 最后释放各种数据提供者对象资源。
基于以上考虑,构建通用数据库访问层主要得实现对数据源访问的底层操作的封装,而仅仅暴露出数据读取器对象或是数据集对象等以供商业逻辑层调用。因此,本文介绍的通用数据库访问层主要实现了以下一些方法:
1. ExecuteNonQuery:执行INSERT、DELETE、UPDATE等SQL语句并返回受影响的行的数目。
2. ExecuteReader:执行SELECT操作并返回一个数据读取器对象。
3. ExecuteScalar:执行SQL操作并返回单值对象,即结果集中第一行的第一条数据。
4. PopulateDataSet:执行SQL操作,通过数据适配器对象将从数据源获取的数据填充到数据集对象中并返回之。
下面便是本文介绍的通用数据库访问层的具体实现,其命名空间和类名均为UniversalDAL,其中还运用到了一个枚举类型-DBType,用于枚举各种不同类型的数据库。UniversalDAL类主要提供了上面介绍的四个基本方法,同时还提供了DatabaseType和ConnectionString这两个基本属性。考虑到具体实现上的性能要求,UniversalDAL类还对构造函数和某些方法进行了重载操作。其中的GetConnection、GetCommand以及GetDataAdapter等函数是UniversalDAL类的核心部分,正是它们实现了通用数据库的访问操作。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.Data.Odbc;
using System.Data.OracleClient;
namespace UniversalDAL
{
/// <summary>
/// 该枚举类型用于枚举数据库的类型
/// </summary>
public enum DBType
{
SQLServer,
OleDb,
ODBC,
Oracle
}
/// <summary>
/// 通用数据库访问类-UniversalDAL类,
/// 支持SQL Server、OleDb、ODBC、Oracle等
/// 不同类型的数据源
/// </summary>
public class UniversalDAL
{
private DBType _DatabaseType = DBType.SQLServer;
/// <summary>
/// 数据库类型属性
/// </summary>
public DBType DatabaseType
{
get { return _DatabaseType; }
set { _DatabaseType = value; }
}
private string _ConnectionString = "";
/// <summary>
/// 数据库连接字符串属性
/// </summary>
public string ConnectionString
{
get { return _ConnectionString; }
set { _ConnectionString = value; }
}
/// <summary>
/// 构造函数
/// </summary>
public UniversalDAL()
{
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="DbType">访问的数据库类型</param>
public UniversalDAL(DBType DbType)
{
this._DatabaseType = DbType;
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="DbType">访问的数据库类型</param>
/// <param name="ConString">数据库连接字符串</param>
public UniversalDAL(DBType DbType, string ConString)
{
this._DatabaseType = DbType;
this._ConnectionString = ConString;
}
/// <summary>
/// 根据数据库类型获取数据库连接接口
/// </summary>
/// <returns>数据库连接接口</returns>
private IDbConnection GetConnection()
{
IDbConnection con = null;
switch (this.DatabaseType)
{
case DBType.SQLServer: con = new
SqlConnection(this.ConnectionString); break;
case DBType.OleDb: con = new
OleDbConnection(this.ConnectionString); break;
case DBType.ODBC: con = new
OdbcConnection(this.ConnectionString); break;
case DBType.Oracle: con = new
OracleConnection(this.ConnectionString); break;
default: con = new
SqlConnection(this.ConnectionString); break;
}
return con;
}
/// <summary>
/// 根据数据库类型获取数据库命令接口
/// </summary>
/// <param name="cmdText">数据库命令字符串</param>
/// <param name="con">数据库连接接口</param>
/// <returns>数据库命令接口</returns>
private IDbCommand GetCommand(string cmdText, IDbConnection con)
{
IDbCommand cmd = null;
switch (this.DatabaseType)
{
case DBType.SQLServer: cmd = new SqlCommand(cmdText,
(SqlConnection)con); break;
case DBType.OleDb: cmd = new OleDbCommand(cmdText,
(OleDbConnection)con); break;
case DBType.ODBC: cmd = new
OdbcCommand(cmdText,(OdbcConnection)con); break;
case DBType.Oracle: cmd = new OracleCommand(cmdText, (OracleConnection)con); break;
default: cmd = new SqlCommand(cmdText,
(SqlConnection)con); break;
}
return cmd;
}
/// <summary>
/// 根据数据库类型获取数据适配器接口
/// </summary>
/// <param name="cmdText">数据库命令字符串</param>
/// <param name="conString">数据库连接字符串</param>
/// <returns>数据适配器接口</returns>
private IDataAdapter GetDataAdapter(string cmdText, string conString)
{
IDataAdapter da = null;
switch (this._DatabaseType)
{
case DBType.SQLServer: da = new SqlDataAdapter(cmdText, conString); break;
case DBType.OleDb: da = new OleDbDataAdapter(cmdText, conString); break;
case DBType.ODBC: da = new OdbcDataAdapter(cmdText, conString); break;
case DBType.Oracle: da = new OracleDataAdapter(cmdText, conString); break;
default: da = new SqlDataAdapter(cmdText, conString); break;
}
return da;
}
/// <summary>
/// 根据数据库类型获取数据适配器接口
/// </summary>
/// <param name="cmd">数据库命令接口</param>
/// <returns>数据适配器接口</returns>
private IDataAdapter GetDataAdapter(IDbCommand cmd)
{
IDataAdapter da = null;
switch (this.DatabaseType)
{
case DBType.SQLServer: da = new
SqlDataAdapter((SqlCommand)cmd); break;
case DBType.OleDb: da = new
OleDbDataAdapter((OleDbCommand)cmd); break;
case DBType.ODBC: da = new
OdbcDataAdapter((OdbcCommand)cmd); break;
case DBType.Oracle: da = new
OracleDataAdapter((OracleCommand)cmd); break;
default: da = new
SqlDataAdapter((SqlCommand)cmd); break;
}
return da;
}
/// <summary>
/// 执行SQL语句并返回受影响的行的数目
/// </summary>
/// <param name="cmdText">数据库命令字符串</param>
/// <returns>受影响的行的数目</returns>
public int ExecuteNonQuery(string cmdText)
{
IDbConnection con = null;
IDbCommand cmd = null;
try
{
con = this.GetConnection();
cmd = this.GetCommand(cmdText, con);
con.Open();
return cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return 0;
}
finally
{
if (cmd != null)
cmd.Dispose();
if (con != null)
con.Dispose();
} }
|