• .net 开发框架(二) [实体层与ScriptQuery类]


         本篇我描述DevNet中实体层的实现,.net的数据集很强大,原来做cs项目时都用的DataTable,不管从性能,灵活性,可操作性以及写代码的方便性都很实用,唯一感觉不舒服的是在获取某字段值是需要使用DataRow[fieldName]或者DataRow[index]方式,在项目需求变化数据字段有变化时,需要修改大量的字段名称,在编译时并不会报错,项目很容易出错。再到后来做bs项目时使用了实体模式,虽然在灵活性及List数据集的计算等方面不如DataTable方便,但感觉在维护及开发调试中方便了许多。

      实体如果都要手写的话就受不了了,网上很多生成实体的工具,我也根据DevNet类库弄了个实体生成器,有需要的朋友可以到第一篇下载。

          在转换成实体时原先都是用反射方法,网上绝大部分都使用该方法,虽然用起来没啥大问题,但我感觉对性能多少有点影响。下面的反射转换实体的代码,DevNet类库中仍保留着。

      

    代码
    /// <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项生成的实体如下:

    代码
    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类(下面会讲一点)操作,完全不需要把字段名称写在项目中,有了这个实体,那么实体转换就不再需要用反射了。

    代码
    /// <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方法设置实体属性值,不再依赖反射。另外在该类中有这样一个方法

    代码
    /// <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语句以及提供了简单的操作帮助,代码在此就不贴了,贴些部分使用代码

    代码
    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”),否则将出错。

    再看一下该类如何使用存储过程

    代码
    //存储过程
             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();

    下面是查询实体参数的使用: 

    代码
    //基类抽象方法的实现
    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数据层。

  • 相关阅读:
    浅谈纯文本&&富文本&&Markdown区别
    浅谈CSS图片base64编码技术
    postman测试请求API:方式post、上传文件file
    Antd版本V3-->V4迁移问题:初始化调整
    浅谈switch语句的技巧
    react路由传参
    react dangerouslySetInnerHTML用法
    SPA单页应用的2种类型分页技术(React、Vue等组件化开发)
    (3)re模块(正则表达式模块)
    (2)hashlib模块(加密算法模块)
  • 原文地址:https://www.cnblogs.com/sjfe_cn/p/Entity.html
Copyright © 2020-2023  润新知