【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方法我们再各自的驱动类中实现即可。
题外
本篇到此结束,所示代码仅供参考未经测试,功能也只是部分,例如事务操作都没有阐述等等。下一篇和大家一起讨论下依赖注入的一些另类实现方法。
-
收起|展开博文列表
-
收起|展开分享改进
-
收起|展开C#|.NET
- [C#|.NET]分布式锁服务
- [C#|.NET]分布式文件系统-FastDFS打造
- [C#|.NET]分布式通信方案-综合测评
- [C#|.NET]分布式缓存算法-对一致性Hash说不
- [C#|.NET]从控制反转(依赖注入)想到事件注入
- [C#|.NET]从细节出发 设计良好的分布式框架(一) 通用接口 aop dto 相关
- [C#|.NET]从细节出发 设计良好的分布式框架(二) 善用泛型 委托
- 抓虫(一) 从简单程序开始 线程安全
- 抓虫(二) 不要轻视web程序中常用的三个"池" 之应用程序池
- 抓虫(三) 不要轻视web程序中常用的三个"池" 之数据库连接池
- 抓虫(四) 不要轻视web程序中常用的三个"池" 之线程池
- 抓虫(五) 浅谈依赖注入与控制反转
- 抓虫(六) XSS跨站脚本实例
-
收起|展开走向DBA
-
收起|展开架构类随笔
-
收起|展开设计模式
- 结合项目实例 打造属于自己的设计模式
- 结合项目实例 回顾传统设计模式(一)策略模式
- 结合项目实例 回顾传统设计模式(二)观察者模式
- 结合项目实例 回顾传统设计模式(三)装饰者模式
- 结合项目实例 回顾传统设计模式(四)工厂模式(简单工厂、普通工厂、抽象工厂)
- 结合项目实例 回顾传统设计模式(五)单例模式
- 结合项目实例 回顾传统设计模式(六)命令模式
- 结合项目实例 回顾传统设计模式(七)适配器模式(附外观模式)
- 结合项目实例 回顾传统设计模式(八)模板方法模式
- 结合项目实例 回顾传统设计模式(九)迭代器模式
- 结合项目实例 回顾传统设计模式(十)状态模式
- 结合项目实例 回顾传统设计模式(十一)代理模式
-
收起|展开Linux+C
-
收起|展开读书笔记
-
收起|展开学习笔记(非全部原创内容,包含msdn、专题讲座等资源)
- 数据库安全 (改自2008腾讯网络安全技术峰会资料)
- 互联网企业应对恶意网址的思考 (改自2008腾讯网络安全技术峰会资料)
- .NET互联网网站架构 (网络优秀资源汇总)
- 重构与设计解析 (改自老员工讲座)
- 动态方法与动态代理(上篇) (改自老员工讲座)
- 动态方法与动态代理(下篇) (改自老员工讲座)
- wcf、webservivce、remoting、wse、Enterprise Service、msmq.... 乱谈 (msdn资源汇总)
- Webservice 安全与应用 (msdn资源汇总)
- Windows、(*)nux回忆录 作为架构师的你 值得拥有 O(∩_∩)O~
- 回头再学Asp.net系列--基础篇(序)
- 回头再学Asp.net系列--基础篇(一)
- 回头再学Asp.net系列--基础篇(二)
- 回头再学Asp.net系列--基础篇(三)
- 回头再学Asp.net系列--基础篇(四)
- 回头再学Asp.net系列--基础篇(五)
- 回头再学Asp.net系列--基础篇(六)
-
收起|展开...
-