65.9K
CodeProject 正在变化。 阅读更多。
Home

数据访问组件

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (7投票s)

2009年12月23日

CPOL

6分钟阅读

viewsIcon

39128

downloadIcon

934

一个简单的组件,用于连接不同类型的数据源。

引言

数据访问始终是任何软件开发过程中不可避免的一部分。市场上存在不同类型的数据仓库管理系统 (DBMS)。软件架构师在设计多层应用程序时,会将数据访问视为一个单独的层。我在这里介绍一个独特的数据访问组件,它封装了 ADO.NET 的数据访问功能,以及其他一些有用的实用工具,可用于任何类型的应用程序和数据库后端。此组件的一个主要优点是,它为访问 SQL、OLEDB、ODBC 和 Oracle 后端提供了一个透明的接口。它可视化了一个称为参数引擎 (Parameter Engine) 的新概念,利用该概念可以轻松构建存储过程等的参数,并将其传递给任何类型的后端。

这个库简单轻量。任何有 .NET 数据访问经验的人都可以轻松理解它。使用此组件,您可以非常轻松地切换您的 DBMS 系统,而无需更改您的代码。您将针对此库中的定义编写数据访问逻辑。在部署时,只需更改设置中的连接字符串,根据另一个设置初始化适当的类,其余的将由数据访问组件管理。

结构

MS.Components.Data

  • IBackendSource:定义了可以在任何类型的后端上执行的一些基本操作。连接管理等在此。
  • IBackend:继承自 IBackendSource,并定义了 ADO.NET 支持的通用数据访问操作,例如 ExecuteNonQueryBeginTransaction 等。
  • 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 构建于两个接口和一个抽象类之上:IBackendSourceIBackendDataAccess。这些接口和抽象类的主要目的是为进一步扩展和向组件添加新的 DBMS 访问库定义标准。让我们深入了解一下。

这些接口的目的是定义一个标准化的数据访问语言。类似于 ADO.NET 的 IDbConnectionIDbCommand 等。这些接口将允许该程序集暴露给 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 操作的返回值返回。在这种情况下,您需要检查 IBackendSourceLastException 属性来识别生成的异常。否则,将返回驱动程序特定的返回值。

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. 实用工具

组件中包含了一些实用工具。有些仍在研究中。目前,名为 SQLInstancesFinderODBCDataSourcesFinder 的两个静态实用类正在运行。

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 日:发布文章。
© . All rights reserved.