[原创]如何写好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

时间: 2024-08-02 16:40:39

[原创]如何写好SqlHelper的相关文章

[原创]如何写好SqlHelper 之终章

精简的美丽...... 标题有点大.但是,我觉得99%的接近了. 好了,下面我们来说说一个SqlHelper为了适应各种不同的业务需要,它应该具备哪些基本要素. 第一点.可控的事务. 事务是数据库操作的关键部分,在对数据库进行插入.修改.删除时都会用到:事务是基于数据库连接的.一个事务必须要运行在一个连接上,但一个连接上可以有多个事务. 可控的事务就是你在进行数据的增删改时需要一个事务来进行辅助,当操作出错时将修改的数据还原.类似下面的过程 transaction.begin(); ...do

自学.net(1)手写一个sqlhelper类

自学.net已经快一个月了,看的是传智播客的视频,今天正好申请了博客,就先把手写的sqlhelper类上传上去吧 别忘了在解决方案管理器的“引用”添加System.Configuration 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Configuration; 6 using System.Data.SqlClient;

自己写的SqlHelper,提示在调用&quot;Fill&quot;前,SelectCommand 属性尚未初始化.错误

namespace 操作数据{    class SqlHelper    {        public DataSet SqlTODs(string cmdstring)        {            DataSet ds = new DataSet();            try            {                string sqlstring = System.Configuration.ConfigurationManager.Connection

根据传智写的SqlHelper

1 using System; 2 using System.Configuration; 3 using System.Data; 4 using System.Data.SqlClient; 5 6 namespace ADO连接数据库 7 { 8 public static class SqlHelper 9 { 10 //首先是连接字符串 11 private static readonly string Strcon = ConfigurationManager.ConnectionS

原创文章代写赚钱吗?

相信很多朋友都会有这样的疑问,原创文章代写赚钱不,其实原创文章代写服务行业目前还是挺赚钱的,赚钱到什么程度呢?一篇文章作者几十分钟就可以写完了,但是一篇原创文章的收入可能会60以上甚至上百,如果遇到好一点的客户这个价值可能还不止,会更高,所以说大家可想而知原创文章代写服务目前是有多赚钱有多暴利了,一篇高质量的原创文章可能会给客户带来源源不断的软文收益,单从这个价格来考虑的话原创文章代写的价格也不为过,最起码那是代写作者用心一个字一个字的写出来的,又不是复制的,所以说原创文章代写行业目前还是比较吃

seo原创文章代写×××写手?

从事网络代写行业的都知道,写文章是最枯燥的工作和生活,每天重复一日的工作只有不断的敲键盘和写作,所以说写文章对于写手意味着枯燥和烦躁,因为工作没有新鲜度,但是没办法写手只能通过做这些工作来实现赚钱的目的. 一般我们做站长的或者是做新媒体运营的每天都需要定时的给网站或者平台填充新鲜的原创文章资源,因为现在不像过去的时代,随便采集几篇文章就发到网站或平台上面,会被搜索引擎惩罚,所以原创才是站长和自媒体以后的出路,但是有些人对于原创资源的需求量比较大,可能每天需要上百篇,上千篇的原创资源,这么多的资源

机房重构 之 SqlHelper

机房收费开始一段时间了,刚开始也是敲了一段时间,发现D层访问数据库出现了大量的重复代码,每个D层类都要 单独访问数据库.发现问题,咱们就解决问题,查阅前人的博客,发现了一个SqlHelper类,运用一下,果然好用,省 去了大量时间去写重复的代码. 小面对SQL中的一些类方法进行简单的介绍. 1.SQLHelper.ExecuteNonQuery    作用:用于执行语句 2. SQLHelper.ExecuteScalar       作用:用于获取单字段值语句 3. SQLHelper.Exe

【ASP.NET-中级】SQLHelper数据访问公共类

ASP.NET开发中的三层开发思想指的是UI层(界面显示层),BLL层(业务逻辑层),DAL层(数据访问层)三层,三层之间通过函数的调用来达到降低耦合,易于系统维护的目的,SQLHelper助手类的主要作用在于接收并执行来自各个数据表累传来的sql语句或存储过程.一般的SQLHelper类中主要包括以下几个函数功能: 1.执行不带参数的增删改方法 2.执行带参数的增删改方法. 3.执行不带参数的查询方法. 4.执行带参数的查询方法. 作为一个程序员SQLHelper类编写的好坏不仅影响着系统的可

根据用户需求快速写原创文章的技巧

每天有时间就在QQ群里看看大家聊什么问题,发现这几天在讨论如何写原创文章?原创文章好还是伪原创好?伪原创怎么写?等等这些问题.今天我总结了一下,根据我的经验给大家说一下如何根据用户需求快速写原创文章. 案例网站:太原雅辉装修公司 太原雅辉装修网的文章全部是高度伪原创,加上可以匹配的图片,写一篇文章需要半个小时左右. 文章实例: <a href="http://www.0351zhuangxiu.com/112.html">40平米小户型装修效果图</a> 为什么