数据访问组件
一个简单的组件,用于连接不同类型的数据源。
引言
数据访问始终是任何软件开发过程中不可避免的一部分。市场上存在不同类型的数据仓库管理系统 (DBMS)。软件架构师在设计多层应用程序时,会将数据访问视为一个单独的层。我在这里介绍一个独特的数据访问组件,它封装了 ADO.NET 的数据访问功能,以及其他一些有用的实用工具,可用于任何类型的应用程序和数据库后端。此组件的一个主要优点是,它为访问 SQL、OLEDB、ODBC 和 Oracle 后端提供了一个透明的接口。它可视化了一个称为参数引擎 (Parameter Engine) 的新概念,利用该概念可以轻松构建存储过程等的参数,并将其传递给任何类型的后端。
这个库简单轻量。任何有 .NET 数据访问经验的人都可以轻松理解它。使用此组件,您可以非常轻松地切换您的 DBMS 系统,而无需更改您的代码。您将针对此库中的定义编写数据访问逻辑。在部署时,只需更改设置中的连接字符串,根据另一个设置初始化适当的类,其余的将由数据访问组件管理。
结构
MS.Components.Data
IBackendSource
:定义了可以在任何类型的后端上执行的一些基本操作。连接管理等在此。IBackend
:继承自IBackendSource
,并定义了 ADO.NET 支持的通用数据访问操作,例如ExecuteNonQuery
、BeginTransaction
等。BackendType
:枚举,指定后端的类型,例如 SQL Oracle 等。ParameterEngine
:一个独特的类,有助于在不知道后端类型的情况下轻松操作数据库命令的数据库参数。
MS.Components.Data.Base
DataAccess
:继承自IBackend
的抽象类。此类是任何特定后端类型实现的基类,并提供了诸如连接管理、事务控制、命令生成等通用操作的实现。
MS.Components.Data.SQLServer
SQLServerDB
:派生自DataAccess
,并为 SQL Server 数据源提供了数据访问操作的具体实现。
MS.Components.Data.Oracle
OracleDB
:派生自DataAccess
,并为 Oracle 数据源提供了数据访问操作的具体实现。
MS.Components.Data.ODBC
OdbcAccess
:派生自DataAccess
,并为 ODBC 数据源提供了数据访问操作的具体实现。
MS.Components.Data.OLEDB
OleDbAccess
:派生自DataAccess
,并为 OLEDB 数据源提供了数据访问操作的具体实现。
MS.Components.Data.Utilities
SQLInstancesFinder
:一个静态实用类,可用于查找网络上可用的 SQL Server 实例,并检索其中的数据库信息。ODBCDataSourcesFinder
:一个静态实用类,可用于检索有关在控制面板中设置的 ODBC 数据源的信息。
MS.Components.Data.Controls
SQLConnectionSetupControl
:一个 Windows 用户控件,您可以使用它来浏览网络上的 SQL Server 实例及其数据库,并为它们生成连接字符串。类似于服务器资源管理器中的添加连接。
深入数据访问组件
A. 数据访问接口和类
DAL 构建于两个接口和一个抽象类之上:IBackendSource
、IBackend
和 DataAccess
。这些接口和抽象类的主要目的是为进一步扩展和向组件添加新的 DBMS 访问库定义标准。让我们深入了解一下。
这些接口的目的是定义一个标准化的数据访问语言。类似于 ADO.NET 的 IDbConnection
、IDbCommand
等。这些接口将允许该程序集暴露给 COM,并与 VB6 等应用程序一起使用。此外,它们还有助于为新型数据源实现新的数据层组件。有两个接口将 DBMS 操作分为连接操作和数据访问操作。
IBackendSource
此接口主要定义连接管理操作,例如指定连接字符串、指定后端类型、打开/关闭连接等。请参阅每个定义的注释。
public interface IBackendSource : IDisposable
{
// Get and set the connection string
string ConnectionString
// Represents the current ADO.NET DBConnection
DbConnection Connection
// An enumeration specifying the type of the backend DBMS
BackendType BackendType
// Specifies whether connection is established
bool IsConnected
// Retrives the last exception occurred inside the data layer
Exception LastException
// Connects to the backed using the connection string.
// The reconnect indicates to close any open connection and reconnect.
// If connection is already opened then specifying false
// may generate an exception
bool Connect(bool reconnect);
// Connects use the specified connection string
bool Connect(string conString, bool reconnect);
// Disconnect
bool Disconnect();
}
IBackend
它继承自 IBackendSource
,并定义了数据操作,例如访问数据、执行查询等。
public interface IBackend : IBackendSource
{
// Retrieves data by specifying and SQL statement
DataTable GetData(string sql);
// Retrieves data using a db command
DataTable GetData(DbCommand command);
// Retrieves data using the sql statement
// and appends with the specified data table
void GetData(string sql, ref DataTable dt);
// Retrieves data using the dbcommand
// and appends with the specified data table
void GetData(DbCommand command, ref DataTable dt);
// Executes the specified query with specified command type
int ExecuteNonQuery(string sqlCommand, CommandType cmdType);
// Executes the specified dbcommand on the data source
int ExecuteNonQuery(DbCommand command);
// Executes the specified query over the data source with
// specified command type and parameter engine
int ExecuteNonQuery(string sql, CommandType cmdType,
ParameterEngine param);
// Retrieves a single value using the specified db command
object GetSingleValue(DbCommand command);
// Retrieves a single value using
// the specified query and command type
object GetSingleValue(string sql, CommandType cmdType);
// Retrieves a single value using the specified query/procedure
// with the command type and parameter engine
object GetSingleValue(string sql, CommandType cmdType,
ParameterEngine param);
// Generates a corresponding db command
// of the backend type for executing stored procedures etc.
// using specified command type and parameter engine
DbCommand GenerateCommand(string sql, CommandType cmdType,
ParameterEngine param);
// Creates a corresponding db parameter for the backend type.
DbParameter CreateParameter();
// Starts a transaction
DbTransaction BeginTransaction();
// Starts a transaction with specified isolation level
DbTransaction BeginTransaction(IsolationLevel isolationLevel);
// Commits the transaction
void CommitTransaction();
// Rolls back the transaction
void RollbackTransaction();
}
DataAccess
这是一个继承自 IBackend
接口的抽象类,并为 IBackend
中指定的某些功能提供了具体实现。此类充当派生和实现 SQL Server、Oracle、ODBC 和 OLEDB 类型组件(在 ADO.NET 库中定义)的具体数据访问类的基类。此类实现并处理连接管理、数据库命令生成、事务管理等功能,因此派生类无需担心实现这些功能。它们只需重写此类中派生自 IBackend
的抽象功能,并提供源特定的实现。
此类还定义了一个常量 ERROR_RESULT
。目前,在派生类中,ExecuteNonQuery
操作的异常会在派生类中捕获,并更新 LastException
属性。此时,此 ERROR_RESULT
常量作为 ExecuteNonQuery
操作的返回值返回。在这种情况下,您需要检查 IBackendSource
的 LastException
属性来识别生成的异常。否则,将返回驱动程序特定的返回值。
public abstract class DataAccess : IBackend
{
public const int ERROR_RESULT = int.MaxValue;
// Inherits the interfaces from IBackend and IBackendSource
// Some of the interface methods are given concrete implementation
// while some others are defined as abstract
// for derived class implementation
// *** Protected ***
protected abstract void createConnection();
protected abstract DbCommand getCommand(ref string sqlCommand,
CommandType cmdType);
// *** Public ***
// IBackend Members
public abstract DataTable GetData(string sql);
public abstract DataTable GetData(DbCommand command);
public abstract void GetData(string sql, ref DataTable dt);
public abstract void GetData(DbCommand command, ref DataTable dt);
public abstract int ExecuteNonQuery(string sqlCommand, CommandType cmdType);
public abstract int ExecuteNonQuery(DbCommand command);
public abstract int ExecuteNonQuery(string sql,
CommandType cmdType, ParameterEngine param);
public abstract object GetSingleValue(DbCommand command);
public abstract object GetSingleValue(string sql, CommandType cmdType);
public abstract object GetSingleValue(string sql,
CommandType cmdType, ParameterEngine param);
public abstract DbCommand GenerateCommand(string sql,
CommandType cmdType, ParameterEngine param);
public abstract DbParameter CreateParameter();
}
public class SQLServerDB : DataAccess
{
// Implements data access functionalities specific to SQL server
}
public class OracleDB : DataAccess
{
// Implements data access functionalities specific to Oracle
}
public class OdbcAccess : DataAccess
{
// Implements data access functionalities specific to ODBC
}
public class OleDbAccess : DataAccess
{
// Implements data access functionalities specific to OLEDB
}
ParameterEngine
ParameterEngine
是此 DAL 组件引入的新概念,它提供了数据库参数的透明易用的操作。无论后端类型如何,用户都可以管理可以传递给存储过程的输入和输出参数(此参数引擎仅支持 SQL、Oracle、ODBC 和 OLEDB 类型后端)。它可用于在查询执行后检索输出参数。一旦创建了参数引擎的实例并填入了参数,就可以使用它来生成数据库命令,然后执行查询。
public sealed class ParameterEngine
{
// Adding parameters
public bool Add(string name, object value)
public bool Add(string name, DbType dataTypeEnum, int size, object value)
public bool Add(string name, DbType dataTypeEnum, int size,
string sourceColumn, object value)
public bool Add(string name, DbType dataTypeEnum, int size,
ParameterDirection direction, string sourceColumn,
DataRowVersion sourceVersion, object value)
// Adding output parameters
public bool AddOutputParameter(string name)
public bool AddOutputParameter(string name, int size)
public bool AddOutputParameter(string name, DbType dataType, int size)
// Retrieving the value from output parameter after the query execution
public object RetrieveOutputParameterValue(string parameterName)
}
B. 实用工具
组件中包含了一些实用工具。有些仍在研究中。目前,名为 SQLInstancesFinder
和 ODBCDataSourcesFinder
的两个静态实用类正在运行。
SQLInstancesFinder
此类提供静态功能来查询网络上可用的 SQL Server 实例。每个 SQL Server 实例都使用 SQLInstancesFinder.SQLServerInstance
类表示,该类包含该实例的名称和版本信息。还有其他功能,可以通过 Windows/SQL 身份验证检索特定服务器中数据库的名称。
public static class SQLInstancesFinder
{
// Retrives the list of available server
public static List<SQLServerInstance> GetNetworkSQLServerInstances()
// Retrives the list of databases
// in a particular server using windows authentication
public static List<string> GetDatabasesFromServer(string server)
// Retrives the list of databases
// in a particular server using SQL authentication
public static List<string> GetDatabasesFromServer(string server,
string useName, string password)
}
ODBCDataSourcesFinder
此类提供静态功能来查询计算机上配置的 ODBC 数据源(系统/用户/文件)。每个数据源都使用 ODBCDataSourcesFinder.ODBCDataSource
类表示,该类包含该数据源的名称和驱动程序信息。
public static class ODBCDataSourcesFinder
{
// Gets the list of all DSNs
public static List<ODBCDataSource> GetAllDSNs()
// Gets the list of system DSNs
public static List<ODBCDataSource> GetSystemDSNs()
// Gets the list of user DSNs
public static List<ODBCDataSource> GetUserDSNs()
// Gets the list of file DSNs
public static List<ODBCDataSource> GetFileDSNs()
}
C. 用户控件
SQLConnectionSetupControl
这是一个 Windows 用户控件,类似于服务器资源管理器中的“添加连接”对话框。它可用于以交互方式连接到网络中的服务器,连接到数据库,并最终生成连接字符串。
public partial class SQLConnectionSetupControl : UserControl
{
// ********* Data *********
// Specifies the authentication mode. Windows/SQL
[DefaultValue(typeof(SQLConnectionSetupControl.LogonMode),
"Windows")]
public LogonMode AuthenticationMode
// Gets the connection string
public string ConnectionString
// Gets/sets the name of the server
public string ServerName
// Gets/sets the name of the database
public string DatabaseName
// Gets/sets the user name for connection
public string UserName
// Gets/sets the password for connection
public string Password
// Gets/sets the timeout for connection
[DefaultValue(-1)]
public int TimeOut
// ********* Behaviour / Appearance *********
// Setting true will resize the parent form
// to resize to the size of the control
[DefaultValue(false)]
public bool AutoSizeParentForm
// Gets/sets whether the parent form should
// be closed when OK or cancel is clicked
[DefaultValue(false)]
public bool CloseParentFormOnOKCancel
// Gets/sets whether the caption of the parent form
// to be altered when opening a connection
[DefaultValue(false)]
public bool UpdateParentFormStatus
// Gets/sets whether the "Test" button to be visible
// for testing the connection
[DefaultValue(true)]
public bool ShowTestButton
[DefaultValue("")]
// Gets/sets the message to be displayed when a connection
// is opening. Has effect only when
// UpdateParentFormStatus is set true
public string ParentFormStatusMessage
// ********* Buttons *********
// Gets/sets the caption of OK button
[DefaultValue("&OK")]
public string OKButtonText
// Gets/sets the caption of cancel button
[DefaultValue("&Cancel")]
public string CancelButtonText
// ********* Methods *********
// Check whether the connection is success with the input data
public bool CheckConnection()
}
使用组件
以下代码段说明了组件的用法。您可以在 TestApp 项目的 FormUsage.cs 中找到相同的代码。测试项目包含另一个演示 SQLConnectionSetupControl
用法的窗体。
DataAccess da = null;
string conStr =
"Data Source=localhost;Initial Catalog=MyDB;User ID=sa;Password=pwd";
private static DataAccess _CreateDataSource(BackendType type)
{
DataAccess ret = null;
switch (type)
{
case BackendType.SQL: ret = new SQLServerDB(); break;
case BackendType.Oracle: ret = new OracleDB(); break;
case BackendType.Odbc: ret = new OdbcAccess(); break;
case BackendType.OleDb: ret = new OleDbAccess(); break;
}
return ret;
}
private void Init()
{
this.da = _CreateDataSource(BackendType.SQL);
}
// *************** Connecting & Disconnecting ***************
private void Connect()
{
this.da.ConnectionString = conStr;
this.da.Connect(false);
// OR
this.da.Connect(conStr, true);
// Note : The reconnect parameter indicates whether
// any existing connection to be dropped
// and new connection to be created.
// When you specify false and there already an open connection
// available, then an exception will be generated.
}
private void Disconenct()
{
this.da.Disconnect();
}
// ********************* Retrieving Data *********************
private void GetData()
{
// 1. Basic data access with SQL statements
DataTable dt1 = this.da.GetData("SELECT * FROM Users");
// 2. Basic data access with SQL statements,
// passing an existing data table to be filled up with
DataTable dt2 = new DataTable();
this.da.GetData("SELECT * FROM Users", ref dt2);
// 3. Data access using DB commands.
DbCommand cmd1 = this._GenCommand();
DataTable dt3 = this.da.GetData(cmd1);
// 4. Data access using DB commands,
// passing an existing data table to be filled up with
DbCommand cmd2 = this._GenCommand();
DataTable dt4 = new DataTable();
this.da.GetData(cmd2, ref dt4);
}
private void GetSingleValue()
{
// 1. Using commands
DbCommand cmd1 = this._GenCommand();
object o1 = this.da.GetSingleValue(cmd1);
// 2. Using direct SQL statement or procedure without parameters
object o2 = this.da.GetSingleValue("Query", CommandType.Text);
object o3 = this.da.GetSingleValue("Procedure",
CommandType.StoredProcedure);
// 3. Using procedure with parameters
ParameterEngine pe = this._CreateParamEngine();
object o4 = this.da.GetSingleValue("Procedure",
CommandType.StoredProcedure, pe);
}
// ********************* Executing Other Queries *********************
private void ExecuteNonQuery()
{
int result = 0;
// 1. Using commands
DbCommand cmd1 = this._GenCommand();
result = this.da.ExecuteNonQuery(cmd1);
// 2. Using direct SQL statement or procedure without parameters
result = this.da.ExecuteNonQuery("Query", CommandType.Text);
result = this.da.ExecuteNonQuery("Procedure",
CommandType.StoredProcedure);
// 3. Using procedure with parameters
ParameterEngine pe = this._CreateParamEngine();
result = this.da.ExecuteNonQuery("Procedure",
CommandType.StoredProcedure, pe);
// Checking error
if (result == DataAccess.ERROR_RESULT)
{
// Represents the last occured exception.
// LastException is available only for ExecuteNonQuery command.
// Data retrieval commands throws out the exceptions immediately.
throw this.da.LastException;
}
}
// ********************* DBCommand Generation *******************
private DbCommand _GenCommand()
{
// Direct SQL statements
DbCommand cmd1 = this.da.GenerateCommand(
"SELECT * FROM Users", CommandType.Text, null);
// Stored procedures
ParameterEngine pe = this._CreateParamEngine();
DbCommand cmd2 = this.da.GenerateCommand("spMyProcedure",
CommandType.StoredProcedure, pe);
return null;
}
// ********************* Parameter Engine Creation ****************
private ParameterEngine _CreateParamEngine()
{
ParameterEngine pe = ParameterEngine.New(this.da);
// Specifying input parameters
pe.Add("param1", 1);
pe.Add("param1", DateTime.Now);
pe.Add("param1", "Hello");
// Specifying output parameter
pe.AddOutputParameter("OutParam1");
pe.AddOutputParameter("OutParam2", DbType.Int32, 4);
return pe;
}
// ********* Retrieving Output Values From ParameterEngine *********
private object _RetrieveOutputValue(ParameterEngine pe,
string paramName)
{
object ret = pe.RetrieveOutputParameterValue(paramName);
return ret;
}
// ********** Retrieving Output Values From ParameterEngine ********
private void Transaction()
{
// Note : Only one transaction
// can be opened for a particular connection
// Begins a transaction on the existing connection
DbTransaction transaction = this.da.BeginTransaction();
// Commits the begun transaction
this.da.CommitTransaction();
// Rollbacks the changes made in the existing transaction
this.da.RollbackTransaction();
}
未来
我正在考虑为该组件带来新功能。您可以在代码中看到一些其他类,它们仍在建设中。下一个功能可能是一个模式检索类,通过该类可以从数据源检索表、存储过程、字段名称等的模式。我感谢您的建议。
历史
- 2009 年 12 月 22 日:发布文章。