我也來寫:數據庫訪問類DBHelper

1、前言css

  相信許多人都百度過:「.net 數據庫訪問類」。而後就出來一大堆SqlHelper。我也用過這些SqlHelper,也本身寫過,一堆靜態方法,開始使用起來感受很不錯,它們也確實在不少時候能夠很好的工做。ADO.NET已經封裝很好了,咱們很容易就能夠實現本身的數據庫訪問類。web

  好久前,忘記在哪裏看到過了,有一個朋友寫了一篇【如何作一個好用的數據庫訪問類】(有興趣的朋友仍然能夠搜索到),這篇文章確實寫得很好,做者很詳細的講解了如何設計一個好的數據庫訪問類;所謂「好「是指:輕量、易用、通用、高效。面試

  其實代碼是好久前就實現了,只是如今才總結記錄,但願能夠分享一下學習的過程。ok,在開始前先來看幾個ADO.NET常見的面試題:sql

1. ADO.NET 5個核心對象是哪5個?數據庫

2. 與ADO.NET 相關對象中,哪些能夠用於數據綁定?c#

3. DataSet與DataReader有什麼區別?分別適用在什麼狀況?設計模式

2、需求數組

  這是一個簡單的、基於ADO.NET的數據庫訪問類,它最起碼要具有如下特色:  服務器

1. 支持多種數據庫架構

  搞.net的視乎有一個固定的思惟:數據庫就是用sql server。額,只是不少用sql server,但不是所有,也有不少用 my sql 等的。咱們並不能限制必定用什麼數據庫。

2. 支持多個數據庫

  有時候咱們的應用程序會用到多個數據庫,而且這些數據庫還不是部署在同一臺服務器上的。

3. 簡單

  知足常見的操做。

4. 可擴展

  能夠隨時增長新的方法;對於具體的數據源,也能夠有特有的操做。

3、主要說明

3.1 使用DbProviderFactory

  既然要支持多種數據庫,那麼咱們以前常寫的SqlConnection、SqlCommand 就都不能用了,由於它們是針對sql server 數據源的。若是換成 my sql 就是 MySqlConnection, Oracle 就是 OracleConnection 了。

  既然有那麼多種Connection,不少朋友可能會想到經過設計模式來處理,例如定義一個父類(或接口),而後各類類型的數據庫繼承它,而後再經過一個工廠,來建立所須要的對象;之後要增長哪一種類型的數據庫就再增長一個對應的類就能夠了。大概是像下面這樣:

    public abstract class DBHelper
    {
        public abstract void Open(string key){}
        public abstract int ExecuteNonQuery() { }
        public abstract object ExecuteScalar() { }
        public abstract DataSet GetDataSet() { }
    }

    public class SqlHelper : DBHelper
    {
        const char _prefix = '@';
        //實現抽象方法...
    }

    public class MySqlHelper : DBHelper
    {
        const char _prefix = "@";
        //實現抽象方法...
    }

    public class OracleHelper : DBHelper
    {
        const char _prefix = ":";
        //實現抽象方法...
    }

    public class DBFactory
    {
        public static DBHelper GetDBHelper()
        {
            //根據條件返回DBHelper
        }
    }

  這樣實現已經比用SqlXXX好不少了,這也是我以前寫過的一種方式。但它仍然不夠靈活,而且實現起來就會發現不少代碼都是相似的,這就與咱們上面的簡單的需求相違背了。

  經過上面的分析,咱們知道用工廠模式能夠解決咱們的問題,但這不用咱們本身實現,.net 早就提供這樣的工廠:DbProviderFactory。由名稱能夠指定DbProviderFactory就是數據源提供程序工廠,負責建立具體的數據源提供程序。它根據 ProviderName就能夠建立對應數據源的訪問對象了。這樣咱們的實現也由具體變成抽象了,具體的SqlConection變成了抽象的DbConnection。

  什麼是 ProviderName? 在配置 web.config 的connectionStrings 時,就會有一個 providerNmae 屬性,例如sql server就是 」System.Data.SqlClient「,這個名稱空間就是對應的數據源提供程序。

3.2 參數問題

  不一樣數據庫參數查詢的格式可能不同,例如 sql server/my sql 支持「@變量」 形式,而 oracle 支持「:變量」的形式。像上面的父類的寫法,子類就必須定義本身的參數前綴。但這些用了DbProviderFactory後也不是問題了。

3.3 using 問題

  咱們都知道using是c#的語法糖,其實編譯後就是 try-finaly;uisng寫起來比較優雅,並且在有異常的時候會自動調用對象的Disponse方法,避免有些人忘記調用。因此嵌套的 using,編譯後就是嵌套的try-finaly,但其實只要咱們注意在拋異常的時候釋放資源,一個try-finaly便可。

3.4 DbDataReader 問題

  實際項目中,咱們更多的是使用DbDataReader而非DataSet/DataTable,而 DbDataReader須要本身逐行讀取,這在每一個調用的地方都這樣寫是很麻煩的,怎麼解決?委託,又是它!

  說到委託還有一個小小的建議,有些人喜歡本身去定義委託,但其實.net已經內置了3種委託:Func、Action、Predicate,而且提供了多個重載版本,應該優先考慮使用這些委託,在不知足的狀況下,再去自定義。

3.5 在分層架構裏的角色

  爲 DAL 層提供數據訪問服務,由 DAL 直接調用;不涉及sql語句拼接、日誌記錄等。

4、例子

  假設要調用一個 P_GetFriends存儲過程,接收一個id參數,返回一個好友列表。以下:

        public List<Friend> GetFriends(int id)
        {
            try
            {                
                DBHelper helper = new DBHelper("dbConnectionKey");
                DbParameter[] parameters = new DbParameter[]
                {
                    helper.CreateDbParameter("id",id)
                };
                return helper.ExecuteReader(CommandType.StoredProcedure, "P_GetFriends", parameters,
                reader =>
                {
                    return new Friend()
                    {
                        ID = reader.GetInt32(reader.GetOrdinal("ID")),
                        Name = reader.GetString(reader.GetOrdinal("Name"))
                    };
                });
            }
            catch
            {
                throw;
            }
        }  

附源代碼

    public class DBHelper
    {
        #region 屬性

        /// <summary>
        /// 連接字符串
        /// </summary>
        private string conStr;

        /// <summary>
        /// DB工廠
        /// </summary>
        private DbProviderFactory provider;

        #endregion

        #region 構造函數

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="key">連接字符串鍵</param>
        public DBHelper(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }
            ConnectionStringSettings css = WebConfigurationManager.ConnectionStrings[key];
            if (css == null)
            {
                throw new InvalidOperationException("未找到指定的連接字符串!");
            }
            this.conStr = css.ConnectionString;
            this.provider = DbProviderFactories.GetFactory(css.ProviderName);
        }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="conStr">連接字符串</param>
        /// <param name="providerStr">數據源提供程序</param>
        public DBHelper(string conStr, string providerStr)
        {
            if (string.IsNullOrEmpty(conStr))
            {
                throw new ArgumentNullException("conStr");
            }
            if (string.IsNullOrEmpty(providerStr))
            {
                throw new ArgumentNullException("providerStr");
            }
            this.provider = DbProviderFactories.GetFactory(providerStr);
            this.conStr = conStr;
        }

        #endregion

        #region 外部方法

        /// <summary>
        /// 執行命令,返回受影響行數
        /// </summary>
        /// <param name="commandType">命令類型</param>
        /// <param name="sql">sql語句或存儲過程名稱</param>
        /// <param name="parameters">參數數組</param>
        /// <returns>受影響行數,失敗返回-1</returns>
        public virtual int ExecuteNonQuery(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            try
            {
                con.Open();
                return cmd.ExecuteNonQuery();
            }
            finally
            {
                cmd.Dispose();
                con.Dispose();
            }
        }

        /// <summary>
        /// 執行命令,返回第一行第一列對象
        /// </summary>
        /// <param name="commandType">命令類型</param>
        /// <param name="sql">sql語句或存儲過程名稱</param>
        /// <param name="parameters">參數數組</param>
        /// <returns>執行結果</returns>
        public virtual object ExecuteScalar(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            try
            {
                con.Open();
                return cmd.ExecuteScalar();
            }
            finally
            {
                cmd.Dispose();
                con.Dispose();
            }
        }

        /// <summary>
        /// 執行命令返回DataSet
        /// </summary>
        /// <param name="commandType">命令類型</param>
        /// <param name="sql">sql語句或存儲過程名稱</param>
        /// <param name="parameters">參數數組</param>
        /// <returns>DataSet</returns>
        public virtual DataSet GetDataSet(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            DataSet set = new DataSet();
            DbDataAdapter adapter = this.provider.CreateDataAdapter();
            try
            {
                con.Open();
                adapter.SelectCommand = cmd;
                adapter.Fill(set);
                return set;
            }
            finally
            {
                adapter.Dispose();
                cmd.Dispose();
                con.Dispose();
            }
        }

        /// <summary>
        /// 執行命令返回DbDataReader
        /// </summary>
        /// <param name="commandType">命令類型</param>
        /// <param name="sql">sql語句或存儲過程名稱</param>
        /// <param name="parameters">參數數組</param>
        /// <param name="action">委託</param>
        /// <returns>對象列表</returns>
        public virtual List<T> ExecuteReader<T>(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters,
            Func<DbDataReader, T> action)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters);
            DbDataReader reader = null;
            List<T> result = new List<T>();
            try
            {
                con.Open();
                reader = cmd.ExecuteReader();                
                while (reader.Read())
                {
                    var item = action(reader);
                    result.Add(item);
                }
                return result;               
            }
            finally
            {
                if (reader != null)
                {
                    reader.Dispose();
                }
                cmd.Dispose();
                con.Dispose();
            }
        }

        /// <summary>
        /// 批量執行sql語句
        /// </summary>
        /// <param name="sqlList">sql語句集合</param>
        /// <param name="paramList">參數數組集合</param>
        /// <returns>執行成功或失敗</returns>
        public virtual bool ExecuteSqlBatchByTrans(IEnumerable<string> sqlList, IEnumerable<List<DbParameter>> paramList)
        {
            DbConnection con = CreateConnection();
            DbCommand cmd = CreateCommand(con, CommandType.Text);
            DbTransaction trans = null;
            try
            {
                con.Open();
                trans = con.BeginTransaction();
                cmd.Transaction = trans;
                int length = sqlList.Count();
                IEnumerable<DbParameter> parameters = null;
                for (int i = 0; i < length; i++)
                {
                    cmd.CommandText = sqlList.ElementAt<string>(i);
                    cmd.Parameters.Clear();
                    parameters = paramList.ElementAt<List<DbParameter>>(i);
                    foreach (DbParameter pm in parameters)
                    {
                        cmd.Parameters.Add(pm);
                    }
                    cmd.ExecuteNonQuery();
                }
                trans.Commit();
                return true;
            }
            catch
            {
                if (trans != null)
                {
                    trans.Rollback();
                }
                throw;
            }
            finally
            {
                if (trans != null)
                {
                    trans.Dispose();
                }
                cmd.Dispose();
                con.Dispose();
            }
        }

        #endregion

        #region CreateDbParameter

        public DbParameter CreateDbParameter(string name, object value)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            return parameter;
        }

        public DbParameter CreateDbParameter(string name, object value, ParameterDirection direction)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Direction = direction;
            return parameter;
        }

        public DbParameter CreateDbParameter(string name, object value, int size)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Size = size;
            return parameter;
        }

        public DbParameter CreateDbParameter(string name, object value, int size, DbType type)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Size = size;
            parameter.DbType = type;
            return parameter;
        }

        public DbParameter CreateDbParameter(string name, object value, int size, DbType type, ParameterDirection direction)
        {
            DbParameter parameter = this.provider.CreateParameter();
            parameter.ParameterName = name;
            parameter.Value = value;
            parameter.Size = size;
            parameter.DbType = type;
            parameter.Direction = direction;
            return parameter;
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 獲取連接實例
        /// </summary>
        /// <returns>連接實例</returns>
        private DbConnection CreateConnection()
        {
            DbConnection con = this.provider.CreateConnection();
            con.ConnectionString = this.conStr;
            return con;
        }

        /// <summary>
        /// 獲取命令實例
        /// </summary>
        /// <param name="con">連接實例</param>
        /// <param name="commandType">命令類型</param>
        /// <param name="sqlOrProcName">sql語句或存儲過程名稱</param>
        /// <returns>命令實例</returns>
        private DbCommand CreateCommand(DbConnection con, CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters)
        {
            DbCommand cmd = InitCommand(con, commandType, parameters);
            cmd.CommandText = sqlOrProcName;
            return cmd;
        }

        /// <summary>
        /// 獲取命令實例
        /// </summary>
        /// <param name="con">連接實例</param>
        /// <param name="commandType">命令類型</param>
        /// <returns>命令實例</returns>
        private DbCommand CreateCommand(DbConnection con, CommandType commandType)
        {
            return InitCommand(con, commandType, null);
        }

        /// <summary>
        /// 初始化命令
        /// </summary>
        /// <param name="commandType">命令類型</param>
        /// <param name="parameters">參數集合</param>
        /// <returns></returns>
        private DbCommand InitCommand(DbConnection con, CommandType commandType, IEnumerable<DbParameter> parameters)
        {
            DbCommand cmd = con.CreateCommand();
            cmd.CommandType = commandType;
            if (parameters != null)
            {
                foreach (DbParameter pm in parameters)
                {
                    cmd.Parameters.Add(pm);
                }
            }
            return cmd;
        }

        #endregion
    }
相關文章
相關標籤/搜索