在 .NET 的學習過程當中用過三種數據庫:Sql Server、Access、SQLite。Sql Server 用得相對多一點點,可是,麻煩,每次用它都須要開服務,並且還費資源,更麻煩的是拷貝到一臺沒有裝SqlServer的電腦上還不能用,因此通常練習中只要不涉及到什麼存儲過程這些也基本不用它。Access簡單的文件型數據庫,office的套件,基本的SQL語句也支持,在通常的小型應用系統中用起來還行,並且在Windows 7開始操做系統中就已經內置了Access的服務,隨便拷貝。SQLite用過一次,須要安裝服務,和Access同樣,拷貝一下就能夠用,就是管理工具的界面比較shit。sql
用過三種數據庫後,發現其實在微軟的ADO.NET中操做數據庫的方式是同樣的,連接對象都是繼承自實現了IDbConnection接口的抽象類DbConnection,以及實現了IDbCommand的接口的抽象類DbCommand,其它的都同樣。古就能夠統一用接口或者抽象來來進行操做數據庫了,這樣就可以實現基本的跨數據庫訪問的功能了。惟一不一樣的就是實例化鏈接字符串的時候要用具體的子類來進行實例化,這時就能夠用靜態工廠來實現,從而作到不修改程序就實現誇數據庫的功能,固然應用程序中的SQL語句是通用的才行,若是是某些數據庫特有的,那就不行了。數據庫
也成爲簡單工廠,主要是用來因對對象的變化,根據不一樣的要求建立不一樣的實例,就能夠進行一個不一樣的操做。在黑馬的基礎增強裏面,有一個比較經典的例子可以說明他的做用:簡單計算器。主程序中一直不變,變的是運算規則,而後把運算規則給抽出來,寫到一個工廠中,而後根據不一樣的運算符來建立不一樣的計算實例,而後用一個同一的接口返回這個實例對象,就可以完成計算了。數組
1 /// <summary>
2 /// 根據傳遞過來的運算符,來創造不一樣的子類對象 3 /// </summary>
4 /// <param name="op">運算符</param>
5 /// <returns>返回的是一個實現了接口ICalculatorable.ICalculable的子類對象</returns>
6 public static ICalculatorable.ICalculable CreatInstance (string op) 7 { 8 //建立一個接口變量,賦值爲null
9 ICalculatorable.ICalculable cc = null; 10 ////根據傳遞過來的運算符來建立對應的實例成員
11 switch (op) 12 { 13 //若是傳遞過來的是加法運算符,就建立一個計算加法的實例成員,而且賦值給上面的接口變量
14 case "+": cc = new AddLib_02.Add(); break; 15 case "-": cc = new SubLib_02.Sub(); break; 16 case "*": cc = new MultipLib_02.Multip(); break; 17 case "/": cc = new DivLib_02.Div(); break; 18 case "%": cc = new ModelLib_02.Model(); break; 19 //若上面的都不符合,就將接口變量賦值爲null
20 default: cc = null; 21 break; 22 } 23 //返回指向了一個具體實例對象的接口變量
24 return cc; 25 }
其實在訪問數據庫的時候也是同樣的,惟一變的就是連接實例,因此就能夠把建立實例的東西給抽出來,交給一個工廠來實現,工廠能夠經過讀取配置文件來進行建立相應的實例對象,而後返回。代碼以下:oracle
1 internal sealed class DbProvideFactory 2 { 3 private static string dbProvide = ConfigurationManager.AppSettings["provide"]; 4 public static string DbProvide { get { return dbProvide; } } 5
6 private static string connectionString = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; 7 /// <summary>
8 /// 鏈接字符串 9 /// </summary>
10 public static string ConnectionString 11 { 12 get { return connectionString; } 13 private set { connectionString = value; } 14 } 15
16
17 #region 返回一個鏈接對象
18 public static DbConnection GetConnection () 19 { 20 switch (dbProvide.ToLower()) 21 { 22 case "sqlserver": 23 case "sql": 24 case "mssqlserver": 25 case "mssql": return new SqlConnection(connectionString); 26 case "access": return new OleDbConnection(connectionString); 27 case "sqlite": return new SQLiteConnection(connectionString); 28 case "oracle": return new OracleConnection(connectionString); 29 default: return new SqlConnection(connectionString); 30 } 31 } 32 #endregion
33
34 #region 返回一個 Adapter
35 /// <summary>
36 /// 返回一個 Adapter 37 /// </summary>
38 /// <param name="cmd">Command 命令</param>
39 /// <returns></returns>
40 public static DataAdapter GetAdapter (IDbCommand cmd) 41 { 42 switch (dbProvide.ToLower()) 43 { 44 case "sqlserver": 45 case "sql": 46 case "mssqlserver": 47 case "mssql": return new SqlDataAdapter(cmd as SqlCommand); 48 case "access": return new OleDbDataAdapter(cmd as OleDbCommand); 49 case "sqlite": return new SQLiteDataAdapter(cmd as SQLiteCommand); 50 case "oracle": return new OracleDataAdapter(cmd as OracleCommand); 51 default: return new SqlDataAdapter(cmd as SqlCommand); 52 } 53 } 54 #endregion
55
56 #region 生成參數化查詢時的參數對象
57 /// <summary>
58 /// 生成參數化查詢時的參數對象 59 /// </summary>
60 /// <param name="name">參數名,@符號無關緊要</param>
61 ///
62 /// <returns></returns>
63 public static DbParameter GetParameter (string name, object value) 64 { 65 if (!name.StartsWith("@")) 66 { 67 name = "@" + name; 68 } 69 if (value == null) 70 { 71 value = DBNull.Value; 72 } 73 switch (dbProvide.ToLower()) 74 { 75 case "sqlserver": 76 case "sql": 77 case "mssqlserver": 78 case "mssql": return new SqlParameter(name, value); 79 case "access": return new OleDbParameter(name, value); 80 case "sqlite": return new SQLiteParameter(name, value); 81 case "oracle": return new OracleParameter(name, value); 82 default: return new SqlParameter(name, value); 83 } 84 } 85
86 /// <summary>
87 /// 生成參數化查詢時的 輸出參數 88 /// </summary>
89 /// <param name="name">參數名</param>
90 /// <param name="type">參數類型</param>
91 /// <returns></returns>
92 public static IDbDataParameter GetParameter (string name,DbType type) 93 { 94 if (!name.StartsWith("@")) 95 { 96 name = "@" + name; 97 } 98 IDbDataParameter p; 99 switch (dbProvide.ToLower()) 100 { 101 case "sqlserver": 102 case "sql": 103 case "mssqlserver": 104 case "mssql": p = new SqlParameter(name, type); break; 105 case "access": p = new OleDbParameter(name, type); break; 106 case "sqlite": p = new SQLiteParameter(name, type); break; 107 case "oracle": p = new OracleParameter(name, type); break; 108 default: p = new SqlParameter(name, type); break; 109 } 110 p.Direction = ParameterDirection.Output; 111 return p; 112 } 113 #endregion
114 }
上面代碼中首先讀取了配置文件,以及鏈接字符串,這點可能封裝的是太好,按理說,應該從DbHelper中傳遞過來,可是考慮到後面多處用到它,就把他放到這個地方了。
根據配置文件中數據提供者的值不一樣而後建立不一樣的數據庫鏈接對象。同時因爲在參數化查詢的時候,須要實例化參數對象,有封裝了兩個參數對象方法。在用SqlServer的存儲過程當中,考慮到有輸出參數,古作了一個重載。ide
至於 DataAdapter,因爲它裏也是能夠本身建立鏈接的,因此也就比較麻煩一點,手動建立了,其實能夠利用微軟已經提供好的一種方式,就是經過讀取配置文件中的配置提供者,而後調用一些方法來進行建立,實現原理中應該也是利用的反射,因爲這個值在配置文件中比較麻煩,須要寫全稱,也就放棄它了。工具
上面基本上就是利用簡單工廠來實現的建立不一樣數據庫實例和參數化對象的方式。sqlserver
SQLite須要在官方下載ADO.NET的插件,而後進行引用,Oracle數據庫沒有嘗試過,也須要應用組件,列在此處也是爲了之後方便擴展學習
這裏面就封裝了幾個常規的增刪改查的方法,調用工廠的方法獲得鏈接對象和參數,而後用接口去指向他們,因爲使用的是接口去指向,故在添加參數的時候,只可以經過遍從來實現,能夠改成抽象類來指向,這樣就可以一次添加一個數組了。代碼以下:spa
1 public partial class DbHelper 2 { 3 //#region 獲得一個鏈接對象,由具體的子類去重寫而實現
4 ///// <summary>
5 ///// 獲得一個鏈接對象,由具體的子類去重寫而實現 6 ///// </summary>
7 ///// <returns></returns>
8 //public abstruct DbConnection GetConnection (); 9 //#endregion
10
11 #region 執行增長、刪除、更新三個非查詢語句
12 public static int ExecuteNonQuery (string strSql, params IDataParameter[] pars) 13 { 14 using (IDbConnection conn = DbProvideFactory.GetConnection() ) 15 { 16 using (IDbCommand cmd = conn.CreateCommand()) 17 { 18 cmd.CommandText = strSql; 19
20 if (pars != null && pars.Length > 0) 21 { 22 foreach (var item in pars) 23 { 24 cmd.Parameters.Add(item); 25 } 26 } 27 conn.Open(); 28 return cmd.ExecuteNonQuery(); 29 } 30 } 31 } 32 #endregion
33
34 #region 執行標量值查詢
35 public static object ExecuteScalar (string strSql, params IDataParameter[] pars) 36 { 37 using (IDbConnection conn = DbProvideFactory.GetConnection()) 38 { 39 using (IDbCommand cmd = conn.CreateCommand()) 40 { 41 cmd.CommandText = strSql; 42 if (pars != null && pars.Length > 0) 43 { 44 foreach (var item in pars) 45 { 46 cmd.Parameters.Add(item); 47 } 48 } 49 conn.Open(); 50 return cmd.ExecuteScalar(); 51 } 52 } 53 } 54 #endregion
55
56 #region 返回一個只讀的DbDataReader
57 public static IDataReader ExecuteReader (string strSql,CommandType type, params IDataParameter[] pars) 58 { 59 IDbConnection conn = DbProvideFactory.GetConnection(); 60
61 using (IDbCommand cmd = conn.CreateCommand()) 62 { 63 cmd.CommandText = strSql; 64 if (pars != null && pars.Length > 0) 65 { 66 foreach (var item in pars) 67 { 68 cmd.Parameters.Add(item); 69 } 70 } 71 cmd.CommandType = type; 72 try
73 { 74 conn.Open(); 75 //外部關閉當前鏈接
76 return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection); 77 } 78 catch
79 { 80 conn.Close(); 81 conn.Dispose(); 82 throw; 83 } 84 } 85 } 86 #endregion
87
88 #region 返回一張數據表格,若是沒有查到數據就返回null
89 //因爲用的是接口(或者抽象類)不可以直接建立Command來添加參數,因此調用了工廠的方法建立一個鏈接對象,而後建立Command來實現參數的添加
90 public static DataTable DataAdpater (string strSql, params IDataParameter[] pars) 91 { 92 using (IDbConnection conn = DbProvideFactory.GetConnection()) 93 { 94 IDbCommand cmd = conn.CreateCommand(); 95 cmd.CommandText = strSql; 96 if (pars != null && pars.Length > 0) 97 { 98 foreach (var item in pars) 99 { 100 cmd.Parameters.Add(item); 101 } 102 } 103 IDataAdapter adapter = DbProvideFactory.GetAdapter(cmd); 104 DataSet set = new DataSet(); 105 int count = adapter.Fill(set); 106 return count > 0 ? set.Tables[0] : null; 107 } 108 } 109 #endregion
110
111 #region 將一個SqlDataReader對象轉換成一個實體類對象 +static TEntity MapEntity<TEntity>(SqlDataReader reader) where TEntity : class,new()
112 /// <summary>
113 /// 將一個DataReader對象轉換成一個實體類對象 114 /// </summary>
115 /// <typeparam name="TEntity">實體類型</typeparam>
116 /// <param name="reader">當前指向的reader</param>
117 /// <returns>實體對象</returns>
118 public static TEntity MapEntity<TEntity>(IDataReader reader) where TEntity : class,new() 119 { 120 try
121 { 122 var props = typeof(TEntity).GetProperties(); 123 var entity = new TEntity(); 124 foreach (var prop in props) 125 { 126 if (prop.CanWrite) 127 { 128 try
129 { 130 var index = reader.GetOrdinal(prop.Name); 131 var data = reader.GetValue(index); 132 if (data != DBNull.Value) 133 { 134 prop.SetValue(entity, Convert.ChangeType(data, prop.PropertyType), null); 135 } 136 } 137 catch (IndexOutOfRangeException) 138 { 139 continue; 140 } 141 } 142 } 143 return entity; 144 } 145 catch
146 { 147 return null; 148 } 149 } 150 #endregion
151 }
在上面代碼中,若是程序須要使用具體的鏈接對象,能夠將DBHelper改爲一個抽象類,而後把建立鏈接實例對象改爲抽象方法,就能夠了。
操作系統
經過上面的簡單工廠和用接口來指向操做數據庫的各類對象就可以作到簡單的跨數據庫的功能了