asp.net—自定义轻量级ORM
大型项目中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