1. 程式人生 > >C#操作SqlServer MySql Oracle通用幫助類

C#操作SqlServer MySql Oracle通用幫助類

.html 結果 dbn t對象 htm cut ins 定義 div

C#操作SqlServer MySql Oracle通用幫助類

【前言】

  作為一款成熟的面向對象高級編程語言,C#在ADO.Net的支持上已然是做的很成熟,我們可以方便地調用ADO.Net操作各類關系型數據庫,在使用了多年的Sql_Helper_DG後,由於項目需要,於是乎,就準備寫一個Mysql_Helper在實現過程中,發現ADO.Net封裝之完善,以及面向對象的封裝、繼承、多態,有了這些特性,何不把數據庫操作封裝成為一個通用的類呢,此文由此鋪展而來...

【實現功能】

  這篇文章將要介紹的主要內容如下:

  1、ADO.NET之SqlServer

  2、ADO.NET之Oracle

  3、ADO.NET之MySql

  4、充分利用面向對象的特征,實現通用的操作類

【環境準備】

  1、MySql連接器的DLL引用

  使用Nuget搜索 MySql.Data 引用即可:

  技術分享

  2、Oracle連接器的DLL引用

  使用Nuget搜索 Oracle.ManagedDataAccess 進行引用:

  技術分享

【實現思路】

  在ADO.NET對SqlServer,Oracle,Mysql的操作熟練的基礎上,我們逐漸發現所有的操作都是使用的同一套的東西,不同的是:

  SqlServer的操作使用的是SqlConnection、SqlCommand,SqlDataAdapter;

  MySql使用的是MySqlConnection、MySqlCommand、MySqlDataAdapter;

  Oracle使用的是OracleSqlConnection、OracleCommand、OracleDataAdapter;

  該連接類,操作類都分別繼承自基礎類:DbConnection、DbCommand、DbDataAdapter;

  其類間關系如圖所示:

  1.DbConnection家族

  技術分享

  2.DbCommand家族

  技術分享

  3.DBDataAdapter家族

  技術分享

  了解如上的幾個特點後,我們裏面能聯系到了“多態”這個概念,我們可以使用同一套相同的代碼,用“多態”的特性實例化出不同的實例,進而可以進一步封裝我們的操作,達到代碼精煉可重用的目的。

【實現過程】

  1.定義枚舉類 Opt_DataBaseType 用於參數選擇具體要實例的數據庫

1 public enum Opt_DataBaseType
2 {
3         SqlServer,
4         MySql,
5         Oracle
6 }

  2.自定義內部類SqlConnection_WR_Safe(多態提供DbConnection的對象、讀寫分離的支持)

  1.在該內部類中,我們定義類屬性DbConnection用於承接根據不同的數據庫參數多態實例化後的對應Connection
  2.實現IDisposable接口,提供釋放DbConnection的方法
  3.在讀數據庫連接失敗時,及時切換到讀寫主數據庫,提升系統的可用性

技術分享
 1     internal class SqlConnection_WR_Safe : IDisposable
 2     {
 3         /// <summary>
 4         /// SqlConnection
 5         /// </summary>
 6         public DbConnection DbConnection { get; set; }
 7 
 8         public SqlConnection_WR_Safe(Opt_DataBaseType dataBaseType, string ConnString_RW)
 9         {
10             this.DbConnection = GetDbConnection(dataBaseType, ConnString_RW);
11         }
12         /**
13          * if read db disabled,switchover to read write db immediately
14          * */
15         public SqlConnection_WR_Safe(Opt_DataBaseType dataBaseType, string ConnString_R, string ConnString_RW)
16         {
17             try
18             {
19                 this.DbConnection = GetDbConnection(dataBaseType, ConnString_R);
20             }
21             catch (Exception)
22             {
23                 this.DbConnection = GetDbConnection(dataBaseType, ConnString_RW);
24             }
25         }
26 
27         /// <summary>
28         /// GetDataBase ConnectionString by database type and connection string -- private use
29         /// </summary>
30         /// <param name="dataBaseType"></param>
31         /// <param name="ConnString"></param>
32         /// <returns></returns>
33         private DbConnection GetDbConnection(Opt_DataBaseType dataBaseType, string ConnString)
34         {
35             switch (dataBaseType)
36             {
37                 case Opt_DataBaseType.SqlServer:
38                     return new SqlConnection(ConnString);
39                 case Opt_DataBaseType.MySql:
40                     return new MySqlConnection(ConnString);
41                 case Opt_DataBaseType.Oracle:
42                     return new OracleConnection(ConnString);
43                 default:
44                     return new SqlConnection(ConnString);
45             }
46         }
47         /// <summary>
48         /// Must Close Connection after use
49         /// </summary>
50         public void Dispose()
51         {
52             if (this.DbConnection != null)
53             {
54                 this.DbConnection.Dispose();
55             }
56         }
57     }
技術分享

  3.自定義內部類 DbCommandCommon 用於提供DbCommand對象

技術分享
 1     internal class DbCommandCommon : IDisposable
 2     {
 3         /// <summary>
 4         /// common dbcommand
 5         /// </summary>
 6         public DbCommand DbCommand { get; set; }
 7         public DbCommandCommon(Opt_DataBaseType dataBaseType)
 8         {
 9             this.DbCommand = GetDbCommand(dataBaseType);
10         }
11 
12         /// <summary>
13         /// Get DbCommand select database type
14         /// </summary>
15         /// <param name="dataBaseType"></param>
16         /// <returns></returns>
17         private DbCommand GetDbCommand(Opt_DataBaseType dataBaseType)
18         {
19             switch (dataBaseType)
20             {
21                 case Opt_DataBaseType.SqlServer:
22                     return new SqlCommand();
23                 case Opt_DataBaseType.MySql:
24                     return new MySqlCommand();
25                 case Opt_DataBaseType.Oracle:
26                     return new OracleCommand();
27                 default:
28                     return new SqlCommand();
29             }
30         }
31         /// <summary>
32         /// must dispose after use
33         /// </summary>
34         public void Dispose()
35         {
36             if (this.DbCommand != null)
37             {
38                 this.DbCommand.Dispose();
39             }
40         }
41     }
技術分享

  4.自定義內部類 DbDataAdapterCommon 用於提供DbDataAdapter

  該類繼承自DbDataAdapter,以實現DataAdapter的Fill方法,可以將結果集填充到DataSet中去。

技術分享
 1     /// <summary>
 2     /// DbDataAdapterCommon
 3     /// </summary>
 4     internal class DbDataAdapterCommon : DbDataAdapter, IDisposable
 5     {
 6         public DbDataAdapter DbDataAdapter { get; set; }
 7         public DbDataAdapterCommon(Opt_DataBaseType dataBaseType, DbCommand dbCommand)
 8         {
 9             //get dbAdapter
10             this.DbDataAdapter = GetDbAdapter(dataBaseType, dbCommand);
11             //provid select command
12             this.SelectCommand = dbCommand;
13         }
14         private DbDataAdapter GetDbAdapter(Opt_DataBaseType dataBaseType, DbCommand dbCommand)
15         {
16             switch (dataBaseType)
17             {
18                 case Opt_DataBaseType.SqlServer:
19                     return new SqlDataAdapter();
20                 case Opt_DataBaseType.MySql:
21                     return new MySqlDataAdapter();
22                 case Opt_DataBaseType.Oracle:
23                     return new OracleDataAdapter();
24                 default:
25                     return new SqlDataAdapter();
26             }
27         }
28         /// <summary>
29         /// must dispose after use
30         /// </summary>
31         public new void Dispose()
32         {
33             if (this.DbDataAdapter != null)
34             {
35                 this.DbDataAdapter.Dispose();
36             }
37         }
38     }
技術分享

  5.在執行Sql查詢的時候,我們便使用我們自定義的內部類進行操作

  >1 這裏以ExecuteNonQuery為例:

  技術分享  

技術分享
 1 public static int ExecuteNonQuery(string commandTextOrSpName, CommandType commandType = CommandType.Text)
 2 {
 3     using (SqlConnection_WR_Safe conn = new SqlConnection_WR_Safe(dataBaseType, ConnString_RW))
 4     {
 5         using (DbCommandCommon cmd = new DbCommandCommon(dataBaseType))
 6         {
 7             PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
 8             return cmd.DbCommand.ExecuteNonQuery();
 9         }
10     }
11 }
技術分享

  該代碼通過參數DataBaseType確定要實例化的數據庫類型,ConnString_RW傳入寫數據庫的連接字符串進行實例化,DbCommand也是使用dataBaseType實例我們需要實際操作的數據庫對象。
  >2 查詢ExecuteDataSet方法:

  技術分享

  該方法通過參數dataBaseType確定要實例化的具體DbConnection,通過讀寫分離的連接字符串進行選擇讀庫和寫庫。

技術分享
 1 public static DataSet ExecuteDataSet(string commandTextOrSpName, CommandType commandType = CommandType.Text)
 2 {
 3     using (SqlConnection_WR_Safe conn = new SqlConnection_WR_Safe(dataBaseType, ConnString_R, ConnString_RW))
 4     {
 5         using (DbCommandCommon cmd = new DbCommandCommon(dataBaseType))
 6         {
 7             PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
 8             using (DbDataAdapterCommon da = new DbDataAdapterCommon(dataBaseType, cmd.DbCommand))
 9             {
10                 DataSet ds = new DataSet();
11                 da.Fill(ds);
12                 return ds;
13             }
14         }
15     }
16 }
技術分享

 全部代碼見此:

  1、數據庫選擇器枚舉類:Opt_DataBaseType->

技術分享 View Code

  2、主類代碼Db_Helper_DG->

技術分享 Db_Helper_DG

  Db_Helper_DG簡介:

  本類分為 ExecuteNonQuery、ExecuteScalar、ExecuteScalar、ExecuteDataTable、ExecuteDataSet、ExecuteList Entity、ExecuteEntity七大部分,每一部分分為 無條件參數執行Sql語句或存儲過程、SqlParameter[]參數執行Sql語句,Object[]參數執行存儲過程三個重載方法。

  方法的詳細代碼見上一條主代碼Db_Helper_DG中折疊部分,這裏對ExecuteListEntity和ExecuteEntity方法進行著重介紹。

  ExecuteListEntity和ExecuteEntity,此二方法是為了將查詢結果和Model即Entity實體進行映射所用,使用C#反射Reflect技術,進行將查詢結果直接賦值成為了Entity或者List<Entity>對象(此亦是ORM框架的核心)

  ExecuteList方法通過二次封裝,顯式調用GetListFromDataSet方法,從DataSet結果集中遍歷結果以進行賦值,代碼如下:

技術分享
 1 public static List<Entity> GetListFromDataSet<Entity>(DataSet ds) where Entity : class
 2         {
 3             List<Entity> list = new List<Entity>();//實例化一個list對象
 4             PropertyInfo[] propertyInfos = typeof(Entity).GetProperties();     //獲取T對象的所有公共屬性
 5 
 6             DataTable dt = ds.Tables[0];    // 獲取到ds的dt
 7             if (dt.Rows.Count > 0)
 8             {
 9                 //判斷讀取的行是否>0 即數據庫數據已被讀取
10                 foreach (DataRow row in dt.Rows)
11                 {
12                     Entity model1 = System.Activator.CreateInstance<Entity>();//實例化一個對象,便於往list裏填充數據
13                     foreach (PropertyInfo propertyInfo in propertyInfos)
14                     {
15                         try
16                         {
17                             //遍歷模型裏所有的字段
18                             if (row[propertyInfo.Name] != System.DBNull.Value)
19                             {
20                                 //判斷值是否為空,如果空賦值為null見else
21                                 if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
22                                 {
23                                     //如果convertsionType為nullable類,聲明一個NullableConverter類,該類提供從Nullable類到基礎基元類型的轉換
24                                     NullableConverter nullableConverter = new NullableConverter(propertyInfo.PropertyType);
25                                     //將convertsionType轉換為nullable對的基礎基元類型
26                                     propertyInfo.SetValue(model1, Convert.ChangeType(row[propertyInfo.Name], nullableConverter.UnderlyingType), null);
27                                 }
28                                 else
29                                 {
30                                     propertyInfo.SetValue(model1, Convert.ChangeType(row[propertyInfo.Name], propertyInfo.PropertyType), null);
31                                 }
32                             }
33                             else
34                             {
35                                 propertyInfo.SetValue(model1, null, null);//如果數據庫的值為空,則賦值為null
36                             }
37                         }
38                         catch (Exception)
39                         {
40                             propertyInfo.SetValue(model1, null, null);//如果數據庫的值為空,則賦值為null
41                         }
42                     }
43                     list.Add(model1);//將對象填充到list中
44                 }
45             }
46             return list;
47         }
技術分享

  ExecuteEntity部分又分為從DataReader中獲取和Linq從List<Entity>獲取第一條進行獲取兩種方式,由於DataReader有占用連接不釋放的特點,在高並發的環境下使用並不友好,因此在實際生產環境中使用推薦使用第二種Linq獲取List<Entity>的方式:

技術分享
 1 public static Entity GetEntityFromDataReader<Entity>(DbDataReader reader) where Entity : class
 2         {
 3             Entity model = System.Activator.CreateInstance<Entity>();           //實例化一個T類型對象
 4             PropertyInfo[] propertyInfos = model.GetType().GetProperties();     //獲取T對象的所有公共屬性
 5             using (reader)
 6             {
 7                 if (reader.Read())
 8                 {
 9                     foreach (PropertyInfo propertyInfo in propertyInfos)
10                     {
11                         //遍歷模型裏所有的字段
12                         if (reader[propertyInfo.Name] != System.DBNull.Value)
13                         {
14                             //判斷值是否為空,如果空賦值為null見else
15                             if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
16                             {
17                                 //如果convertsionType為nullable類,聲明一個NullableConverter類,該類提供從Nullable類到基礎基元類型的轉換
18                                 NullableConverter nullableConverter = new NullableConverter(propertyInfo.PropertyType);
19                                 //將convertsionType轉換為nullable對的基礎基元類型
20                                 propertyInfo.SetValue(model, Convert.ChangeType(reader[propertyInfo.Name], nullableConverter.UnderlyingType), null);
21                             }
22                             else
23                             {
24                                 propertyInfo.SetValue(model, Convert.ChangeType(reader[propertyInfo.Name], propertyInfo.PropertyType), null);
25                             }
26                         }
27                         else
28                         {
29                             propertyInfo.SetValue(model, null, null);//如果數據庫的值為空,則賦值為null
30                         }
31                     }
32                     return model;//返回T類型的賦值後的對象 model
33                 }
34             }
35             return default(Entity);//返回引用類型和值類型的默認值0或null
36         }
技術分享
1 public static Entity GetEntityFromDataSet<Entity>(DataSet ds) where Entity : class
2         {
3             return GetListFromDataSet<Entity>(ds).FirstOrDefault();
4         }

【系統測試】

  在全部功能實現之余,下面我們進行代碼測試環節。

  1、MySql數據庫操作

  技術分享

  各種方式給Db_Helper_DG的鏈接字符串屬性進行賦值,這裏不再贅述。

  技術分享

  技術分享

  根據測試表的設計進行新建對應的實體類:

技術分享
1 public class TB_People
2     {
3         public Guid Uid { get; set; }
4         public string Name { get; set; }
5         public int Age { get; set; }
6         public int ClassId { get; set; }
7     }
技術分享

  技術分享

  填寫好連接字符串,並給Db_Helper_DG類的ConnString_Default屬性賦值後,我們直接調用方法進行查詢操作。

  技術分享

  調用靜態方法ExecuteList以便直接映射到實體類:

1 List<TB_People> peopleList = Db_Helper_DG.ExecuteList<TB_People>("select * from student where ClassId=?ClassId", System.Data.CommandType.Text, new MySqlParameter("?ClassId", 1));
2             foreach (var item in peopleList)
3             {
4                 Console.WriteLine(item.Name);
5             }

  技術分享

  這裏的MySql語句 select * from student where ClassId=?ClassId 然後參數化賦值 ?ClassId=1 進行查詢。

  結果如下:

  技術分享

  可見,查詢結果並無任何差池,自動映射到了實體類的屬性。

  2、SqlServer數據庫操作

  技術分享

  因為數據庫結構MySql和SqlServer的結構是一致的,因此使用上述的實體類TB_People。

  技術分享

  同樣填寫連接字符串,並給Db_Helper_DG類的ConnString_Default屬性賦值後,我們直接調用方法進行查詢操作。

  技術分享

  技術分享

  然後我們修改Sql語句,並且修改為SqlServer傳遞參數方式進行查詢:

  技術分享 

1 List<TB_People> peopleList = Db_Helper_DG.ExecuteList<TB_People>("select * from TB_People where [email protected]", System.Data.CommandType.Text, new SqlParameter("@ClassId", 1));
2 foreach (var item in peopleList)
3 {
4     Console.WriteLine(item.Name);
5 }

  select * from TB_People where ClassId =1,ClassId按照SqlServer參數傳遞的方式進行傳遞。

  技術分享

  可見,查詢結果並無任何差池,自動映射到了實體類的屬性。

  3、Oracle由於本人當前Oracle環境問題,先不進行測試。

C#操作SqlServer MySql Oracle通用幫助類