zoukankan      html  css  js  c++  java
  • C#中,数据处理层之数据库基类

      做开发也有将近2年的时间了,但是经验其实也不多。经历过几个小公司,数据处理层使用过基本的SQL,也有NHibernate框架。框架确实好用,省去了不少代码量,但是业务复杂的情况下,也就难以依托了,仍然需要自己手动书写SQL。业余开发项目的时候数据层使用了框架,后来改回了基础的SQL底层,原因嘛,也想说自己在重复造轮子的情况下,能有其他的收获。

      原本使用泛型版本的底层基类,无法使用多态,因此也就无法使用工厂模式创建对应的SQL接口了。

      原数据库基类:

     1 /// <summary>
    2 /// 数据库连接基类
    3 /// </summary>
    4 /// <typeparam name="DbConn">数据库连接池</typeparam>
    5 /// <typeparam name="DbCmd">数据库Command</typeparam>
    6 /// <typeparam name="DbParam">数据参数</typeparam>
    7 /// <typeparam name="DbReader">数据库读取流</typeparam>
    8 /// <typeparam name="DbAdapter">数据库Adapter</typeparam>
    9 /// <typeparam name="DbTran">事务</typeparam>
    10 public class BaseDb<DbConn, DbCmd, DbParam, DbReader, DbAdapter, DbTrans>
    11 where DbConn : DbConnection, new()
    12 where DbCmd : DbCommand, new()
    13 where DbParam : DbParameter, new()
    14 where DbReader : DbDataReader
    15 where DbAdapter : DbDataAdapter, new()
    16 where DbTrans : DbTransaction
    17 {
    18 // code
    19 }

      数据库派生类:

     1 /// <summary>
    2 /// Ms-SQL数据库连接
    3 /// </summary>
    4 public class MsSqlDb : BaseDb<SqlConnection, SqlCommand, SqlParameter, SqlDataReader, SqlDataAdapter, SqlTransaction>
    5 {
    6 //Code
    7 }
    8
    9 /// <summary>
    10 /// Access数据库
    11 /// </summary>
    12 public class OleDb : BaseDb<OleDbConnection, OleDbCommand, OleDbParameter, OleDbDataReader, OleDbDataAdapter, OleDbTransaction>
    13 {
    14 //Code
    15 }

      这种情况下,除非使用4.0的新特性“协变”,否则无法通过基类来接收对应的派生类了。

      其实数据层的基类无非就是DbConnection、DbCommand、DbParameter等,差异的地方无非就是在实例化DbConnection、DbCommand、DbDataAdapter这些,因此我们只要让派生类去重写返回DbConnection、DbCommand、DbDataAdapter对应的派生类就可以了。

      抽象方法如下:

     1 /// <summary>
    2 /// 新数据库连接池
    3 /// </summary>
    4 /// <param name="connectionStr">数据库连接字符串</param>
    5 /// <returns></returns>
    6 protected abstract DbConnection NewConn(string connectionStr);
    7
    8 /// <summary>
    9 /// 新sql解析方式
    10 /// </summary>
    11 /// <returns></returns>
    12 protected abstract DbCommand NewCommand();
    13
    14 /// <summary>
    15 /// 新DataAdapter
    16 /// </summary>
    17 /// <returns></returns>
    18 protected abstract DbDataAdapter NewAdapter();

      数据库基类就做好了,这时候我们使用了几次之后,就会发现在获取DbDataReader的时候,要自己重复的写如下代码:

    1 AbstractBaseDb db = //相应的派生类;
    2 using(DbDataReader reader = db.ExecuteReader(参数)){
    3 while(reader.Read()){
    4 //获取数据代码
    5 }
    6 }

      于是乎,就有了想把DbDataReader直接转换成对应实体类的想法,既然有了想法我们就动手开始写吧。

      要将DbDataReader内的数据库输出流转化为对应的实体类泛型,其实就是将每一行值进行转换而已,这时候我们可以用泛型去定义,因为实体类需要实例化,因此我们需要这个T必须是可以实例化的。

      代码如下:

     1 IList<T> classList = new List<T>();
    2 PropertyInfo[] properties = typeof(T).GetProperties();
    3 Predicate<PropertyInfo> match;
    4 while (this.dataReader.Read())
    5 {
    6 T tObj = new T();
    7 for (int i = 0; i < this.dataReader.FieldCount; i++)
    8 {
    9 string filedName = this.dataReader.GetName(i);
    10 match = p => p.Name == filedName;
    11 if (!this.dataReader.IsDBNull(i) && Array.Exists(properties, match))
    12 {
    13 PropertyInfo property = Array.Find(properties, match);
    14 property.SetValue(tObj, this.dataReader.GetValue(i), null);
    15 }
    16 }
    17 classList.Add(tObj);
    18 }

      做到这里的时候,可能有些人已经想到了一个问题,那就是如果实体类属性名跟数据库的列名不相同(代码生成的时候对数据库列名进行了处理)怎么办呢,那这个时候,我们就需要提供一个可以将数据库列名进行转化的方法,该委托出场了。
      修改后代码如下:

     1 /// <summary>
    2 /// 转化为类泛型
    3 /// </summary>
    4 /// <typeparam name="T">引用类型</typeparam>
    5 /// <param name="format">格式化列名</param>
    6 /// <returns></returns>
    7 IList<T> ToList<T>(Func<string, string> format) where T : new()
    8 {
    9 IList<T> classList = new List<T>();
    10 PropertyInfo[] properties = typeof(T).GetProperties();
    11 Predicate<PropertyInfo> match;
    12 while (this.dataReader.Read())
    13 {
    14 T tObj = new T();
    15 for (int i = 0; i < this.dataReader.FieldCount; i++)
    16 {
    17 string filedName = this.dataReader.GetName(i);
    18 if (format != null)
    19 {
    20 filedName = format(filedName);
    21 }
    22 match = p => p.Name == filedName;
    23 if (!this.dataReader.IsDBNull(i) && Array.Exists(properties, match))
    24 {
    25 PropertyInfo property = Array.Find(properties, match);
    26 property.SetValue(tObj, this.dataReader.GetValue(i), null);
    27 }
    28 }
    29 classList.Add(tObj);
    30 }
    31 return classList;
    32 }

      以上加入了参数Func<string,string>,并且在获取列名的时候,判断如果格式化方法存在,则对列名进行处理,到这里将DbDataReader转化为实体类泛型就完成了,然而完成到这里我们就会有了新的想法了,既然实体类已经实现了,那么如果T是基础类型怎么办呢,于是乎我们要重载这个方法,让他也可以适用于基础类型泛型。

      进一步改进以后,代码如下:

     1 /// <summary>
    2 /// 将DataReader转化为泛型
    3 /// </summary>
    4 /// <typeparam name="T">需要转化的实体类类型</typeparam>
    5 /// <param name="format">格式化列名</param>
    6 /// <returns></returns>
    7 public IList<T> ToList<T>(Func<string, string> format) where T : new()
    8 {
    9 Type tType = typeof(T);
    10 IList<T> list;
    11 if (tType.IsValueType)
    12 {
    13 list = ToValueList<T>();
    14 }
    15 else
    16 {
    17 list = ToClassList<T>(format);
    18 }
    19 return list;
    20 }
    21
    22 /// <summary>
    23 /// 转化为值类型泛型
    24 /// </summary>
    25 /// <typeparam name="T">值类型类型</typeparam>
    26 /// <returns></returns>
    27 IList<T> ToValueList<T>()
    28 {
    29 IList<T> valueList = new List<T>();
    30 int dataIndex = 0;
    31 while (this.dataReader.Read())
    32 {
    33 valueList.Add(this.GetDataReaderValue<T>(dataIndex));
    34 }
    35 return valueList;
    36 }
    37
    38 /// <summary>
    39 /// 转化为类泛型
    40 /// </summary>
    41 /// <typeparam name="T">引用类型</typeparam>
    42 /// <param name="format">格式化列名</param>
    43 /// <returns></returns>
    44 IList<T> ToClassList<T>(Func<string, string> format) where T : new()
    45 {
    46 IList<T> classList = new List<T>();
    47 PropertyInfo[] properties = typeof(T).GetProperties();
    48 Predicate<PropertyInfo> match;
    49 while (this.dataReader.Read())
    50 {
    51 T tObj = new T();
    52 for (int i = 0; i < this.dataReader.FieldCount; i++)
    53 {
    54 string filedName = this.dataReader.GetName(i);
    55 if (format != null)
    56 {
    57 filedName = format(filedName);
    58 }
    59 match = p => p.Name == filedName;
    60 if (!this.dataReader.IsDBNull(i) && Array.Exists(properties, match))
    61 {
    62 PropertyInfo property = Array.Find(properties, match);
    63 property.SetValue(tObj, this.dataReader.GetValue(i), null);
    64 }
    65 }
    66 classList.Add(tObj);
    67 }
    68 return classList;
    69 }

      这样转化为泛型的方法就比较ok了。于是又使用了一段时间,发觉业务逻辑复杂的时候,返回的数据可不止一张表,也可能是多张表的数据一起返回的,这个时候除了可以使用DataTable以外,没有其他的方法可以用了。想到了在NHibernate获取数据的时候,可以返回IList类型,接下来我们也可依葫芦画瓢也做一个这样的转换。

      转换的方式其实跟转换为类泛型是差不多的,在循环DbDataReader.FieldCount的时候,我们只要将循环的数据放入ArrayList内就可以了。

      具体代码如下:

     1 IList subList = new ArrayList();
    2 while (this.dataReader.Read())
    3 {
    4 IList list = new ArrayList();
    5 for (int i = 0; i < this.dataReader.FieldCount; i++)
    6 {
    7 object value = this.dataReader[i];
    8 list.Add(this.dataReader.IsDBNull(i) ? null : value);
    9 }
    10 subList.Add(list);
    11 }
    12 return subList;

      到了这里,个人总结的方法也就差不多了,如果还有新的想法,请留言,让我们一起将其扩展得更加完全,呵呵。


  • 相关阅读:
    JQuery常用函数方法全集
    从零开始学习jQuery (三) 管理jQuery包装集
    Jquery选择器总结
    oracle常用函数总结
    普通Java类获取Spring的Bean的方法
    AtCoder Beginner Contest 173 A
    HDU 5974 A Simple Math Problem
    Codeforces Round #655 (Div. 2) B. Omkar and Last Class of Math
    Codeforces Round #655 (Div. 2) A. Omkar and Completion
    SWPU信息学院团体程序设计竞赛题解
  • 原文地址:https://www.cnblogs.com/ahl5esoft/p/2192936.html
Copyright © 2011-2022 走看看