DBHelper (支持事務與數據庫變動)

1   概述

更新內容:添加 "支持數據分頁"html

這個數據庫操做類的主要特點有數據庫

1>     事務操做更加的方便數組

2>     變動數據庫更加的容易app

3>   支持數據分頁框架

最新的全部代碼:ide

using System;
using System.Data;
using System.Data.Common;
using Project.BaseFramework;
using System.Collections.Generic;
using System.Configuration;

namespace Project.BaseFramework.DataProvider
{
    public class DBHelper
    {

        #region Constuctor
        public DBHelper() { }

        private static string ConnectionString = ConfigurationManager.AppSettings["DBConnectionString"];

        private static IDBClient DBClient = DBClientFactory.GetDBClient(ConfigurationManager.AppSettings["DBClient"]);

        [ThreadStatic]
        private static TransConnection TransConnectionObj = null;

        #endregion

        #region ExecuteNonQuery
        public static int ExecuteNonQuery(CommandType cmdType, string cmdText, params DbParameter[] parameterValues)
        {
            int result = 0;
            bool mustCloseConn = true;

            DbCommand cmd = PrepareCmd(cmdType, cmdText, parameterValues, out mustCloseConn);
            OpenConn(cmd.Connection);
            result = cmd.ExecuteNonQuery();

            if (mustCloseConn) CloseConn(cmd.Connection);
            ClearCmdParameters(cmd);
            cmd.Dispose();

            return result;
        }

        #endregion ExecuteNonQuery

        #region ExecuteScalar
        public static object ExecuteScalar(CommandType cmdType, string cmdText, params DbParameter[] parameterValues)
        {
            object result = 0;
            bool mustCloseConn = true;

            DbCommand cmd = PrepareCmd(cmdType, cmdText, parameterValues, out mustCloseConn);
            OpenConn(cmd.Connection);
            result = cmd.ExecuteScalar();

            if (mustCloseConn) CloseConn(cmd.Connection);
            ClearCmdParameters(cmd);
            cmd.Dispose();

            return result;
        }
        #endregion ExecuteScalar

        #region ExecuteReader
        public static DbDataReader ExecuteReader(CommandType cmdType, string cmdText, params DbParameter[] parameterValues)
        {
            DbDataReader result = null;
            bool mustCloseConn = true;
            DbCommand cmd = PrepareCmd(cmdType, cmdText, parameterValues, out mustCloseConn);
            try
            {
                OpenConn(cmd.Connection);
                if (mustCloseConn)
                {
                    result = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                }
                else
                {
                    result = cmd.ExecuteReader();
                }
                ClearCmdParameters(cmd);
                return result;
            }
            catch (Exception ex)
            {
                if (mustCloseConn) CloseConn(cmd.Connection);
                ClearCmdParameters(cmd);
                cmd.Dispose();
                throw ;
            }
        }
        #endregion ExecuteReader

        #region ExecuteDataset
        public static DataSet ExecuteDataSet(CommandType cmdType, string cmdText, params DbParameter[] parameterValues)
        {
            DataSet result = null;
            bool mustCloseConn = true;

            DbCommand cmd = PrepareCmd(cmdType, cmdText, parameterValues, out mustCloseConn);
            using (DbDataAdapter da = DBClient.GetDbDataAdappter())
            {
                da.SelectCommand = cmd;
                result = new DataSet();

                da.Fill(result);
            }

            if (mustCloseConn) CloseConn(cmd.Connection);
            ClearCmdParameters(cmd);
            cmd.Dispose();

            return result;
        }
        #endregion ExecuteDataset

        #region ExecuteDataTable
        public static DataTable ExecuteDataTable(CommandType cmdType, string cmdText, params DbParameter[] parameterValues) 
        {
            DataSet ds = ExecuteDataSet(cmdType,cmdText, parameterValues);
            if (ds != null && ds.Tables.Count > 0)
                return ds.Tables[0];
            else
                return null;
        }
        #endregion

        #region ExecutePaging
        public static DataTable ExecutePagingDataTable(CommandType cmdType, string cmdText,int pageIndex,int pageSize,string orderInfo, params DbParameter[] parameterValues)
        {
            cmdText = DBClient.GetPagingSql(cmdText, pageIndex, pageSize, orderInfo);
            return ExecuteDataTable(CommandType.Text, cmdText, parameterValues);
        }

        public static DbDataReader ExecutePagingReader(CommandType cmdType, string cmdText, int pageIndex, int pageSize, string orderInfo, params DbParameter[] parameterValues)
        {
            cmdText = DBClient.GetPagingSql(cmdText, pageIndex, pageSize, orderInfo);
            return ExecuteReader(CommandType.Text, cmdText, parameterValues);
        }
        #endregion

        #region Transaction
        public static void BeginTransaction()
        {
            if (TransConnectionObj == null) 
            {
                DbConnection conn = DBClient.GetDbConnection(ConnectionString);
                OpenConn(conn);
                DbTransaction trans = conn.BeginTransaction();
                TransConnectionObj = new TransConnection();
                TransConnectionObj.DBTransaction = trans;
            }
            else 
            {
                TransConnectionObj.Deeps += 1;
            }
        }

        public static void CommitTransaction()
        {
            if (TransConnectionObj == null) return;
            if (TransConnectionObj.Deeps > 0)
            {
                TransConnectionObj.Deeps -= 1;
            }
            else
            {
                TransConnectionObj.DBTransaction.Commit();
                ReleaseTransaction();
            }
        }

        public static void RollbackTransaction()
        {
            if (TransConnectionObj == null) return;
            if (TransConnectionObj.Deeps > 0)
            {
                TransConnectionObj.Deeps -= 1;
            }
            else
            {
                TransConnectionObj.DBTransaction.Rollback();
                ReleaseTransaction();
            }
        }

        private static void ReleaseTransaction()
        {
            if (TransConnectionObj == null) return;
            DbConnection conn = TransConnectionObj.DBTransaction.Connection;
            TransConnectionObj.DBTransaction.Dispose();
            TransConnectionObj = null;
            CloseConn(conn);
        }

        #endregion

        #region Connection
        private static void OpenConn(DbConnection conn)
        {
            if (conn == null) conn = DBClient.GetDbConnection(ConnectionString);
            if (conn.State == ConnectionState.Closed) conn.Open();
        }

        private static void CloseConn(DbConnection conn)
        {
            if (conn == null) return;
            if (conn.State == ConnectionState.Open) conn.Close();
            conn.Dispose();
            conn = null;
        }
        #endregion

        #region Create DbParameter

        public static DbParameter CreateInDbParameter(string paraName, DbType type, int size, object value)
        {
            return CreateDbParameter(paraName, type, size, value, ParameterDirection.Input);
        }

        public static DbParameter CreateInDbParameter(string paraName, DbType type, object value)
        {
            return CreateDbParameter(paraName, type, 0, value, ParameterDirection.Input);
        }

        public static DbParameter CreateOutDbParameter(string paraName, DbType type, int size)
        {
            return CreateDbParameter(paraName, type, size, null, ParameterDirection.Output);
        }

        public static DbParameter CreateOutDbParameter(string paraName, DbType type)
        {
            return CreateDbParameter(paraName, type, 0, null, ParameterDirection.Output);
        }

        public static DbParameter CreateReturnDbParameter(string paraName, DbType type, int size)
        {
            return CreateDbParameter(paraName, type, size, null, ParameterDirection.ReturnValue);
        }

        public static DbParameter CreateReturnDbParameter(string paraName, DbType type)
        {
            return CreateDbParameter(paraName, type, 0, null, ParameterDirection.ReturnValue);
        }

        public static DbParameter CreateDbParameter(string paraName, DbType type, int size, object value, ParameterDirection direction)
        {
            DbParameter para = DBClient.GetDbParameter();
            
            para.ParameterName = paraName;

            if (size != 0)
            {
                para.Size = size;
            }

            para.DbType = type;

            if (value != null)
            {
                para.Value = value;
            }
            else
            {
                para.Value = DBNull.Value;
            }

            para.Direction = direction;

            return para;
        }

        #endregion

        #region Command and Parameter
        /// <summary>
        /// 預處理用戶提供的命令,數據庫鏈接/事務/命令類型/參數
        /// </summary>
        /// <param>要處理的DbCommand</param>
        /// <param>數據庫鏈接</param>
        /// <param>一個有效的事務或者是null值</param>
        /// <param>命令類型 (存儲過程,命令文本, 其它.)</param>
        /// <param>存儲過程名或都T-SQL命令文本</param>
        /// <param>和命令相關聯的DbParameter參數數組,若是沒有參數爲'null'</param>
        /// <param><c>true</c> 若是鏈接是打開的,則爲true,其它狀況下爲false.</param>
        private static DbCommand PrepareCmd(CommandType cmdType,string cmdText, DbParameter[] cmdParams, out bool mustCloseConn)
        {
            DbCommand cmd = DBClient.GetDbCommand(cmdText);

            DbConnection conn = null;
            if (TransConnectionObj != null)
            {
                conn = TransConnectionObj.DBTransaction.Connection;
                cmd.Transaction = TransConnectionObj.DBTransaction;
                mustCloseConn = false;
            }
            else
            {
                conn = DBClient.GetDbConnection(ConnectionString);
                mustCloseConn = true;
            }
            cmd.Connection = conn;

            cmd.CommandType = cmdType;

            AttachParameters(cmd, cmdParams);

            return cmd;
        }

        /// <summary>
        /// 將DbParameter參數數組(參數值)分配給DbCommand命令.
        /// 這個方法將給任何一個參數分配DBNull.Value;
        /// 該操做將阻止默認值的使用.
        /// </summary>
        /// <param>命令名</param>
        /// <param>SqlParameters數組</param>
        private static void AttachParameters(DbCommand command, DbParameter[] commandParameters)
        {
            if (command == null) throw new ArgumentNullException("command");
            if (commandParameters != null)
            {
                foreach (DbParameter p in commandParameters)
                {
                    if (p != null)
                    {
                        // 檢查未分配值的輸出參數,將其分配以DBNull.Value.
                        if ((p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Input) &&
                        (p.Value == null))
                        {
                            p.Value = DBNull.Value;
                        }
                        command.Parameters.Add(p);
                    }
                }
            }
        }

        private static void ClearCmdParameters(DbCommand cmd)
        {
            bool canClear = true;
            if (cmd.Connection != null && cmd.Connection.State != ConnectionState.Open)
            {
                foreach (DbParameter commandParameter in cmd.Parameters)
                {
                    if (commandParameter.Direction != ParameterDirection.Input)
                    {
                        canClear = false;
                        break;
                    }
                }
            }
            if (canClear)
            {
                cmd.Parameters.Clear();
            }
        }
        #endregion
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;

namespace Project.BaseFramework.DataProvider
{
    internal class TransConnection
    {
        public TransConnection()
        {
            this.Deeps = 0;
        }

        public DbTransaction DBTransaction { get; set; }

        public int Deeps { get; set; }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data.SqlClient;

namespace Project.BaseFramework.DataProvider
{
    public interface IDBClient
    {
        DbConnection GetDbConnection(string connectionString);

        DbCommand GetDbCommand(string cmdText);

        DbDataAdapter GetDbDataAdappter();

        DbParameter GetDbParameter();

        string GetPagingSql(string cmdText, int pageIndex, int pageSize, string orderInfo);
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data.SqlClient;

namespace Project.BaseFramework.DataProvider
{
    public class SqlServerClient:IDBClient
    {
        public DbConnection GetDbConnection(string connectionString)
        {
            return new SqlConnection(connectionString);
        }

        public DbCommand GetDbCommand(string cmdText)
        {
            return new SqlCommand(cmdText);
        }

        public DbDataAdapter GetDbDataAdappter()
        {
            return new SqlDataAdapter();
        }

        public DbParameter GetDbParameter()
        {
            return new SqlParameter();
        }

        public string GetPagingSql(string cmdText, int pageIndex, int pageSize, string orderInfo) 
        {
            int startIndex = (pageIndex - 1) * pageSize;
            int endIndex = startIndex + pageSize + 1;
            cmdText = string.Format(@";WITH T1 AS({0}),T2 AS(SELECT *,ROW_NUMBER()OVER ({1}) AS _RowNum FROM T1)
                                       SELECT *FROM T2
                                       WHERE _RowNum>{2} AND _RowNum<{3}",cmdText,orderInfo,startIndex,endIndex);
            return cmdText;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using MySql.Data.MySqlClient;

namespace Project.BaseFramework.DataProvider
{
    public class MySqlClient:IDBClient
    {
        public DbConnection GetDbConnection(string connectionString)
        {
            return new MySqlConnection(connectionString);
        }

        public DbCommand GetDbCommand(string cmdText)
        {
            return new MySqlCommand(cmdText);
        }

        public DbDataAdapter GetDbDataAdappter()
        {
            return new MySqlDataAdapter();
        }

        public DbParameter GetDbParameter()
        {
            return new MySqlParameter();
        }

        public string GetPagingSql(string cmdText, int pageIndex, int pageSize, string orderInfo)
        {
            int startIndex = (pageIndex - 1) * pageSize;
            cmdText = string.Format(@"{0} {1} Limit {2}, {3}", cmdText, orderInfo, startIndex,pageSize);
            return cmdText;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;

namespace Project.BaseFramework.DataProvider
{
    public class DBClientFactory
    {
        private static readonly string path = "Project.BaseFramework";

        public static IDBClient GetDBClient(string dbClientClassName) 
        {
            if(string.IsNullOrEmpty(dbClientClassName))
                dbClientClassName="SqlServerClient";
            string className = string.Format("{0}.DataProvider.{1}", path, dbClientClassName);
            return (IDBClient)Assembly.Load(path).CreateInstance(className);
        }
    }
}
View Code

配置文件性能

<appSettings>
    <add key="DBConnectionString" value="Data Source=.;Initial Catalog=ProjectData;Persist Security Info=True;User ID=sa;Password=kjkj,911;"/>
    <add key="DBClient" value="SqlServerClient"/>
</appSettings>

 

 

2  事務操做

2.1 單個事務操做示例this

try
{
    DBHelper.BeginTransaction();
    // add 
    DBHelper.ExecuteNonQuery(CommandType.Text, "INSERT INTO TRole(ID,RoleName) VALUES('R1','MKT')");

    //detele by pk
    DBHelper.ExecuteNonQuery(CommandType.Text, "DELETE FROM TRole WHERE ID='R1'");

    Console.WriteLine(string.Format("Success and Commited"));
    DBHelper.CommitTransaction();
}
catch (Exception ex)
{
    Console.WriteLine(string.Format("Exception and rollback"));
    DBHelper.RollbackTransaction();
}

 

用法是:只須要把相關聯的代碼放在BeginTransaction和CommitTransaction中間,若是發生異常調用RollbackTransaction便可。spa

實現事務的方法是:線程

首先,DBHelper維護一個TransConnection類型的字段,並添加ThreadStatic. ThreadStatic能夠維護在線程級別上的惟一性。

[ThreadStatic]
private static TransConnection TransConnectionObj = null;

 

其次,TransConnection的做用是保存事務,並記錄嵌套事務的嵌套級別。

internal class TransConnection
{
    public TransConnection()
    {
        this.Deeps = 0;
    }

    public DbTransaction DBTransaction { get; set; }

    public int Deeps { get; set; }
}

 

最後,當調用 BeginTransaction時建立TransConnection對象。以後的多個DbCommand命令都從這個事務上拿鏈接。由於TransConnectionObj添加了ThreadStatic屬性,因此它是線程惟一的,不會影響其它線程上的事務;全部方法執行完後,調用CommitTransaction 就提交事務,並關閉鏈接;若是發生異常,則調用RollbackTransaction,就會回滾全部命令,並關閉鏈接。

2.2 嵌套事務示例

static void Main(string[] args)
{

    try
    {
        DBHelper.BeginTransaction();

        // add 
        DBHelper.ExecuteNonQuery(CommandType.Text, "INSERT INTO TRole(ID,RoleName) VALUES('R1','MKT')");

        Transaction2();

        //detele by pk
        DBHelper.ExecuteNonQuery(CommandType.Text, "DELETE FROM TRole WHERE ID='R1'");

        Console.WriteLine(string.Format("Success and Commited"));
        DBHelper.CommitTransaction();
    }
    catch (Exception ex)
    {
        Console.WriteLine(string.Format("Exception and rollback"));
        DBHelper.RollbackTransaction();
    }

    Console.ReadLine();
}

private static void Transaction2() 
{
    try
    {
        DBHelper.BeginTransaction();
        //update model
        DBHelper.ExecuteNonQuery(CommandType.Text, "UPDATE TRole SET RoleName='Marketer' WHERE ID='R1'");
        //throw new Exception("");

        DbParameter param = DBHelper.CreateInDbParameter("@ID", DbType.String, "R1");
        DbDataReader reader= DBHelper.ExecuteReader(CommandType.Text, "SELECT * FROM TRole WHERE ID=@ID",param);
        while (reader.Read()) 
        {
            Console.WriteLine(reader["RoleName"]);
        }

        reader.Close();

        DBHelper.CommitTransaction();
    }
    catch(Exception ex)
    {
        Console.WriteLine(string.Format("Exception and rollback: {0}", ex.Message));
        DBHelper.RollbackTransaction();
        throw;
    }
}

 

2.2.1

當爲嵌套事務時,首次調用BeginTransaction,一樣會建立新的TransConnection對象,深度默認爲0,並保存在TransConnectionObj字段上;

第n(n>1)次調用時方法時,僅會累加嵌套的深度,不會開起新的事務。

public static void BeginTransaction()
{
    if (TransConnectionObj == null) 
    {
        DbConnection conn = DBClient.GetDbConnection(ConnectionString);
        OpenConn(conn);
        DbTransaction trans = conn.BeginTransaction();
        TransConnectionObj = new TransConnection();
        TransConnectionObj.DBTransaction = trans;
    }
    else 
    {
        TransConnectionObj.Deeps += 1;
    }
}

 

2.2.2

當CommitTransaction提交事務時,若是深度Deeps>0,那麼表示這次提交的事務是內層事務,計數器減1便可;

當調用CommitTransaction提交事務,若是深度爲0時,表示爲最外層事務,剛作實際上的提交事務工做;

public static void CommitTransaction()
{
    if (TransConnectionObj == null) return;
    if (TransConnectionObj.Deeps > 0)
    {
        TransConnectionObj.Deeps -= 1;
    }
    else
    {
        TransConnectionObj.DBTransaction.Commit();
        ReleaseTransaction();
    }
}

 

2.2.3

當RollbackTransaction提交事務時,若是深度Deeps>0,那麼表示這次提交的事務是內層事務,計數器減1便可;

當調用RollbackTransaction提交事務,若是深度爲0時,表示爲最外層事務,剛作實際上的回滾操做;

public static void RollbackTransaction()
{
    if (TransConnectionObj == null) return;
    if (TransConnectionObj.Deeps > 0)
    {
        TransConnectionObj.Deeps -= 1;
    }
    else
    {
        TransConnectionObj.DBTransaction.Rollback();
        ReleaseTransaction();
    }
}

 

3  變動數據庫

同一個底層庫,應用到不一樣項目時,數據庫可能會不一樣。若是咱們比較下不一樣數據庫操做類之間的不一樣點,咱們會發現全部的方法都是一致的,就是某些類型不一樣,以下表所示:

 

基類

SQL Server

MySql

DbConnection

SqlConnection

MySqlConnection

DbCommand

SqlCommand

MySqlCommand

DbDataAdapter

SqlDataAdapter

MySqlDataAdapte

DbParameter

SqlParameter

MySqlParameter

 

因此,根據子類出現的地方,能夠用父類替換的原則,將SqlHeper中關於特定數據庫的類,換成基類,並將建立特定數據庫對象實例的代碼統一到IDBClinet中

主要類有

DBHelper: 使用基類訪問數據庫,並聚合IDBClient來建立特定數據庫對象的實例。

IDBClient: 定義建立特定數據庫實例的接口,並轉成基類型;

SqlServerClient:定義建立SqlServer對象的實例,並轉成基類型;

MySqlCient:定義建立MySql對象的實例,並轉成基類型;

DBClientFactory:根據類名動態建立IDBClient的實現類;

 

最後若是想要更換數據庫時,只須要修改以下代碼,並在配置文件中修改下鏈接字符串和具體的DBClient的類名:

<appSettings>
    <add key="DBConnectionString" value="Data Source=.;Initial Catalog=ProjectData;Persist Security Info=True;User ID=sa;Password=kjkj,911;"/>
    <add key="DBClient" value="SqlServerClient"/>
</appSettings>

 

4  支持數據分頁

爲了支持數據分頁,在DBHelper添加了兩個方法,分別返回DataTable和DbDataReader, DbDataReader用於在ORM中轉成實體對象。

#region ExecutePaging
public static DataTable ExecutePagingDataTable(CommandType cmdType, string cmdText,int pageIndex,int pageSize,string orderInfo, params DbParameter[] parameterValues)
{
    cmdText = DBClient.GetPagingSql(cmdText, pageIndex, pageSize, orderInfo);
    return ExecuteDataTable(CommandType.Text, cmdText, parameterValues);
}

public static DbDataReader ExecutePagingReader(CommandType cmdType, string cmdText, int pageIndex, int pageSize, string orderInfo, params DbParameter[] parameterValues)
{
    cmdText = DBClient.GetPagingSql(cmdText, pageIndex, pageSize, orderInfo);
    return ExecuteReader(CommandType.Text, cmdText, parameterValues);
}
#endregion

 在分頁時,雖然各類數據庫的分頁方法不同,但它們都須要信息是:排序字段,開始索引位置,頁大小,結束索引位置(能夠經過開始索引位置和頁大小計算出來)。至於它們最終分頁的SQL不同,能夠放在實現了IDBClient的類中。

好比在SqlServerDBClient是用ROW_NUMBER作的:

public string GetPagingSql(string cmdText, int pageIndex, int pageSize, string orderInfo) 
{
    int startIndex = (pageIndex - 1) * pageSize;
    int endIndex = startIndex + pageSize + 1;
    cmdText = string.Format(@";WITH T1 AS({0}),T2 AS(SELECT *,ROW_NUMBER()OVER ({1}) AS _RowNum FROM T1)
                               SELECT *FROM T2
                               WHERE _RowNum>{2} AND _RowNum<{3}",cmdText,orderInfo,startIndex,endIndex);
    return cmdText;
}

在MySqlDBClient中是用Limit 實現的:

public string GetPagingSql(string cmdText, int pageIndex, int pageSize, string orderInfo)
{
    int startIndex = (pageIndex - 1) * pageSize;
    cmdText = string.Format(@"{0} {1} Limit {2}, {3}", cmdText, orderInfo, startIndex,pageSize);
    return cmdText;
}

 

在下一次隨筆中,將會用這個數據庫操做類,以及上篇文章用T4 Template生成代碼 來實現一個簡單的ORM框架,支持CRUD。暫時不考慮用反射來實現這個ORM,暫時還hold不住反射的性能問題。

相關文章
相關標籤/搜索