我也来写:数据库访问类DBHelper(转)
一、前言
相信许多人都百度过:“.net 数据库访问类”。然后就出来一大堆SqlHelper。我也用过这些SqlHelper,也自己写过,一堆静态方法,开始使用起来感觉很不错,它们也确实在很多时候可以很好的工作。ADO.NET已经封装很好了,我们很容易就可以实现自己的数据库访问类。
很久前,忘记在哪里看到过了,有一个朋友写了一篇【如何做一个好用的数据库访问类】(有兴趣的朋友仍然可以搜索到),这篇文章确实写得很好,作者很详细的讲解了如何设计一个好的数据库访问类;所谓“好“是指:轻量、易用、通用、高效。
其实代码是很久前就实现了,只是现在才总结记录,希望可以分享一下学习的过程。ok,在开始前先来看几个ADO.NET常见的面试题:
1. ADO.NET 5个核心对象是哪5个?
2. 与ADO.NET 相关对象中,哪些可以用于数据绑定?
3. DataSet与DataReader有什么区别?分别适用在什么情况?
二、需求
这是一个简单的、基于ADO.NET的数据库访问类,它最起码要具备以下特点:
1. 支持多种数据库
搞.net的视乎有一个固定的思维:数据库就是用sql server。额,只是很多用sql server,但不是全部,也有很多用 my sql 等的。我们并不能限制一定用什么数据库。
2. 支持多个数据库
有时候我们的应用程序会用到多个数据库,并且这些数据库还不是部署在同一台服务器上的。
3. 简单
满足常见的操作。
4. 可扩展
可以随时增加新的方法;对于具体的数据源,也可以有特有的操作。
三、主要说明
3.1 使用DbProviderFactory
既然要支持多种数据库,那么我们之前常写的SqlConnection、SqlCommand 就都不能用了,因为它们是针对sql server 数据源的。如果换成 my sql 就是 MySqlConnection, Oracle 就是 OracleConnection 了。
既然有那么多种Connection,很多朋友可能会想到通过设计模式来处理,例如定义一个父类(或接口),然后各种类型的数据库继承它,然后再通过一个工厂,来创建所需要的对象;以后要增加哪种类型的数据库就再增加一个对应的类就可以了。大概是像下面这样:
|
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
|
public abstract class DBHelper{ public abstract void Open(string key){} public abstract int ExecuteNonQuery() { } public abstract object ExecuteScalar() { } public abstract DataSet GetDataSet() { }}public class SqlHelper : DBHelper{ const char _prefix = '@'; //实现抽象方法...}public class MySqlHelper : DBHelper{ const char _prefix = "@"; //实现抽象方法...}public class OracleHelper : DBHelper{ const char _prefix = ":"; //实现抽象方法...}public class DBFactory{ public static DBHelper GetDBHelper() { //根据条件返回DBHelper }} |
这样实现已经比用SqlXXX好很多了,这也是我之前写过的一种方式。但它仍然不够灵活,并且实现起来就会发现很多代码都是类似的,这就与我们上面的简单的需求相违背了。
通过上面的分析,我们知道用工厂模式可以解决我们的问题,但这不用我们自己实现,.net 早就提供这样的工厂:DbProviderFactory。由名称可以指定DbProviderFactory就是数据源提供程序工厂,负责创建具体的数据源提供程序。它根据 ProviderName就可以创建对应数据源的访问对象了。这样我们的实现也由具体变成抽象了,具体的SqlConection变成了抽象的DbConnection。
什么是 ProviderName? 在配置 web.config 的connectionStrings 时,就会有一个 providerNmae 属性,例如sql server就是 ”System.Data.SqlClient“,这个名称空间就是对应的数据源提供程序。
3.2 参数问题
不同数据库参数查询的格式可能不一样,例如 sql server/my sql 支持“@变量” 形式,而 oracle 支持“:变量”的形式。像上面的父类的写法,子类就必须定义自己的参数前缀。但这些用了DbProviderFactory后也不是问题了。
3.3 using 问题
我们都知道using是c#的语法糖,其实编译后就是 try-catch-finaly;uisng写起来比较优雅,而且在有异常的时候会自动调用对象的Disponse方法,避免有些人忘记调用。所以嵌套的 using,编译后就是嵌套的try-catch-finaly,但其实只要我们注意在抛异常的时候释放资源,一个try-catch-finaly即可。
3.4 DbDataReader 问题
实际项目中,我们更多的是使用DbDataReader而非DataSet/DataTable,而 DbDataReader需要自己逐行读取,这在每个调用的地方都这样写是很麻烦的,怎么解决?委托,又是它!
说到委托还有一个小小的建议,有些人喜欢自己去定义委托,但其实.net已经内置了3种委托:Func、Action、Predicate,并且提供了多个重载版本,应该优先考虑使用这些委托,在不满足的情况下,再去自定义。
3.5 在分层架构里的角色
为 DAL 层提供数据访问服务,由 DAL 直接调用;不涉及sql语句拼接、日志记录等。
四、例子
假设要调用一个 P_GetFriends存储过程,接收一个id参数,返回一个好友列表。如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public List<Friend> GetFriends(int id){ try { DBHelper helper = new DBHelper("dbConnectionKey"); DbParameter[] parameters = new DbParameter[] { helper.CreateDbParameter("id",id) }; return helper.ExecuteReader(CommandType.StoredProcedure, "P_GetFriends", parameters, reader => { return new Friend() { ID = reader.GetInt32(reader.GetOrdinal("ID")), Name = reader.GetString(reader.GetOrdinal("Name")) }; }); } catch { throw; }} |
附源代码
|
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
public class DBHelper{ #region 属性 /// <summary> /// 链接字符串 /// </summary> private string conStr; /// <summary> /// DB工厂 /// </summary> private DbProviderFactory provider; #endregion #region 构造函数 /// <summary> /// 构造函数 /// </summary> /// <param name="key">链接字符串键</param> public DBHelper(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } ConnectionStringSettings css = WebConfigurationManager.ConnectionStrings[key]; if (css == null) { throw new InvalidOperationException("未找到指定的链接字符串!"); } this.conStr = css.ConnectionString; this.provider = DbProviderFactories.GetFactory(css.ProviderName); } /// <summary> /// 构造函数 /// </summary> /// <param name="conStr">链接字符串</param> /// <param name="providerStr">数据源提供程序</param> public DBHelper(string conStr, string providerStr) { if (string.IsNullOrEmpty(conStr)) { throw new ArgumentNullException("conStr"); } if (string.IsNullOrEmpty(providerStr)) { throw new ArgumentNullException("providerStr"); } this.provider = DbProviderFactories.GetFactory(providerStr); this.conStr = conStr; } #endregion #region 外部方法 /// <summary> /// 执行命令,返回受影响行数 /// </summary> /// <param name="commandType">命令类型</param> /// <param name="sql">sql语句或存储过程名称</param> /// <param name="parameters">参数数组</param> /// <returns>受影响行数,失败返回-1</returns> public virtual int ExecuteNonQuery(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters) { DbConnection con = CreateConnection(); DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters); try { con.Open(); int row = cmd.ExecuteNonQuery(); return row; } catch { throw; } finally { cmd.Dispose(); con.Close(); } } /// <summary> /// 执行命令,返回第一行第一列对象 /// </summary> /// <param name="commandType">命令类型</param> /// <param name="sql">sql语句或存储过程名称</param> /// <param name="parameters">参数数组</param> /// <returns>执行结果</returns> public virtual object ExecuteScalar(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters) { DbConnection con = CreateConnection(); DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters); try { con.Open(); object obj = cmd.ExecuteScalar(); return obj; } catch { throw; } finally { cmd.Dispose(); con.Close(); } } /// <summary> /// 执行命令返回DataSet /// </summary> /// <param name="commandType">命令类型</param> /// <param name="sql">sql语句或存储过程名称</param> /// <param name="parameters">参数数组</param> /// <returns>DataSet</returns> public virtual DataSet GetDataSet(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters) { DbConnection con = CreateConnection(); DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters); DataSet set = new DataSet(); DbDataAdapter adapter = this.provider.CreateDataAdapter(); try { con.Open(); adapter.SelectCommand = cmd; adapter.Fill(set); return set; } catch { throw; } finally { adapter.Dispose(); cmd.Dispose(); con.Close(); } } /// <summary> /// 执行命令返回DbDataReader /// </summary> /// <param name="commandType">命令类型</param> /// <param name="sql">sql语句或存储过程名称</param> /// <param name="parameters">参数数组</param> /// <param name="action">委托</param> /// <returns>对象列表</returns> public virtual List<T> ExecuteReader<T>(CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters, Func<DbDataReader, T> action) { DbConnection con = CreateConnection(); DbCommand cmd = CreateCommand(con, commandType, sqlOrProcName, parameters); List<T> result = new List<T>(); try { con.Open(); DbDataReader reader = cmd.ExecuteReader(); try { while (reader.Read()) { var item = action(reader); result.Add(item); } return result; } catch { throw; } finally { reader.Dispose(); } } catch { throw; } finally { cmd.Dispose(); con.Close(); } } /// <summary> /// 批量执行sql语句 /// </summary> /// <param name="sqlList">sql语句集合</param> /// <param name="paramList">参数数组集合</param> /// <returns>执行成功或失败</returns> public virtual bool ExecuteSqlBatchByTrans(IEnumerable<string> sqlList, IEnumerable<List<DbParameter>> paramList) { DbConnection con = CreateConnection(); DbCommand cmd = CreateCommand(con, CommandType.Text); try { con.Open(); DbTransaction trans = con.BeginTransaction(); cmd.Transaction = trans; try { int length = sqlList.Count(); IEnumerable<DbParameter> parameters = null; for (int i = 0; i < length; i++) { cmd.CommandText = sqlList.ElementAt<string>(i); cmd.Parameters.Clear(); parameters = paramList.ElementAt<List<DbParameter>>(i); foreach (DbParameter pm in parameters) { cmd.Parameters.Add(pm); } cmd.ExecuteNonQuery(); } trans.Commit(); return true; } catch { trans.Rollback(); throw; } finally { trans.Dispose(); } } catch { throw; } finally { cmd.Dispose(); con.Close(); } } #endregion #region CreateDbParameter public DbParameter CreateDbParameter(string name, object value) { DbParameter parameter = this.provider.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; return parameter; } public DbParameter CreateDbParameter(string name, object value, ParameterDirection direction) { DbParameter parameter = this.provider.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; parameter.Direction = direction; return parameter; } public DbParameter CreateDbParameter(string name, object value, int size) { DbParameter parameter = this.provider.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; parameter.Size = size; return parameter; } public DbParameter CreateDbParameter(string name, object value, int size, DbType type) { DbParameter parameter = this.provider.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; parameter.Size = size; parameter.DbType = type; return parameter; } public DbParameter CreateDbParameter(string name, object value, int size, DbType type, ParameterDirection direction) { DbParameter parameter = this.provider.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; parameter.Size = size; parameter.DbType = type; parameter.Direction = direction; return parameter; } #endregion #region 私有方法 /// <summary> /// 获取链接实例 /// </summary> /// <returns>链接实例</returns> private DbConnection CreateConnection() { DbConnection con = this.provider.CreateConnection(); con.ConnectionString = this.conStr; return con; } /// <summary> /// 获取命令实例 /// </summary> /// <param name="con">链接实例</param> /// <param name="commandType">命令类型</param> /// <param name="sqlOrProcName">sql语句或存储过程名称</param> /// <returns>命令实例</returns> private DbCommand CreateCommand(DbConnection con, CommandType commandType, string sqlOrProcName, IEnumerable<DbParameter> parameters) { DbCommand cmd = InitCommand(con, commandType, parameters); cmd.CommandText = sqlOrProcName; return cmd; } /// <summary> /// 获取命令实例 /// </summary> /// <param name="con">链接实例</param> /// <param name="commandType">命令类型</param> /// <returns>命令实例</returns> private DbCommand CreateCommand(DbConnection con, CommandType commandType) { return InitCommand(con, commandType, null); } /// <summary> /// 初始化命令 /// </summary> /// <param name="commandType">命令类型</param> /// <param name="parameters">参数集合</param> /// <returns></returns> private DbCommand InitCommand(DbConnection con, CommandType commandType, IEnumerable<DbParameter> parameters) { DbCommand cmd = con.CreateCommand(); cmd.CommandType = commandType; if (parameters != null) { foreach (DbParameter pm in parameters) { cmd.Parameters.Add(pm); } } return cmd; } #endregion} |
我也来写:数据库访问类DBHelper(转)的更多相关文章
- 我也来写:数据库访问类DBHelper
一.前言 相信许多人都百度过:“.net 数据库访问类”.然后就出来一大堆SqlHelper.我也用过这些SqlHelper,也自己写过,一堆静态方法,开始使用起来感觉很不错,它们也确实在很多时候可以 ...
- C#.NET数据库访问类DBHelper
这是一个与C# .NET通用的数据库访问类,包含了工厂模式.事务处理等安全机制. 调用方式: DBHelper db = new DBHelper(); DbCommand cmd = db.GetS ...
- 学习实践:使用模式,原则实现一个C++数据库访问类
一.概述 在我参与的多个项目中,大家使用libMySQL操作MySQL数据库,而且是源码即复用,在多个项目中有多套相同或相似的源码,这样的复用方式给开发带来了不变,而且libMySQL的使用比较麻烦, ...
- DataAccess通用数据库访问类,简单易用,功能强悍
以下是我编写的DataAccess通用数据库访问类,简单易用,支持:内联式创建多个参数.支持多事务提交.支持参数复用.支持更换数据库类型,希望能帮到大家,若需支持查出来后转换成实体,可以自行扩展dat ...
- 一个通用数据库访问类(C#,SqlClient)
本文转自:http://www.7139.com/jsxy/cxsj/c/200607/114291.html使用ADO.NET时,每次数据库操作都要设置connection属性.建立connecti ...
- 关于PHP建立数据库访问类的封装以及操作php单例模式连接数据库封装类
建立数据库访问类的封装 <?php class DBDA { public $host = "localhost"; //服务器地址 public $ui ...
- 一个C#的XML数据库访问类
原文地址:http://hankjin.blog.163.com/blog/static/33731937200942915452244/ 程序中不可避免的要用到配置文件或数据,对于数据量比较小的程序 ...
- 通用数据库帮助类DBHelper(含log日志信息实时记录)
项目需要,需要一个通用的数据库操作类,增删改查.事务.存储过程.日志记录都要有,于是在已有的帮助类上做了一些改进,并将log4j的.NET版--log4net嵌入其中记录sql的执行环境和状态. 用起 ...
- Java知多少(107)几个重要的java数据库访问类和接口
编写访问数据库的Java程序还需要几个重要的类和接口. DriverManager类 DriverManager类处理驱动程序的加载和建立新数据库连接.DriverManager是java.sql包中 ...
随机推荐
- Android Studio高级配置
转载:http://www.jianshu.com/p/4243f3b52644 Android Studio 提供了一个非常方便的功能帮助我们导入或者导出设置.因此我们在安装一个新的Androi ...
- js-JavaScript高级程序设计学习笔记16
第20章 JSON JOSN,JavaScript对象表示法,是JS的一个严格的子集,但是它是一种数据格式,虽然与JS具有相同的语法形式,但是不从属于JS. 1.语法 ①可表示简单值--字符串.数值. ...
- Jenkins+CCNET的另类部署图
最近公司的CI系统升级,从CCNET换成Jenkins进行搭建,原因是Jenkins支持所有语言,不再是单一的dotnet语言支持,并且以节点的形式能做分布式自动构建,非常节省配置成功. 而且从MSB ...
- Jenkins实现生产环境部署文件的回滚操作(Windows)
由于dotnet项目的生产环境环境部署工具比较少,所以我使用jenkins作为生产环境的自动化部署工具. 既然有回滚操作,那么就会有部署操作:要实现回滚,先要实现部署的操作,我在jenkins搭建了一 ...
- 整站网页doc下载wget (转)
-x -np -p -m -k -t -X/upload/ http://网址 为了让这个命令行的各选项意义更加明确,它还可以写成: --force-directories --no-parent - ...
- Birthday-24
2013 LEXUS花样滑冰 和母亲在一起,生日快乐!
- CentOS 6.x安装Chromium
在CentOS/RHEL 7出来之前继续使用Chrome怎么办?使用Chrome的开源版本:Chromium. 1.切换到root: su - 或者 sudo -i 2.下载新的软件源定义: cd / ...
- HDU 1811 Rank of Tetris(拓扑排序+并查集)
题目链接: 传送门 Rank of Tetris Time Limit: 1000MS Memory Limit: 32768 K Description 自从Lele开发了Rating系统, ...
- 如何判断ios设备中是否安装了某款应用
URL Schemes关键字研究一下即可 常见得URL Schemes见http://www.cnblogs.com/huangzs/p/4491286.html if ([[UIApplicatio ...
- resultset 对象获取行字段数据时报:java.sql.SQLException: Column 'id' not found.
resultset 对象获取行字段数据时报:java.sql.SQLException: Column 'id' not found. 代码: String sql="SELECT d.co ...