首页 > 代码库 > [原创]如何写好SqlHelper
[原创]如何写好SqlHelper
所有写数据库应用的都会遇到SqlHelper。每个人实现的也不同,网上现成的例子也很多。但在实际操作中,大部分都不实用。什么样的才是实用的?答:适合应用场景的!
下面来介绍下我写的一个关于Oracle的SqlHelper。没有进行大规模及性能测试。
首先来说下为什么写这个SqlHelper。在以往的桌面程序开发中,我遇到最多的Sql操作,一般不会涉及多个表同时操作,即使有,在使用SqlHelper时用一个Transaction就可以了。
但现在场景换了。在Web里我们将业务操作与具体数据库操作分离了成 Ba 与Da(人为强制性的)。在Ba里对业务操作进行必要的检测,然后调用Da读写数据。Ba可以供其它Ba操作时复用。
在复用时,我们要确保多个以及多级Ba在同一个事务(如果需要)里,同时Ba只能访问自己的Da。这样的操作环境催生了现在我要展示的SqlHelper。然而,当我写完了这个SqlHelper后,发现它可以很好的完成桌面以及Web应用中对Sql的操作。下面是桌面应用的测试用例
public class Ta : Blo { public int InsertAndNext() { var sql = "INSERT INTO TB_SYS_DD(DDID,DICNAME) values(‘{0}‘,‘{1}‘) returning DDID into :result";//注意结尾不需要使用 ; Trans.SqlHelper.CommandText = string.Format(sql, "2001", "测试项"); var para = new OracleParameter("result", OracleType.Number); para.Direction = ParameterDirection.Output; Trans.SqlHelper.AddParameter(para); Trans.SqlHelper.Execute(); return Convert.ToInt32(para.Value); } public void Add() { var sql = "INSERT INTO TB_SYS_DD values(‘{0}‘,‘{1}‘)"; Trans.SqlHelper.CommandText = string.Format(sql, "2001", "测试项"); Trans.SqlHelper.Execute(); } } public class Tb : Blo { public void Add() { var sql = "INSERT INTO TB_SYS_DDDETAIL values(‘{0}‘,‘{1}‘,‘{2}‘)"; Trans.SqlHelper.CommandText = string.Format(sql, "2000", "1", "测试项一"); Trans.SqlHelper.Execute(); } public string GetMaxDicName() { var sql = "select d.dicname from TB_SYS_DD d where d.ddid = (select max(ddid) from TB_SYS_DD)"; Trans.SqlHelper.CommandText = sql; var x = Trans.SqlHelper.ExecuteScalar(); return x.ToString(); } } public class Tc : Blo { public void Add() { var sql = "INSERT INTO TB_SYS_RESETPWD(userId,guid,Time) values(‘{0}‘,‘{0}‘,)"; Trans.SqlHelper.CommandText = string.Format(sql, "2000", "1"); Trans.SqlHelper.Execute(); } public void Update() { var sql = "update TB_SYS_DD set dicname = ‘{0}‘ where ddid = 2000"; Trans.SqlHelper.CommandText = string.Format(sql, "值"); Trans.SqlHelper.Execute(); } }
测试代码
//直接使用sqlhelper的情况 private void button1_Click(object sender, EventArgs e) { using (var helper = new OracleHelper { ConnectionString = txtOracle.Text, CommandText = txtSql.Text }) { Trace.WriteLine(helper.DataSource); var dt = helper.GetDataTable(); if (dt != null && dt.Rows.Count > 0) { foreach (DataRow row in dt.Rows) { lbResult.Items.Add(row.Field<string>("TABLE_NAME") + "\t" + row.Field<string>("COMMENTS")); } } } } //使用业务逻辑创建一个事务。将所有的操作包含在同一事务里 private void button2_Click(object sender, EventArgs e) { var a = new Ta(); //a.Trans.SqlHelper.ConnectionString = txtOracle.Text; a.Trans.Begin(); //下面的两过程效果相同。但建议使用第二种。更容易理解 var b = new Tb {Trans = a.Trans}; //var b = new Tb(); //a.Trans.AddBusiness(b); var c = new Tc(); a.Trans.AddBusiness(c); try { a.Add(); b.Add(); c.Add(); a.Trans.Commit(); } catch (Exception ex) { a.Trans.Rollback(); throw new Exception(); } } //插入之后返回值,如自增Id private void button3_Click(object sender, EventArgs e) { var a = new Ta(); a.Trans.SqlHelper.ConnectionString = txtOracle.Text; a.Trans.Begin(); try { var i = a.InsertAndNext(); MessageBox.Show(i.ToString()); a.Trans.Commit(); } catch (Exception ex) { a.Trans.Rollback(); throw; } } /* 在一个事务里,对N个表进行操作(1)。 这时有其它的连接对相同的表操作。操作是按正常情况执行(2)。 若(2)使用(1)表中的结果,那么如果(1)的事务未提交,则(2)无法访问(1)中最新的值 */ private void button4_Click(object sender, EventArgs e) { var a = new Ta(); a.Trans.SqlHelper.ConnectionString = txtOracle.Text; a.Trans.Begin(); var b = new Tb(); a.Trans.AddBusiness(b); var c = new Tc(); a.Trans.AddBusiness(c); try { a.Add(); Thread.Sleep(1000 * 60 * 5);//等5分钟 b.Add(); c.Add(); a.Trans.Commit(); } catch (Exception ex) { a.Trans.Rollback(); throw; } } private void button5_Click(object sender, EventArgs e) { var b = new Tb(); b.Trans.SqlHelper.ConnectionString = txtOracle.Text; try { var x = b.GetMaxDicName(); MessageBox.Show(x); } catch (Exception ex) { throw; } } private void button6_Click(object sender, EventArgs e) { var c = new Tc(); c.Trans.SqlHelper.ConnectionString = txtOracle.Text; try { c.Update(); } catch (Exception ex) { throw; } } //外层事务,内层独立事务 private void button7_Click(object sender, EventArgs e) { var a = new Ta(); a.Trans.SqlHelper.ConnectionString = txtOracle.Text; a.Trans.Begin(); var b = new Tb(); b.Trans.SqlHelper.ConnectionString = txtOracle.Text; var c = new Tc(); c.Trans.SqlHelper.ConnectionString = txtOracle.Text; try { a.Add(); try { b.Trans.Begin(); b.Add(); b.Trans.Commit(); } catch (Exception) { b.Trans.Rollback(); throw; } try { c.Trans.Begin(); c.Add(); c.Trans.Commit(); } catch (Exception) { c.Trans.Rollback(); throw; } a.Trans.Commit(); } catch (Exception ex) { a.Trans.Rollback(); throw; } } }
看完以上代码。你现在最关心的是如何实现事务共享的,以及如何区分事务的所有者及是否可以进行提交。
秘密其实很简单,能过三个地方来控件。
1、是否共享
public class Blo 为业务对象基类,我给它一个事务控制的属性对象 public Trans Trans { get; set; }
2、Trans 对象为事务控制器。给它设定一个属性 public bool IsInherited { get; set; } 用于标识它的事务是否从其它事务得来的。
3、Blo 的 Trans 的属性读写器
public Trans Trans { get { if (_trans != null) return _trans; _trans = new Trans(this) {IsInherited = false}; return _trans; } set { if (_trans == null) { _trans = new Trans(this); } _trans.SqlHelper = value.SqlHelper; _trans.IsInherited = true; } }
通过这三个地方我们就完全可以控件事务的统一访问了。
说了这么多还是把dll文件上传上来。如果哪个兄弟有心可以对此dll进行性能测试。当然需要.net 4
[原创]如何写好SqlHelper