zoukankan      html  css  js  c++  java
  • 步步为营:Asp.net 通用数据容器的缺陷

    在做试客项目中,我们项目使用的数据框架是ADO.Net, 我常常在想是不是有很好的办法能通用业务到数据请求。即使不能做到所有的业务,但对于同一种业务能达到通用也好。

    于是我就想用泛型尝试解决这个问题,用分页来讲解。

     在控制器我这样写

    List<Crm_Achievements_HD> lstAchievements = BLLAchievements.GetAchievementsByWhereHD(item);

    BLL我这样写

    #region 业绩存储过程分页
    /// <summary>
    /// 业绩存储过程分页
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public static List<Crm_Sell_Achievements> GetAchievementsByWhere(PagerEntity item)
    {
    List<Crm_Sell_Achievements> list = DatabaseProvider.GetInstance().GetCommonPager<Crm_Sell_Achievements>(item);
    return list;
    }
    #endregion

    DAL我这样写

    #region 通用存储过程分页
    /// <summary>
    /// 通用存储过程分页
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="item"></param>
    /// <returns></returns>
    public List<T> GetCommonPager<T>(PagerEntity item) where T : class, new()
    {
    //创建返回的集合
    List<T> objlist = new List<T>();
    string strsql = string.Empty;
    //创建属性的集合
    List<PropertyInfo> prlist = new List<PropertyInfo>();
    //获得反射的入口
    Type t = typeof(T);
    //获取所有公共属性
    PropertyInfo[] ps = t.GetProperties();
    strsql = "P_CommonPager_RowNomber";
    DbParameter[] param = {
    SQLHelper.MakeParam("@tblName",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,item.TblName),
    SQLHelper.MakeParam("@strGetFields",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,item.StrGetFields),
    SQLHelper.MakeParam("@strOrder",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,item.StrOrder),
    SQLHelper.MakeParam("@strWhere",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,item.StrWhere),
    SQLHelper.MakeParam("@pageIndex",(DbType)SqlDbType.Int,4, ParameterDirection.Input,item.PageIndex),
    SQLHelper.MakeParam("@pageSize",(DbType)SqlDbType.Int,4, ParameterDirection.Input,item.PageSize),
    SQLHelper.MakeParam("@recordCount",(DbType)SqlDbType.Int,4, ParameterDirection.Output,item.RecordCount),
    SQLHelper.MakeParam("@doCount",(DbType)SqlDbType.Int,4, ParameterDirection.Input,item.DoCount),
    };
    param[6].Direction = ParameterDirection.Output;
    using (IDataReader rs = SQLHelper.ExecuteReader(CommandType.StoredProcedure, strsql, param))
    {
    while (rs.Read())
    {
    //新实例泛型
    T obj = new T();
    //遍历属性数组
    foreach (PropertyInfo p in ps)
    {
    //将对应的属性填充给对象
    p.SetValue(obj, rs[p.Name] ?? 0, null);
    }
    //添加对象到返回集合
    objlist.Add(obj);
    }
    }
    item.RecordCount = TypeConverter.ObjectToInt(param[6].Value);
    return objlist;
    }
    #endregion

    因为DataReader需要关闭后才能取返回值,所以我将反射属性写到了数据层,实现这功能。

    但是这样产生了一个问题BLL不能通用,这是我非常懊恼的问题。
    之后我又修整了代码,可能是我自己纠结的问题因为就只是分页过程有返回值,其他倒不需要
    于是我又写了一遍

    在控制器这样写

    List<View_TryListCompany> LstTryList = Users.DataTableToList<View_TryListCompany>
    (Users.GetCommonPager(tblName, strGetFields, pkName, strOrder, strWhere,
    pageIndex, pageSize, ref recordCount, doCount, EnumCollection.Enum_SpCommon.SPROWNUMBER));

    BLL中

    #region 通用存储过程分页
    /// <summary>
    /// 通用存储过程分页
    /// </summary>
    /// <param name="tblName">表名或者视图名如:'sk_Users'</param>
    /// <param name="strGetFields">需要返回的列如:'uid,username'</param>
    /// <param name="pkName">主键</param>
    /// <param name="strOrder">排序的字段名如:'order by id desc'</param>
    /// <param name="strWhere">查询条件(注意:不要加where)如:'username like ''%222name%''' </param>
    /// <param name="pageIndex">页码如:2</param>
    /// <param name="pageSize">每页记录数如:20</param>
    /// <param name="recordCount">out 记录总数</param>
    /// <param name="doCount">非0则统计,为0则不统计(统计会影响效率)</param>
    /// <param name="SpCommon">枚举:选择通用过程</param>
    /// <returns>DataTable</returns>
    public static DataTable GetCommonPager(string tblName, string strGetFields, string pkName, string strOrder,
    string strWhere, int pageIndex, int pageSize, ref int recordCount,
    int doCount, EnumCollection.Enum_SpCommon SpCommon)
    {
    DataSet ds = DatabaseProvider.GetInstance().
    GetCommonPager(tblName, strGetFields, pkName, strOrder, strWhere, pageIndex, pageSize, ref recordCount, doCount, SpCommon);
    DataTable table = ds.Tables[0];
    return table;
    }
    #endregion

    DAL中

    #region 通用存储过程分页
    /// <summary>
    /// Author:梁雄开
    /// CreatDate:2010.04.23
    /// Description:通用存储过程分页
    /// </summary>
    /// <param name="tblName">表名如:'sk_Users'</param>
    /// <param name="strGetFields">需要返回的列如:'uid,username'</param>
    /// <param name="pkName">主键</param>
    /// <param name="strOrder">排序的字段名如:'order by id desc'</param>
    /// <param name="strWhere">查询条件(注意:不要加where)如:'username like ''%222name%''' </param>
    /// <param name="pageIndex">页码如:2</param>
    /// <param name="pageSize">每页记录数如:20</param>
    /// <param name="recordCount">out 记录总数</param>
    /// <param name="doCount">非0则统计,为0则不统计(统计会影响效率)</param>
    /// <param name="SpCommon">枚举:选择通用过程</param>
    /// <returns></returns>
    public DataSet GetCommonPager(string tblName, string strGetFields, string pkName,
    string strOrder, string strWhere, int pageIndex, int pageSize, ref int recordCount,
    int doCount, EnumCollection.Enum_SpCommon SpCommon)
    {
    DataSet ds = new DataSet();
    string strsql = string.Empty;
    if (SpCommon == EnumCollection.Enum_SpCommon.SPNOTINT)
    {
    strsql = "P_CommonPager_NotIn";
    DbParameter[] param = {
    SqlHelper.MakeParam("@tblName",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,tblName),
    SqlHelper.MakeParam("@strGetFields",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,strGetFields),
    SqlHelper.MakeParam("@pkName",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,pkName),
    SqlHelper.MakeParam("@strOrder",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,strOrder),
    SqlHelper.MakeParam("@strWhere",(DbType)SqlDbType.VarChar,8000, ParameterDirection.Input,strWhere),
    SqlHelper.MakeParam("@pageIndex",(DbType)SqlDbType.Int,4, ParameterDirection.Input,pageIndex),
    SqlHelper.MakeParam("@pageSize",(DbType)SqlDbType.Int,4, ParameterDirection.Input,pageSize),
    SqlHelper.MakeParam("@recordCount",(DbType)SqlDbType.Int,4, ParameterDirection.Output,recordCount),
    SqlHelper.MakeParam("@doCount",(DbType)SqlDbType.Int,4, ParameterDirection.Input,doCount),
    };
    param[7].Direction = ParameterDirection.Output;
    ds = SqlHelper.ExecuteDataset(CommandType.StoredProcedure, strsql, param);
    recordCount = TypeConverter.ObjectToInt(param[7].Value);
    }
    if (SpCommon == EnumCollection.Enum_SpCommon.SPROWNUMBER)
    {
    strsql = "P_CommonPager_RowNomber";
    DbParameter[] param = {
    SqlHelper.MakeParam("@tblName",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,tblName),
    SqlHelper.MakeParam("@strGetFields",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,strGetFields),
    SqlHelper.MakeParam("@strOrder",(DbType)SqlDbType.VarChar,255, ParameterDirection.Input,strOrder),
    SqlHelper.MakeParam("@strWhere",(DbType)SqlDbType.VarChar,8000, ParameterDirection.Input,strWhere),
    SqlHelper.MakeParam("@pageIndex",(DbType)SqlDbType.Int,4, ParameterDirection.Input,pageIndex),
    SqlHelper.MakeParam("@pageSize",(DbType)SqlDbType.Int,4, ParameterDirection.Input,pageSize),
    SqlHelper.MakeParam("@recordCount",(DbType)SqlDbType.Int,4, ParameterDirection.Output,recordCount),
    SqlHelper.MakeParam("@doCount",(DbType)SqlDbType.Int,4, ParameterDirection.Input,doCount),
    };
    param[6].Direction = ParameterDirection.Output;
    ds = SqlHelper.ExecuteDataset(CommandType.StoredProcedure, strsql, param);
    recordCount = TypeConverter.ObjectToInt(param[6].Value);
    }
    return ds;
    }
    #endregion

    这次换成DataSet获取数据,出来后在转换反射出想要的列表。

    #region DataTable转List
    /// <summary>
    /// DataTable转List
    /// </summary>
    /// <typeparam name="T">泛型</typeparam>
    /// <param name="dt">数据表格</param>
    /// <returns></returns>
    public static List<T> DataTableToList<T>(DataTable dt) where T : class,new()
    {
    //创建一个属性的列表
    List<PropertyInfo> prlist = new List<PropertyInfo>();
    //获取T的类型实例 反射的入口
    Type t = typeof(T);
    //获得T 的所有的Public 属性 并找出T属性和DataTable的列名称相同的属性(PropertyInfo) 并加入到属性列表
    Array.ForEach<PropertyInfo>(t.GetProperties(), p => { if (dt.Columns.IndexOf(p.Name) != -1) prlist.Add(p); });
    //创建返回的集合
    List<T> oblist = new List<T>();
    foreach (DataRow row in dt.Rows)
    {
    //创建T的实例
    T obj = new T();
    //找到对应的数据 并赋值
    prlist.ForEach(p => { if (row[p.Name] != DBNull.Value) p.SetValue(obj, row[p.Name], null); });
    //放入到返回的集合中.
    oblist.Add(obj);
    }
    return oblist;
    }
    #endregion

    完成自己想要的List,但是使用DataSet性能不好。转换也花费了一层消耗,还是不够完美。

    让我们再次整理次吧。


    先让我们讨论看看问题:

    强耦合

    DataReader和DataTable不允许你透明地检索数据,而不影响用户界面的代码。这意味这你的应用程序和数据库结构是强耦合的,对数据库结构的任何改变都需要对你的程序有所改动。这应该是在数据访问层解决的问题,而不是在用户界面层。

    很多时候,数据库都是为一个应用程序提供服务,这样数据组织起来很容易被使用。但是,有些程序是建立在已有的数据库之上的,这时候就不能对数据库做任何改动,因为还有其他程序在使用这个数据库。这种情况下,你的代码可能跟数据库耦合性更强,甚至超出你的想象。例如,订单可能存储在一个表中,送货地址存储在另一个表中。数据访问层的代码可能减少这种影响,但是问题依然存在。

    当列的名称发生改变又会发生什么呢,其实在开发的过程中这种事情经常发生。结果是,你必须改变用户界面层的代码来适应这一变化。

    你会这样写

    object shippingAddress = orders.Rows[0]["ShippingAddress"]; 

    你获取列需要自己写列名,也就是说当修改数据结构,你就必须维护这里。

    string shippingAddress = (string)orders.Rows[0]["ShippingAddress"];
    string shippingAddress = orders.Rows[0]["ShippingAddress"].ToString();

    除了这些,你还需要对类型进行显示的转换,进行类型转换在性能和内存使用上都有一定的损失,因为从值类型转换到引用类型要进行装箱操作,反之进行拆箱操作。

    DataReader比DataTable有优势,它提供了不需要显式转换访问字段的类型方法,方法的参数接受列在一行中得索引值。它还提供了一个提供列的名称返回索引的方法。但是输入的字符串错误,也会抛出异常。

    string address = rd.GetString(rd.GetOrdinal("ShippingAddress")); 
    string address = rd.GetString(rd.GetOrdinal("ShipingAdres")); //exception

    性能问题

    DataSet可能是.NET类库中最复杂的结构了。它包括一个或多个DataTable实例,每个DataTable实例又包括一系列DataRow对象,每个DataRow对象又由DataColumn对象构成的。DataTable可以由一个或多个列组成组成一个主键,还可以在某些列上声明外键,列还支持版本控制等等。尽管这些特性很多时候都是没用的,也常常被开发人员忽略,但是DataSet仍然在内部创建包含这些对象的空的集合。这对一个独立的应用程序可能是微不足道的性能损失,但是在一个多用户的环境中,有成千上万条请求,这将是不可接受的。

    相比之下,DataReader可以适用在不同的场景中。DataTable从数据库中读出所有的数据放到内存中,但是很多时候你并不需要这么多的数据在内存中,你只需要一条条的从数据库中读出记录即可。另外一种情况,你经常查询数据而不用更新它,这种情况下,DataReader是最佳选择,因为它使用只读的方式检索数据。尽管DataReader提升了性能,但是它依然有类型转换带来的性能损失。

  • 相关阅读:
    贪吃蛇大作战游戏攻略
    非对称加密算法RSA
    使用java库中的对称加密算法
    HMAC加密的消息摘要码
    md5和SHA校验码
    拼图可解的充要条件
    Base64编码
    java中文文档官方下载
    JAVA中对字符串的常见处理函数汇总
    测试工程师面试题
  • 原文地址:https://www.cnblogs.com/79039535/p/2293705.html
Copyright © 2011-2022 走看看