一直以來都有一個夢想,想用更少的代碼實現更多的功能。也在一些任職的公司看到過他們內部使用的一些ORM,確實很好很強大,但是個人覺得涉及知識面比較廣,不適合個人的理解和使用。最近突發奇想,想自己嘗試下,借助一些思路,自己寫一個簡單的、入門級的框架,所以便有了此文。此文這是一個雛形,實現了一個簡單的查詢效果,當然如果理解了,那么其他操作都是重復性的了(增加、刪除、修改)。
一些問題(僅針對本人)
1、如果采用剛入門的三層,代碼量太大,雖然可以使用一些工具來生成(T4等)。但是想實現動態查詢的參數化還是比較麻煩的,而且代碼重用性不高,幾乎每個文件實現的都是一模一樣的一些方法。
2、SQL的注入,本人以前的一些代碼就存在這樣的問題。
3、當然還有些問題就不在這里講了,以前用過三層的就知道了。
解決
1、個人思路是把整個數據訪問全部剝離處理,根據不同的實體,對不同的表進行操作。數據訪問層中的方法全是公用的,傳入參數和實體(T)。即所有實體公用一個數據訪問層,不再像三層那樣一個實體,一個數據訪問類。
2、將對數據的所有操作全部封裝成參數話查詢(本文的IN操作沒有參數化)。
3、對業務邏輯的處理全部采用面向對象的形式操作。
代碼實例
1、動態的構造查詢語句參數化及參數列表
private StringBuilder whereString;//SQL查詢條件 private Dictionary<string, object> paramTable;//SQL參數列表 private int KeyNum = 1;//參數標識 private string tableName;//表名 private string[] columnNames;//查詢列名 /// <summary> /// 構造函數 /// </summary> /// <param name="TableName">表名</param> /// <param name="ColumnNames">列名</param> public SelectString(string TableName, params string[] ColumnNames) { if (paramTable == null) paramTable = new Dictionary<string, object>(); if (whereString == null) { whereString = new StringBuilder(); tableName = TableName; columnNames = ColumnNames; } } /// <summary> /// 獲取篩選的列名組成的字符串。 /// </summary> /// <param name="columnNames">篩選的列名集合</param> /// <returns>列名組成的字符串</returns> private string GetColumns() { if (columnNames == null || columnNames.Length == 0) return "*"; string[] cols = new string[columnNames.Length]; for (int i = 0; i < columnNames.Length; i++) { cols[i] = string.Format("{0}", columnNames[i]); } return string.Join(",", cols); } /// <summary> /// and表達式 /// </summary> /// <param name="FieldName">字段名稱</param> /// <param name="FieldValue">字段值</param> /// <param name="Operat">操作符</param> public void Add(string FieldName, object FieldValue, string Operat) { if (Operat == "in") whereString.AppendFormat(" and {0} {1} ({2})", FieldName, Operat, FieldValue); else { whereString.AppendFormat(" and {0} {1} {2}", FieldName, Operat, string.Format("@Key{0}", KeyNum)); paramTable.Add(string.Format("@Key{0}", KeyNum), FieldValue); KeyNum++; } } /// <summary> /// or表達式 /// </summary> /// <param name="FieldName">字段名稱</param> /// <param name="FieldValue">字段值</param> /// <param name="Operat">操作符</param> public void Or(string FieldName, object FieldValue, string Operat) { if (Operat == "in") whereString.AppendFormat(" or {0} {1} ({2})", FieldName, Operat, FieldValue); else { whereString.AppendFormat(" or {0} {1} {2}", FieldName, Operat, string.Format("@Key{0}", KeyNum)); paramTable.Add(string.Format("@Key{0}", KeyNum), FieldValue); KeyNum++; } } /// <summary> /// SQL參數表 /// </summary> public Dictionary<string, object> ParamsTable { get { return paramTable; } set { paramTable = value; } } /// <summary> /// SQL查詢條件 /// </summary> public string QueryString { get { if (whereString.Length > 0) { return string.Format("select {2} from {0} where 1=1 {1}", tableName, whereString, GetColumns()); } else return string.Format("select {1} from {0} where 1=1", tableName, GetColumns()); } }
2、將構造好的參數傳入,獲取及處理數據,然后返回。
/// <summary> /// 獲取數據 /// </summary> /// <param name="Sql">SQL語句</param> /// <param name="paramTable">參數列表</param> /// <returns></returns> public List<T> GetList<T>(string Sql, Dictionary<string, object> paramTable) { List<T> ListT = new List<T>(); using (SqlConnection Connection = new SqlConnection(SqlString)) { Connection.Open(); using (SqlCommand Command = new SqlCommand()) { Command.Connection = Connection; Command.CommandText = Sql; //添加參數 foreach (KeyValuePair<string, object> ItemDictionary in paramTable) { Command.Parameters.Add(new SqlParameter() { ParameterName = ItemDictionary.Key, Value = ItemDictionary.Value }); } SqlDataReader DataReader = Command.ExecuteReader(CommandBehavior.CloseConnection); //反射賦值 PropertyInfo[] propertys = typeof(T).GetProperties(); try { //獲取需要返回的列 List<string> ListColumns = DataReader.GetSchemaTable().Rows.Cast<DataRow>().Select(p => p.ItemArray[0].ToString()).ToList(); while (DataReader.Read()) { T t = System.Activator.CreateInstance<T>(); foreach (PropertyInfo item in propertys) { if (ListColumns.Contains(item.Name)) if (DataReader[item.Name] != DBNull.Value) item.SetValue(t, DataReader[item.Name], null); } ListT.Add(t); } } catch (SqlException ex) { throw ex; } finally { //切記關閉 DataReader.Close(); } } } return ListT; }
3、使用(構造實體類)
/// <summary> /// 表名(務必保持與數據庫一致) /// </summary> public static string TableName = "Users"; #region 字段名(務必保持與數據庫一致) /// <summary> /// 用戶編號 /// </summary> public static string C_FUserID = "FUserID"; #endregion #region 屬性 /// <summary> /// 用戶編號 /// </summary> public int FUserID { get; set; }
使用實例
//實例化查詢構造器 SelectString QuerySql = new SelectString(Users.TableName); QuerySql.Add(Users.C_FUserID, 1000, Operat.DengYu); QuerySql.Add(Users.C_UName, "張三", Operat.Like); QuerySql.Or(Users.C_FUserID, "100,110,120,130,140,150,160,170", Operat.In); //獲取及填充數據 SqlHelper SqlHelper = new SqlHelper(); List<Users> ListUser = SqlHelper.GetList<Users>(QuerySql.QueryString, QuerySql.ParamsTable);
這里面涉及的知識面也不是特別廣,大家應該都能看的懂。其實就是在實體類中多封裝一些信息,然后通過一個構造器,構造出需要執行的SQL語句(參數化)和參數集合,然后在傳入底層,底層根據傳入進來的SQL及參數集合。獲取數據,然后再把同時一起傳入進來的類型T,根據反射動態賦值,然后再返回上去。這樣便簡單的實現了一個動態參數化查詢,只需要構造好實體類,然后查詢構造器,組裝好SQL和參數,傳入同一個底層方法,即可獲取不同表中的數據。此文只說到簡單的單表操作,可自行拓展。其他操作都可以照這種思路進行封裝哦,把平時需要使用到的一些跟數據庫打交道的方法都封裝好,然后使用的時候只需要調用就好了。這樣是不是使用起來比較方便呢?
到此本文就寫完了,其實原理都很簡單的,本人只是抱着分享的態度,高手請多多指教。源碼將於晚上發布到這里、這里喔(其實源碼差不多都在這里了)。