大型項目中ORM的使用已經是相當的頻繁。目前.NET(C#)中比較流行的ORM框架也有很多,比如SqlSugar,Dapper,Entity Framework(EF)等。
相信很多有2年以上工作經驗的園友都會使用其中一種或者幾種。同時多多少少也會存在有會用卻不懂其中原理的園友(我算其中一個),所以憑借
工作之余獨自鑽研了一段時間,現在分享下我的鑽研成果。 同時也希望園內大能者指出不足之處。
在工作中,本人覺得寫SQL 查詢數據還是挺方便。所以這個輕量級的ORM中對於查詢還是使用寫SQL的方式
下圖就是主要的文件:
DataFieldAttribute.cs:實體映射表字段 特性(用於標注實體類中成員屬性對應數據庫中的字段名和字段類型)
PropertyAttribute.cs :實體映射數據庫表 特性(用於標注實體類對應數據庫中哪個表)
DBCrateFactory.cs :創建數據庫對象的工廠(用於創建哪種數據庫對象 MS SQL 還是 ORACLE)
SQLHelper.cs :這是一個抽象函數。DBWorks文件夾下所有類都繼承該抽象函數,這些子類就必須實現SQLHelper中的抽象方法同時也可以使用該抽象函數的公用方法
IWiteem.cs : 對外接口
Witeem.cs :繼承並實現IWiteem接口
CommonHelper.cs :通用工具類
DBWorks文件夾下存放的是數據庫操作類(因為是DEMO,所以只設置了MS SQL和ORACLE)
Enum文件夾下存放的是需要使用到的一些枚舉類(ColumnKeyType.cs 字段狀態, DBEnum.cs 數據庫類型)
下圖是接口中提供的方法:
下載地址中的代碼或許還存在少部分瑕疵,在每次發現並更改過程后我及時更新。
2018-06-26 Bug:
1、SQLHelper類的ExecuteQueryPage函數
2、CommonHelper類的 DataTableToObject<T> 和 DataTableToList<T>修改成根據DataFieldAttribute特性中的字段反射對應的值

public static T DataTableToObject<T>(DataTable dt, bool IsDataField) where T : new() { Type type = typeof(T); string tempName = string.Empty; T myt = new T(); PropertyInfo[] propertys = myt.GetType().GetProperties(); PropertyInfo[] array = propertys; int i = 0; DataFieldAttribute attribute = null; PropertyAttribute proAttribute = null; while (i < array.Length) { PropertyInfo pi = array[i]; if (IsDataField) { object[] infos = null; object[] pros = null; infos = pi.GetCustomAttributes(typeof(DataFieldAttribute), false); pros = pi.GetCustomAttributes(typeof(PropertyAttribute), false); if (infos.Length > 0) { attribute = (DataFieldAttribute)(infos[0]); if (pros.Length>0) { proAttribute = (PropertyAttribute)(pros[0]); if (proAttribute.columnKeyType != ColumnKeyType.Extend) { tempName = attribute.FieldName; } }else tempName = attribute.FieldName; } } else tempName = pi.Name; if (dt.Columns.Contains(tempName)) { if (pi.CanWrite) { object value = dt.Rows[0][tempName]; if (value.GetType().Equals(typeof(DateTime))) value = Convert.ToString(value); if (value != DBNull.Value) pi.SetValue(myt, value, null); } } i += 1; continue; } return myt; }

public static List<T> DataTableToList<T>(DataTable dt, bool IsDataField) where T : new() { List<T> ts = new List<T>(); Type type = typeof(T); string tempName = string.Empty; foreach (DataRow dr in dt.Rows) { T myt = new T(); PropertyInfo[] propertys = myt.GetType().GetProperties(); PropertyInfo[] array = propertys; int i = 0; DataFieldAttribute attribute = null; PropertyAttribute proAttribute = null; while (i < array.Length) { PropertyInfo pi = array[i]; if (IsDataField) { object[] infos = null; object[] pros = null; infos = pi.GetCustomAttributes(typeof(DataFieldAttribute), false); pros = pi.GetCustomAttributes(typeof(PropertyAttribute), false); if (infos.Length > 0) { attribute = (DataFieldAttribute)(infos[0]); if (pros.Length > 0) { proAttribute = (PropertyAttribute)(pros[0]); if (proAttribute.columnKeyType != ColumnKeyType.Extend) { tempName = attribute.FieldName; } } else tempName = attribute.FieldName; } } else tempName = pi.Name; if (dt.Columns.Contains(tempName)) { if (pi.CanWrite) { object value = dr[tempName]; if (value.GetType().Equals(typeof(DateTime))) value = System.Convert.ToString(value); if (value != DBNull.Value) pi.SetValue(myt, value, null); } } i += 1; continue; } ts.Add(myt); } return ts; }
2018-06-28 Bug:反射實體獲取表字段時不是去特性中標注的字段名(現已修復)
1、修改工具類中的GetTableColumns函數(注釋部分為舊的代碼),GetTableColumns函數也做了相應的修改

public static List<string> GetTableColumns(PropertyInfo[] pis, ref List<PropertyInfo> proList, bool Isidentity = false) { List<string> columns = new List<string>(); object[] infos = null; object[] fields = null; DataFieldAttribute Fieldattribute = null; PropertyAttribute attribute = null; foreach (PropertyInfo pi in pis) { //獲取此成員所有自定義特性 infos = pi.GetCustomAttributes(typeof(PropertyAttribute),false); fields = pi.GetCustomAttributes(typeof(DataFieldAttribute), false); if (fields.Length == 0) { continue; } Fieldattribute = (DataFieldAttribute)(fields[0]); if (infos.Length > 0) { attribute = (PropertyAttribute)(infos[0]); if (attribute == null) { //columns.Add(pi.Name); columns.Add(Fieldattribute.FieldName); proList.Add(pi); } else { switch (attribute.columnKeyType) { case ColumnKeyType.Extend: break; case ColumnKeyType.Identity: { if (Isidentity) { //columns.Add(pi.Name); columns.Add(Fieldattribute.FieldName); proList.Add(pi); } }; break; default: { //columns.Add(pi.Name); columns.Add(Fieldattribute.FieldName); proList.Add(pi); }; break; } } } } return columns; }
2、MSSql.cs類 對於上述BUG做了修改

public override int Add<T>(IEnumerable<T> obj) { try { int i = 0; int result = 0; int success = 0; //Type type = obj.GetType(); Type type = typeof(T); //獲取表名 string tableName = CommonHelper.GetTableName(type); PropertyInfo[] pis = type.GetProperties(); //獲取所有字段,和主鍵名稱 List<string> columns = null; List<PropertyInfo> proList = new List<PropertyInfo>(); columns = CommonHelper.GetTableColumns(pis,ref proList, false); //處理是否包含主鍵插入 //if (isIdentity) //{ // columns = CommonHelper.GetTableColumns(pis, true); //} //else //{ // columns = CommonHelper.GetTableColumns(pis, false); //} foreach (T item in obj) { //生成SQL語句 StringBuilder sqlText = new StringBuilder(); sqlText.Append(" INSERT INTO "); sqlText.Append(tableName); sqlText.Append(" ("); //第一個字段 sqlText.Append(columns[0]); //第二個起所有字段 int loop = columns.Count; for (i = 1; i < loop; i++) { sqlText.Append(","); sqlText.Append(columns[i]); } sqlText.Append(") VALUES ("); //第一個字段 sqlText.Append("@"); sqlText.Append(columns[0]); //第二個起所有字段 for (i = 1; i < loop; i++) { sqlText.Append(",@"); sqlText.Append(columns[i]); } sqlText.Append(");"); //生成SqlParamter PropertyInfo propertyInfo = null; List<SqlParameter> paras = new List<SqlParameter>(); for (i = 0; i < loop; i++) { propertyInfo = proList[i]; SqlParameter para = new SqlParameter(columns[i], CommonHelper.GetSqlType(propertyInfo.PropertyType), -1); para.Value = propertyInfo.GetValue(item); paras.Add(para); } result = ExecuteNonQuery(sqlText.ToString(), CommandType.Text, paras, false); if (result > 0) { success += 1; } } if (conn.State == ConnectionState.Open) { ConColsed(); } return success; } catch (Exception ex) { execlog.Debug(DateTime.Now.ToString() + ": Add失敗,原因【" + ex.ToString() + "】"); return -1; } }

public override int Delete<T>(IEnumerable<T> obj) { try { int result = 0; int success = 0; //Type type = obj.GetType(); Type type = typeof(T); //獲取表名 string tableName = CommonHelper.GetTableName(type); PropertyInfo[] pis = type.GetProperties(); PropertyInfo identityInfo = null; identityInfo = CommonHelper.GetTableIdentity(pis); string identityName = CommonHelper.GetIdentityName(pis); if (identityInfo == null) { return 0; } if (string.IsNullOrEmpty(identityName)) { identityName = identityInfo.Name; } foreach (T item in obj) { //生成SQL語句 StringBuilder sqlText = new StringBuilder(); sqlText.Append(" DELETE FROM "); sqlText.Append(tableName); sqlText.Append(" WHERE 1=1 "); //主鍵篩選 sqlText.Append(" AND " + identityName); sqlText.Append("=@" + identityName); //生成SqlParamter List<SqlParameter> paras = new List<SqlParameter>(); SqlParameter para = new SqlParameter(identityName, CommonHelper.GetSqlType(identityInfo.PropertyType), -1); para.Value = identityInfo.GetValue(item); paras.Add(para); result = ExecuteNonQuery(sqlText.ToString(), CommandType.Text, paras,false); if (result>0) { success += 1; } } if (conn.State == ConnectionState.Open) { ConColsed(); } return success; } catch (Exception ex) { execlog.Debug(DateTime.Now.ToString() + ": Delete失敗,原因【" + ex.ToString() + "】"); return -1; } }

public override int Update<T>(IEnumerable<T> obj) { try { int i = 0; int result = 0; int success = 0; //Type type = obj.GetType(); Type type = typeof(T); //獲取表名 string tableName = CommonHelper.GetTableName(type); PropertyInfo[] pis = type.GetProperties(); //獲取所有字段,和主鍵名稱 string identityName = CommonHelper.GetIdentityName(pis); //獲取主鍵名稱 PropertyInfo identityInfo = null; identityInfo = CommonHelper.GetTableIdentity(pis); if (identityInfo == null) { return 0; } if (string.IsNullOrEmpty(identityName)) { identityName = identityInfo.Name; } List<string> columns = null; List<PropertyInfo> proList = new List<PropertyInfo>(); //獲取所有字段名稱 columns = CommonHelper.GetTableColumns(pis, ref proList, true); foreach (T item in obj) { //生成SQL語句 StringBuilder sqlText = new StringBuilder(); int loop = columns.Count; sqlText.Append(" UPDATE "); sqlText.Append(tableName); sqlText.Append(" SET "); //第二個起所有字段 for (i = 0; i < loop; i++) { //判斷第一個字段是否為主鍵 if (columns[i] == identityName) { continue; } sqlText.Append(columns[i] + "=@" + columns[i]); if (i < loop - 1) { sqlText.Append(","); } } //主鍵篩選 sqlText.Append(" WHERE " + identityName); sqlText.Append("=@" + identityName); //生成SqlParamter List<SqlParameter> paras = new List<SqlParameter>(); PropertyInfo propertyInfo = null; for (i = 0; i < loop; i++) { propertyInfo = proList[i]; SqlParameter para = new SqlParameter(columns[i], CommonHelper.GetSqlType(propertyInfo.PropertyType), -1); para.Value = propertyInfo.GetValue(item); paras.Add(para); } result = ExecuteNonQuery(sqlText.ToString(), CommandType.Text, paras,false); if (result>0) { success += 1; } } if (conn.State == ConnectionState.Open) { ConColsed(); } return success; } catch (Exception ex) { execlog.Debug(DateTime.Now.ToString() + ": Update失敗,原因【" + ex.ToString() + "】"); return -1; } }

public override T GetModel<T>(string id) { int i = 0; Type type = typeof(T); T myT = new T(); //獲取表名 string tableName = CommonHelper.GetTableName(type); PropertyInfo[] pis = type.GetProperties(); PropertyInfo identityInfo = null; identityInfo = CommonHelper.GetTableIdentity(pis); string identityName = CommonHelper.GetIdentityName(pis); if (identityInfo == null) { return default(T); } if (string.IsNullOrEmpty(identityName)) { identityName = identityInfo.Name; } //獲取所有字段,和主鍵名稱 List<string> columns = null; List<PropertyInfo> proList = new List<PropertyInfo>(); //獲取所有字段名稱 List<ColumnKeyType> filterList = new List<ColumnKeyType>(); filterList.Add(ColumnKeyType.Default); filterList.Add(ColumnKeyType.Read); filterList.Add(ColumnKeyType.Identity); columns = CommonHelper.GetTableColumns(pis, ref proList, true); //生成SQL語句 StringBuilder sqlText = new StringBuilder(); sqlText.Append(" SELECT "); //第一個字段 sqlText.Append(columns[0]); //第二個起所有字段 int loop = columns.Count; for (i = 1; i < loop; i++) { sqlText.Append(","); sqlText.Append(columns[i]); } sqlText.Append(" FROM "); sqlText.Append(tableName); sqlText.Append(" WHERE 1=1 AND "); sqlText.Append(identityName + "=@" + identityName); //生成SqlParamter List<SqlParameter> paras = new List<SqlParameter>(); SqlParameter para = new SqlParameter(identityName, CommonHelper.GetSqlType(identityInfo.PropertyType), -1); para.Value = id; paras.Add(para); return GetModel<T>(sqlText.ToString(), CommandType.Text, paras); }
3、事務處理應先打開數據庫連接
2018-12-13 獲取所有字段名稱的地方加入緩存機制,目的是避免每次執行都需要去獲取字段名稱數據集,提高效率:
Update 和 GetModel 方法的緩存機制
//獲取所有字段名稱(緩存處理) if (CacheHelper.GetCache(tableName) != null) { columns = (List<string>)CacheHelper.GetCache(tableName); } else { columns = CommonHelper.GetTableColumns(pis, ref proList, true); CacheHelper.SetCache(tableName, columns, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(60)); }
Add方法的緩存機制:
//獲取所有字段名稱(緩存處理) if (CacheHelper.GetCache(tableName + isIdentity.ToString()) != null) { columns = (List<string>)CacheHelper.GetCache(tableName + isIdentity.ToString()); } else { columns = CommonHelper.GetTableColumns(pis, ref proList, isIdentity); CacheHelper.SetCache(tableName + isIdentity.ToString(), columns, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(60)); }
github下載地址:https://github.com/witeem/ASP.NET-ORM.git