我也来写:数据库访问类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

}

时间: 2024-10-24 22:41:27

我也来写:数据库访问类DBHelper(转)的相关文章

我也来写:数据库访问类DBHelper

一.前言 相信许多人都百度过:“.net 数据库访问类”.然后就出来一大堆SqlHelper.我也用过这些SqlHelper,也自己写过,一堆静态方法,开始使用起来感觉很不错,它们也确实在很多时候可以很好的工作.ADO.NET已经封装很好了,我们很容易就可以实现自己的数据库访问类. 很久前,忘记在哪里看到过了,有一个朋友写了一篇[如何做一个好用的数据库访问类](有兴趣的朋友仍然可以搜索到),这篇文章确实写得很好,作者很详细的讲解了如何设计一个好的数据库访问类:所谓“好“是指:轻量.易用.通用.高

ADO数据库访问类查询、属性扩展

1 数据库访问类查询 (1)查询一条 有参数:查询这条信息的主键: 有返回值:返回值是一个实体类: dr.read();执行一遍,读取这行信息并放进users类型中. ///返回users实体类类型 public users chayi(string name) { users u = null; com.CommandText = "select *from users where [email protected]"; com.Parameters.Clear(); com.Par

学习实践:使用模式,原则实现一个C++数据库访问类

一.概述 在我参与的多个项目中,大家使用libMySQL操作MySQL数据库,而且是源码即复用,在多个项目中有多套相同或相似的源码,这样的复用方式给开发带来了不变,而且libMySQL的使用比较麻烦,要应对很多的细节,很容易出错. 我要写一个动态链接库,将对libMySQL的操作封装起来,以二进制复用代替源码级复用:要提供线程安全的接口,用户无需关系是否加锁这样细节性的问题,减少出错及死锁的机会,当然也要允许用户自己选择是否线程安全的访问数据库:要简化访问数据库的流程,接口越简单越好. 我从20

我的C++数据库访问类

一.概述 在我参与的多个项目中,大家使用libMySQL操作MySQL数据库,而且是源码即复用,在多个项目中有多套相同或相似的源码,这样的复用方式给开发带来了不变,而且libMySQL的使用比较麻烦,要应对很多的细节,很容易出错. 我要写一个动态链接库,将对libMySQL的操作封装起来,以二进制复用代替源码级复用:要提供线程安全的接口,用户无需关系是否加锁这样细节性的问题,减少出错及死锁的机会,当然也要允许用户自己选择是否线程安全的访问数据库:要简化访问数据库的流程,接口越简单越好. 我从20

抽离CodeIgniter的数据库访问类 可以独立使用

好吧,因为组织需要,最近又开始转战php了,业务逻辑都还好说,主要是老大要求在数据访问层上加上登录态验证.其实这种要求也是合理的,互联网服务要求上层保护下层,但下层不能完全相信上层.但是问题也就来了,有如下两种方案:1.写一个mysql proxy server,用来将调用方发来的请求拼装,然后返回给调用侧.这样做的主要难度在于:a)SQL语句的拼装及序列化b)数据集序列化,虽然有不少这方面的产品,但是终究还是太过复杂,而且没有时间折腾果断放弃.2.封装一层mysql的api,调用方直接在本地调

一个C#的XML数据库访问类

原文地址:http://hankjin.blog.163.com/blog/static/33731937200942915452244/ 程序中不可避免的要用到配置文件或数据,对于数据量比较小的程序,部署数据库花费的时间就显得浪费了,因此用XML来存储不妨为一个很好的办法,而且结合C#的DataSet,我们可以很轻易的封装出一个代码简单而功能强大的数据访问类XMLConfigconfig.xml<root>  <table1>    <rowName1>hello&l

Sqlite 数据库访问类

使用Sqlite 作为软件数据库: 实现了数据库访问类 C#  使用System.Data.Sqlite 下载地址: http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki网站中下载Precompiled Statically-Linked Binaries for 32-bit Windows (.NET Framework 4.5)或者Precompiled Statically-Linked Binarie

Java知多少(111)几个重要的java数据库访问类和接口

修改数据表记录也有3种方案. 一.使用Statement对象 实现修改数据表记录的SQL语句的语法是:    update表名 set 字段名1 = 字段值1,字段名2 = 字段值2,……where特定条件例如: update ksInfo set 姓名 = ‘张小卫’where 姓名 = ‘张大卫’ 先创建一个SQL语句,然砶调用Statement对象的executeUpdate()方法.例如, 1 sql = “update ksInfo set 姓名 = ‘”+txtName.getTex

DataAccess通用数据库访问类,简单易用,功能强悍

以下是我编写的DataAccess通用数据库访问类,简单易用,支持:内联式创建多个参数.支持多事务提交.支持参数复用.支持更换数据库类型,希望能帮到大家,若需支持查出来后转换成实体,可以自行扩展datarow转实体类,也可以搭配dapper.net实现更强大的功能. /// <summary> /// 通用数据库访问类,支持多种数据库,无直接依赖某个数据库组件 /// 作者:左文俊 /// 日期:2016-6-3 /// </summary> public class DataAc