[原创]如何写好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的更多相关文章
- [原创]如何写好SqlHelper 之终章
精简的美丽...... 标题有点大.但是,我觉得99%的接近了. 好了,下面我们来说说一个SqlHelper为了适应各种不同的业务需要,它应该具备哪些基本要素. 第一点.可控的事务. 事务是数据库操作 ...
- 自己写的SqlHelper
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using ...
- 青鸟 王云鹏老师写的SqlHelper 泛型方法,反射,支持实体类
1: using System; 2: using System.Collections.Generic; 3: using System.Linq; 4: using System.Text; 5: ...
- 根据传智写的SqlHelper
using System; using System.Configuration; using System.Data; using System.Data.SqlClient; namespace ...
- 自己写的SqlHelper,提示在调用"Fill"前,SelectCommand 属性尚未初始化.错误
namespace 操作数据{ class SqlHelper { public DataSet SqlTODs(string cmdstring) { ...
- [原创]python写的sniffer
import socket s=socket.socket(socket.PF_PACKET,socket.SOCK_RAW,8) while 1: data=s.recv(65535) print ...
- redmine中创建项目与跟踪标签(原创)
今天来说下本公司所用到的项目管理工具redmine,总体来说还是比较好用的.redmine中可以记录项目的整个过程,可创建跟踪标签(里程碑.需求用例.功能.任务.缺陷)来进行对项目的管控.跟踪标签根据 ...
- ADO.NET复习——自己编写SqlHelper类
今天复习了一次ADO.NET基础,整理一下自己的认为的重点: 编写SqlHelper类,方便我们执行数据库语句,这时可以直接调用封装在SqlHelper类的方法.现在大多数公司面试的时候,给你的面试题 ...
- 再学习sqlhelper
在机房收费重构系统的时候,第一次学习sqlhelper.当时感觉比较简单,没有写博客总结,现在又经过了图书馆的学习,感觉还是有必要写一写的. SqlHelper是一个基于.NETFramework的数 ...
随机推荐
- windows获取窗口句柄
1.使用FindWindow函数获取窗口句柄 示例:使用FindWindow函数获取窗口句柄,然后获得窗口大小和标题,并且移动窗口到指定位置. #include <Windows.h> # ...
- [HDU 1695] GCD
GCD Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- LeetCode Maximum Depth of Binary Tree (求树的深度)
题意:给一棵二叉树,求其深度. 思路:递归比较简洁,先求左子树深度,再求右子树深度,比较其结果,返回:max_one+1. /** * Definition for a binary tree nod ...
- 理解$watch ,$apply 和 $digest --- 理解数据绑定过程
原文地址:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 注 这篇博文主要是写给新手的,是给那些刚刚开始 ...
- 也用 Log4Net 之走进Log4Net (四)
转载地址:http://www.cnblogs.com/dragon/archive/2005/03/24/124254.html 我是转的别人的内容,我觉得他写的非常好,所以我把其中三分之二转了过来 ...
- ViewPager 滑动页(一)
需求:滑动展示页,能够使用本地数据,及获取服务器数据进行刷新操作: 效果图: 实现分析: 1.目录结构: 代码实现: 1.fragment_main.xml <RelativeLayout xm ...
- 工作流设计 zt
工作流设计 业务流程管理模块是本平台的重要组成部分,要实现将已经发布的标准中规范化的流程转化为具体计算机中的流程从而实现流程的自动运转,将标准化成果与员工的日常工作紧密结合起来,具有重要意义. 业务流 ...
- 查询Table name, Column name, 拼接执行sql文本, 游标, 存储过程, 临时表
018_Proc_UpdateTranslations.sql: SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO if (exists (select ...
- HttpListener supports SSL only for localhost? install certificate
1.Start-All Programs - 2.execute below lines on that ‘Developer Command Prompt..’ tool makecert -n & ...
- (转载)HTML与XHTML有什么区别
转自:http://zhidao.baidu.com/link?url=8wvu7Jbzr-wjeKdWCwWkIiJNSpO3HHLERkgQu1QzuLOPT0zvzkHn9HbAFEjPdchP ...