.net 开发框架(二) [实体层与ScriptQuery类] àì夳堔傛蜴生んèń 2021-12-21 12:43 109阅读 0赞 本篇我描述DevNet中实体层的实现,.net的数据集很强大,原来做cs项目时都用的DataTable,不管从性能,灵活性,可操作性以及写代码的方便性都很实用,唯一感觉不舒服的是在获取某字段值是需要使用DataRow\[fieldName\]或者DataRow\[index\]方式,在项目需求变化数据字段有变化时,需要修改大量的字段名称,在编译时并不会报错,项目很容易出错。再到后来做bs项目时使用了实体模式,虽然在灵活性及List数据集的计算等方面不如DataTable方便,但感觉在维护及开发调试中方便了许多。 实体如果都要手写的话就受不了了,网上很多生成实体的工具,我也根据DevNet类库弄了个实体生成器,有需要的朋友可以到第一篇下载。 在转换成实体时原先都是用反射方法,网上绝大部分都使用该方法,虽然用起来没啥大问题,但我感觉对性能多少有点影响。下面的反射转换实体的代码,DevNet类库中仍保留着。 ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 /// <summary> /// 获取实体集合\[使用反射\] /// </summary> /// <typeparam name="T"> 实体(请确保存在无参数构造函数) </typeparam> /// <param name="table"> 内存表 </param> /// <returns></returns> public static List < T > GetEntityCollection < T > (DataTable table) where T : class , new () \{ if (table == null ) throw new ArgumentNullException( " table " , " 参数table不能为null " ); List < T > ts = new List < T > (); foreach (DataRow dr in table.Rows) \{ T t = new T(); SetObjByRow(t, dr); ts.Add(t); \} return ts; \} /// <summary> /// 从DataRow中获取数据设置Object对象\[实体类\]的值\[使用反射\] /// </summary> /// <typeparam name="T"></typeparam> /// <param name="objEntity"> Object对象\[实体类\](属性名称请与数据表字段名称保持一致) </param> /// <param name="dataRow"></param> /// <returns></returns> public static void SetObjByRow < T > (T objEntity, DataRow dataRow) where T : class \{ if (objEntity == null ) throw new ArgumentNullException( " objEntity " ); if (dataRow == null ) throw new ArgumentNullException( " dataRow " ); System.Reflection.PropertyInfo\[\] objs = objEntity.GetType().GetProperties(); foreach (System.Reflection.PropertyInfo pobj in objs) \{ int i = dataRow.Table.Columns.IndexOf(pobj.Name); if (i >= 0 ) \{ if (dataRow\[pobj.Name\] is DBNull) continue ; try \{ pobj.SetValue(objEntity, dataRow\[pobj.Name\], null ); \} catch (Exception ex) \{ throw new Exception( " 设置实体属性 " \+ pobj.Name \+ " 值时出错,请检查数据表字段 " \+ pobj.Name \+ " 和该实体属性类型是否一致。 " \+ ex.Message, ex); \} \} \} \} 这些方法都在DevNet的DBHelper.cs类中 ,该类中也保留了从DbDataReader转换成实体集合的方法。你可以从(一)篇文档中下载的DevNet.chm文件中查找。 本来一直使用也相安无事,本着精益求精的精神,一直想把反射的方法改掉,所幸从网上找到了方法,有位似乎叫深蓝博士(很抱歉,记得不太清了,如果您看到此篇还请见谅)的博客中实体的思路,方法相当不错,根据他的实体模式,我修改了我的实体结构,从我的实体生成器中选择EntityBase项生成的实体如下: ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 using System; using System.Collections.Generic; using System.Text; using DevNet.Common; // 请添加引用 using DevNet.Common.Entity; namespace CodeDemo.Entity \{ \#region ====PermissionEntity==== /// <summary> /// 表 Permission 的实体类 /// </summary> \[Serializable\] public class Permission: EntityBase \{ public Permission() \{ AddProperty(PermissionID\_FieldName, 0 ); AddProperty(PermissionName\_FieldName, string .Empty); AddProperty(PermissionMemo\_FieldName, string .Empty); AddProperty(PerParentID\_FieldName, 0 ); AddProperty(ImageURL\_FieldName, string .Empty); AddProperty(LinkURL\_FieldName, string .Empty); AddProperty(DisplayIndex\_FieldName, 0 ); AddProperty(IsShow\_FieldName, false ); AddProperty(Owner\_FieldName, 0 ); base .TableName = Permission\_TableName; base .AutoIncrements = AutoIncrement; base .PrimaryKeyFields = PrimaryKeyField; \} \#region ====表名称、字段名称、主键字段、自动增长型字段名称==== /// <summary> /// 表 Permission 数据表名称 /// </summary> public const string Permission\_TableName = " PERMISSION " ; /// <summary> /// 表 Permission 主键字段集合 /// </summary> public readonly static string \[\] PrimaryKeyField = new string \[\] \{ " PermissionID " \}; /// <summary> /// 表 Permission 自动增长型字段名称 /// </summary> public const string AutoIncrement = "" ; /// <summary> /// PermissionID 字段名称 /// </summary> public const string PermissionID\_FieldName = " PermissionID " ; /// <summary> /// 权限名称 字段名称 /// </summary> public const string PermissionName\_FieldName = " PermissionName " ; /// <summary> /// 权限说明 字段名称 /// </summary> public const string PermissionMemo\_FieldName = " PermissionMemo " ; /// <summary> /// 父权限ID 字段名称 /// </summary> public const string PerParentID\_FieldName = " PerParentID " ; /// <summary> /// 图片URL 字段名称 /// </summary> public const string ImageURL\_FieldName = " ImageURL " ; /// <summary> /// 连接URL 字段名称 /// </summary> public const string LinkURL\_FieldName = " LinkURL " ; /// <summary> /// 显示索引 字段名称 /// </summary> public const string DisplayIndex\_FieldName = " DisplayIndex " ; /// <summary> /// 是否在管理显示 字段名称 /// </summary> public const string IsShow\_FieldName = " IsShow " ; /// <summary> /// 权限所属后台系统(多后台系统,譬如:1系统后台权限 2用户后台权限 3园区后台权限 4 政府后台权限......) 字段名称 /// </summary> public const string Owner\_FieldName = " Owner " ; \#endregion \#region ====字段属性==== /// <summary> /// PermissionID 列 /// </summary> public int PermissionID \{ get \{ return Convert.ToInt32(GetProperty(PermissionID\_FieldName)); \} set \{ SetProperty(PermissionID\_FieldName, value); \} \} /// <summary> /// 权限名称 列 /// </summary> public string PermissionName \{ get \{ return Convert.ToString(GetProperty(PermissionName\_FieldName)); \} set \{ SetProperty(PermissionName\_FieldName, value); \} \} /// <summary> /// 权限说明 列 /// </summary> public string PermissionMemo \{ get \{ return Convert.ToString(GetProperty(PermissionMemo\_FieldName)); \} set \{ SetProperty(PermissionMemo\_FieldName, value); \} \} /// <summary> /// 父权限ID 列 /// </summary> public int PerParentID \{ get \{ return Convert.ToInt32(GetProperty(PerParentID\_FieldName)); \} set \{ SetProperty(PerParentID\_FieldName, value); \} \} /// <summary> /// 图片URL 列 /// </summary> public string ImageURL \{ get \{ return Convert.ToString(GetProperty(ImageURL\_FieldName)); \} set \{ SetProperty(ImageURL\_FieldName, value); \} \} /// <summary> /// 连接URL 列 /// </summary> public string LinkURL \{ get \{ return Convert.ToString(GetProperty(LinkURL\_FieldName)); \} set \{ SetProperty(LinkURL\_FieldName, value); \} \} /// <summary> /// 显示索引 列 /// </summary> public int DisplayIndex \{ get \{ return Convert.ToInt32(GetProperty(DisplayIndex\_FieldName)); \} set \{ SetProperty(DisplayIndex\_FieldName, value); \} \} /// <summary> /// 是否在管理显示 列 /// </summary> public bool IsShow \{ get \{ return Convert.ToBoolean(GetProperty(IsShow\_FieldName)); \} set \{ SetProperty(IsShow\_FieldName, value); \} \} /// <summary> /// 权限所属后台系统(多后台系统,譬如:1系统后台权限 2用户后台权限 3园区后台权限 4 政府后台权限......) 列 /// </summary> public int Owner \{ get \{ return Convert.ToInt32(GetProperty(Owner\_FieldName)); \} set \{ SetProperty(Owner\_FieldName, value); \} \} \#endregion \#region ====表关系属性==== \#endregion \} \#endregion \} 实体中的常量是数据表的字段名称,我配合使用我的ScriptQuery.cs类(下面会讲一点)操作,完全不需要把字段名称写在项目中,有了这个实体,那么实体转换就不再需要用反射了。 ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 /// <summary> /// 设置实体属性值\[使用EntityBase中的方法\] /// </summary> /// <typeparam name="T"> 实体泛型\[请继承自EntityBase\] </typeparam> /// <param name="objEntity"> 泛型对象\[请继承自EntityBase\](属性名称请与数据表字段名称保持一致) </param> /// <param name="dataRow"> DataRow数据行 </param> public static void SetEntityByRow < T > (T objEntity, DataRow dataRow) where T : EntityBase \{ if (objEntity == null ) throw new ArgumentNullException( " objEntity " ); if (dataRow == null ) throw new ArgumentNullException( " dataRow " ); foreach (DataColumn col in dataRow.Table.Columns) \{ if (dataRow\[col\] is DBNull) continue ; objEntity.SetPropertyValue(col.ColumnName, dataRow\[col\]); \} \} 在DBHelper.cs类中你可以找到该方法。使用实体的SetPropertyValue方法设置实体属性值,不再依赖反射。另外在该类中有这样一个方法 ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 /// <summary> /// 根据Object对象\[实体类\]设置DbParameter参数值\[使用反射\] /// </summary> /// <typeparam name="T"></typeparam> /// <param name="objEntity"> Object对象\[实体类\](属性名称请与参数名称保持一致) </param> /// <param name="parameter"></param> public static void SetParamsValue < T > (T objEntity, params DbParameter\[\] parameter) where T : class \{ if (parameter == null ) throw new ArgumentNullException( " parameter " ); if (objEntity != null ) \{ System.Reflection.PropertyInfo\[\] properties = objEntity.GetType().GetProperties(); foreach (DbParameter p in parameter) \{ if (p.Direction == ParameterDirection.ReturnValue) // ParameterName == flag + "RETURN\_VALUE") \{ continue ; \} foreach (System.Reflection.PropertyInfo property in properties) \{ if (property.Name.Equals(p.ParameterName.Substring( 1 ),StringComparison.OrdinalIgnoreCase)) \{ try \{ p.Value = property.GetValue(objEntity, null ); \} catch (Exception ex) \{ throw new Exception( " 设置参数 " \+ p.ParameterName \+ " 值时出错,请检查实体属性名 " \+ property.Name \+ " 和该参数类型是否一致。 " \+ ex.Message, ex); \} if (p.Value == null ) \{ throw new ArgumentException( " 实体属性名: " \+ property.Name \+ " 值为 null,请提供该属性值 " , property.Name); \} break ; \} \} \} \} \} 该方法的实体类是指参数查询实体类,不知道各位在多条件查询时如何做的,本人使用查询实体类来作为查询条件传递的。该方法是使用反射方法,根据查询实体属性名称来设置DbParameter的参数值。我想各位都看过微软的SqlHelper类,该类中有方法可以从存储过程中直接创建参数并放入缓存,在DevNet类库中DBStoredParams.cs类中包含了此类方法并且修改成使用Dbparameter抽象类,不再局限于Sql存储过程参数(虽然很多都用的sql存储过程)。 实体的结构就是以上这些,下面描述一下ScriptQuery.cs。 该类基于第一篇DBConnect数据库连接对象,使用参数模式简单的封装了一些sql语句以及提供了简单的操作帮助,代码在此就不贴了,贴些部分使用代码 ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 ScriptQuery \_query = new ScriptQuery( " tb\_user " ); \_query.Select().ALL().From().Where(Tb\_user.Userid\_FieldName, 5 , ScriptQuery.CompareEnum.MoreThan).AddOrderBy() .OrderBy(Tb\_user.Userid\_FieldName, ScriptQuery.SortEnum.DESC); \_query.PageIndex = 1 ; \_query.PageSize = 10 ; List < Tb\_user > users = \_query.GetList < Tb\_user > (); MessageBox.Show( " RecordCount: " \+ \_query.RecordCount.ToString() \+ " PageCount: " \+ \_query.PageCount.ToString()); 该方法获取分页信息列表,默认使用sql2005分页语句(ROW\_NUMBER() OVER (ORDER BY \{0\}),配置文件appSettings节中 <add key="IsSql2000" value="true"/> 将使用top语句查询分页,在使用top语句分页查询时请设置PrimaryKey查询主键的属性值(默认名称为“id”),否则将出错。 再看一下该类如何使用存储过程 ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 // 存储过程 Tb\_user user = \_query.GetSingle<Tb\_user>(Tb\_user.Userid\_FieldName, id, ScriptQuery.CompareEnum.Equal); DbParameter\[\] ps = DBStoredParams.GetSpParameter( " sp\_tb\_user\_insertupdate " ); DBHelper.SetParamsInfo(user, ps); // 这里设置不使用反射,提高效率 user为数据表对应的实体 \_query.Value = " sp\_tb\_user\_insertupdate " ; \_query.SetCmdParameters(ps); \_query.CmdType = CommandType.StoredProcedure; \_query.ExecuteNonQuery(); 下面是查询实体参数的使用: ![ContractedBlock.gif][] ![ExpandedBlockStart.gif][] 代码 //基类抽象方法的实现 public override List < NewBooks > GetPageList(SearchNewBooks condition, Pagination pagination, string sortFieldName, ScriptQuery.SortEnum sortEnum) \{ Script.Select().ALL().From().Where(); if ( ! string .IsNullOrEmpty(condition.F\_Sm)) \{ Script.Like(NewBooks.F\_SM\_FieldName, condition.F\_Sm); \} if ( ! string .IsNullOrEmpty(condition.F\_BBMC)) \{ Script.Like(NewBooks.F\_BBMC\_FieldName, condition.F\_BBMC); \} if (condition.IsShow != 2 ) \{ Script.Where(NewBooks.IsShow\_FieldName, condition.IsShow); \} Script.Between(NewBooks.F\_DJ\_FieldName, condition.MinPrice, condition.MaxPrice); Script.Between(NewBooks.AddDate\_FieldName, condition.StartDate, condition.EndDate); Script.AddOrderBy().OrderBy(sortFieldName, sortEnum); Script.PageIndex = pagination.PageIndex; Script.PageSize = pagination.PageSize; List < NewBooks > lists = Script.GetList < NewBooks > (); pagination.RecordCount = Script.RecordCount; return lists; \} 实体层与ScriptQuery就到此,下一篇描述DevNet的DAL数据层。 转载于:https://www.cnblogs.com/sjfe\_cn/archive/2010/11/08/Entity.html [ContractedBlock.gif]: https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif [ExpandedBlockStart.gif]: /images/20211220/60c9e55cbdf045de863227f55848e99c.png
还没有评论,来说两句吧...