善用泛型 委托

【C#|.NET】从细节出发 设计良好的分布式框架(二) 善用泛型 委托

系列文章完成后 源码发布在我的GIT上 https://github.com/dubing/

文章仅代表个人观点  旨在交流 欢迎讨论


背景

  泛型 委托 反射 设计模式这些概念是大家最喜欢讨论 面试官也最喜欢问的,这里不炒冷饭。基于现在.net开发人员相当一部分都是基于业务开发,自己得不到充分的机会去思考去研究(或者说自己比较懒 不愿意去深入)。很多都是基于项目经理或者技术骨干提供的框架基础来开发或者设计业务模型。举个最简单的例子,单例和工厂模式是大家项目里最长用的,但是多少同学能用上面的知识自己整理出规范简洁统一的代码(相信大家都不是不会只是不乐意去做),我也是比较懒的人,这里就和大家一起思考一下。先附上一个不错的单例方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public  static  T GetInstance( object  lockKey, T instance, Func<T> onCreateInstance)
{
     if  (instance == null )
     {
         if  (lockKey == null )
             lockKey = LockKey;
         lock  (lockKey)
         {
             if  (instance == null )
             {
                 try
                 {
                     if  (onCreateInstance == null )
                         instance = new  T();
                     else
                         instance = onCreateInstance();
                 }
                 catch
                 {
                     instance = default (T);
                 }
             }
         }
     }
     return  instance;
}

  


正文

  按照上一篇结尾留下的话题,本篇着重对数据库操作方面也就是常用工厂模式的地方来进行泛型 委托方向的使用。

  一般大型项目中大家都喜欢依赖注入的方式来数据库操作进行设计,也就是站在面向组件的层次。这里不讨论这样设计的优缺点,我们来看下如果不适用这种方式,只使用委托和泛型如何来良好的替代上面的方案。

  首先分析下sql操作有哪些元素。

?
1
2
3
4
5
6
7
8
9
public  class  BaseDriverParam
{
     public  DbCommand baseCommand { get ; set ; }
     public  DbConnection baseConnection { get ; set ; }
     public  DataAdapter baseDataAdapter { get ; set ; }
     public  DataParameter baseDataParameter { get ; set ; }
     public  DbTransaction baseDbTransaction { get ; set ; }
     public  DbDataReader baseDbDataReader { get ; set ; }
}

  这里只是一部分,当然也是最常用的部分。选择抽出这些是因为大部分数据库的驱动都支持这些。举个数据库连接的例子

?
1
2
3
4
5
public  sealed  class  OleDbConnection : DbConnection, ICloneable, IDbConnection, IDisposable
public  sealed  class  MySqlConnection : DbConnection, ICloneable
public  sealed  class  SqlConnection : DbConnection, ICloneable
public  sealed  class  SQLiteConnection : DbConnection, ICloneable
public  sealed  class  OracleConnection : DbConnection, ICloneable

  一些特殊的内容例如 DataParameter在 IDbDataParameter中并不满足需求的场合下我们选择自己填充,同样的还有下面所有的SqlCommandData等,因为不是文章主旨想表达的内容就不啰嗦了。

    明白这些,下面我们就开始一步一步设计,数据库的常用操作有哪些doCreateConnection创建链接,doCreateCommand声明操作命令,doCreateDataAdapter创建数据适配器,doFillCommand执行命令等等...

  这里我们做第一个选择,按照工厂模式,这些内容都是分别在各自的类中实现,那么既然我们已经摈弃了这种方案,那么我们该如何设计比较合理,用泛型么,将不同的驱动参数作为可变元素带入统一的方法中,听起来是不错的,我们先来建立方法结构

?
1
2
3
4
5
6
public  class  TBaseDriver<TCommand, TDbConnection, TDataAdapter, TDataParameter, TDbTransaction> : BaseDriver
        where TCommand : DbCommand, new ()
        where TDbConnection : DbConnection, new ()
        where TDataAdapter : DbDataAdapter, new ()
        where TDbTransaction : DbTransaction, new ()
        where TDataParameter : DataParameter, new ()

  然后我们逐一的实现方法,在这过程中我们会发现并不是所有的方法都符合我们的需求,例如DbDataAdapter中,标准的command要分很多种类型,例如增删查改。然后对于DbDataAdapter没有一个标准的构造函数例如public SqlDataAdapter(SqlCommand selectCommand)这种形式。这样对于不同的类型又要分开操作。既然我们要选择最简洁的方法,自然这样的方式我们就先不考虑了。那么我们把眼光再网上抛一层,以方法直接作为可变元素。

?
1
2
3
4
5
6
7
public  delegate  string  ActionDelegate();
public  delegate  IConnectionEx CreateConnectionExDelegate();
public  delegate  DbCommand CreateCommandDelegate( string  dbClause);
public  delegate  DbConnection CreateConnectionDelegate( string  dbConnection);
public  delegate  DbConnection CreateFrontConnectionDelegate( string  dbConnection);
public  delegate  DbCommand FillCommandDelegate(DbCommand dbCommand, SqlCommandData sqlCD);
public  delegate  DataAdapter CreateDataAdapter(DbCommand dbCommand);

  然而我们并非笼统的讲所有的方法都抽出,这样也就是失去了文章本来想要表达的意思。这里我们是将原来基础的方法分解,抽出可怜的逻辑设计成委托。举2个简单的例子

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public  DbCommand CreateCommand(SqlCommandData sql)
      {
          DbCommand _c = doCreateCommand(sql.SqlClause);
 
          myConnection.Open();
          if  (IsTransaction && myTransaction == null )
          {
              myTransaction = myConnection.BeginTransaction();
          }
 
          if  (IsTransaction)
          {
              if  (myTransaction == null )
              {
                  myTransaction = myConnection.BeginTransaction();
              }
              _c.Transaction = myTransaction;
          }
          _c.Connection = myConnection;
          _c.CommandTimeout = 300;
          _c.CommandType = sql.CommandType;
 
          _c = doFillCommand(_c, sql);
 
          return  _c;
      }
 
      public  DataTable Query(SqlCommandData sql)
      {
          using  (DbCommand _c = this .CreateCommand(sql))
          {
              DataAdapter _s = doCreateDataAdapter(_c);
              DataSet _d = new  DataSet();
              _s.Fill(_d);
              PopuloateCommand(_c, sql);
              if  (!Create)
              {
                  Dispose( true );
              }
              return  _d.Tables[0];
          }
      }

  那么我们在各自的驱动类中实现这里委托的逻辑,例如oracle的驱动中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public  class  OracleDriver : BaseDriver
     {
         public  OracleDriver()
         {
             this .doCreateConnectionEx = () =>
                 {
                     MaoyaDbConnection mc = new  MaoyaDbConnection();
                     mc.ConnectionString = this .ConnectionString;
                     mc.Create = true ;
                     
                     mc.doCreateConnection = (conn) =>
                         {
                             return  new  OracleConnection(conn);
                         };
                     mc.doCreateFrontConnection = (conn) =>
                         {
                             return  this .CreateConnection<OracleConnection>(conn);
                         };
                     mc.doCreateCommand = (comm) =>
                         {
                             return  new  OracleCommand(comm);
                         };
                     mc.doCreateDataAdapter = (sqlcomm) =>
                         {
                             return  new  OracleDataAdapter((OracleCommand)sqlcomm);
                         };
                     mc.doFillCommand = (sqlcomm, sql) =>
                         {
                             foreach  (DataParameter dp in  sql.Parameters)
                             {
                                 OracleParameter p = new  OracleParameter();
                              
                                 p.ParameterName = dp.ParameterName;
                                 p.Size = dp.Size;
                                 p.Direction = dp.Direction;
                                 p.IsNullable = dp.IsNullable;
                                 p.Value = dp.Value == null  ? DBNull.Value : dp.Value;
 
                                 sqlcomm.Parameters.Add(p);
                             }
                             return  sqlcomm;
                         };
                     return  mc;
                 };
 
         }
     }

  或者在mysql的驱动中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public  class  MySqlDriver : BaseDriver
{
     public  MySqlDriver()
     {
         this .doCreateConnectionEx = () =>
             {
                 MaoyaDbConnection mc = new  MaoyaDbConnection();
                 mc.ConnectionString = this .ConnectionString;
                 mc.Create = true ;
                 
                 mc.doCreateConnection = (conn) =>
                     {
                         return  new  MySqlConnection(conn);
                     };
                 mc.doCreateFrontConnection = (conn) =>
                     {
                         return  this .CreateConnection<MySqlConnection>(conn);
                     };
                 mc.doCreateCommand = (comm) =>
                     {
                         return  new  MySqlCommand(comm);
                     };
                 mc.doCreateDataAdapter = (sqlcomm) =>
                     {
                         return  new  MySqlDataAdapter((MySqlCommand)sqlcomm);
                     };
                 mc.doFillCommand = (sqlcomm, sql) =>
                     {
                         foreach  (DataParameter dp in  sql.Parameters)
                         {
                             MySqlParameter p = new  MySqlParameter();
                          
                             p.ParameterName = dp.ParameterName;
                             p.Size = dp.Size;
                             p.Direction = dp.Direction;
                             p.IsNullable = dp.IsNullable;
                             p.Value = dp.Value == null  ? DBNull.Value : dp.Value;
 
                             sqlcomm.Parameters.Add(p);
                         }
                         return  sqlcomm;
                     };
                 return  mc;
             };
 
     }
}

  这么写似乎是ok了,但是我们发现各个驱动中还是有很多可以抽出通用的部分,那么我们重回泛型的概念

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public  class  TBaseDriver<TCommand, TDbConnection, TDataAdapter, TDataParameter, TDbTransaction> : BaseDriver
     where TCommand : DbCommand, new ()
     where TDbConnection : DbConnection, new ()
     where TDataAdapter : DbDataAdapter, new ()
     where TDbTransaction : DbTransaction, new ()
     where TDataParameter : DataParameter, new ()
{
    
     public  TBaseDriver()
     {
         this .doCreateConnectionEx = () =>
         {
             MaoyaDbConnection mc = new  MaoyaDbConnection();
             mc.ConnectionString = this .ConnectionString;
             mc.Create = true ;
 
             mc.doCreateConnection = (conn) =>
             {
                 var baseConn = new  TDbConnection();
                 baseConn.ConnectionString = conn;
                 return  baseConn;
             };
             mc.doCreateFrontConnection = (conn) =>
             {
                 return  this .CreateConnection<TDbConnection>(conn);
             };
             mc.doCreateCommand = (comm) =>
             {
                 var baseComm = new  TCommand();
                 baseComm.CommandText = comm;
                 return  baseComm;
             };
            
             mc.doFillCommand = (sqlcomm, sql) =>
             {
                 foreach  (DataParameter dp in  sql.Parameters)
                 {
                     TDataParameter p = new  TDataParameter();
 
                     p.ParameterName = dp.ParameterName;
                     p.Size = dp.Size;
                     p.Direction = dp.Direction;
                     p.IsNullable = dp.IsNullable;
                     p.Value = dp.Value == null  ? DBNull.Value : dp.Value;
 
                     sqlcomm.Parameters.Add(p);
                 }
                 return  sqlcomm;
             };
             return  mc;
         };
 
     }
 
     
}

  这里我们将可以共通的方法抽出,至于doCreateDataAdapter方法我们再各自的驱动类中实现即可。


题外

  本篇到此结束,所示代码仅供参考未经测试,功能也只是部分,例如事务操作都没有阐述等等。下一篇和大家一起讨论下依赖注入的一些另类实现方法。

 

原创作品允许转载,转载时请务必以超链接形式标明文章原始出处以及作者信息。
作者:熬夜的虫子
点击查看: 全文目录 博文索引
点击查看: 读书笔记(更新至1219 CLR相关)
 
分类:  C#、.net
    
原创作品允许转载,转载时请务必以超链接形式标明文章原始出处以及作者信息。
作者:熬夜的虫子
点击查看: 全文目录 博文索引
点击查看: 读书笔记(更新至1219 CLR相关)

你可能感兴趣的:(泛型)