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;

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


原文地址:https://www.cnblogs.com/ahl5esoft/p/2192936.html