不该活着的SqlHelper跟DBHelper

不该活着的SqlHelper和DBHelper

前言:

还记得刚学ADO.NET的情景么?

还记得当年是怎么从ADO.NET被忽悠到用SqlHelper的么?

话说从入门到走上工作岗位那些年,我们就一直被纯纯地教导或引导,ADO.NET太原始,得封装成SqlHelper或DBHelper......

后来,这种思维一直深深就存在脑海里,并不知不觉中进入了潜意识,形成一种习惯。

在写框架的前几年,我也一直延续着这种思维,早期CYQ.Data的源码里,也有Sqlhelper,我也分享过Sqlhelper类的源码......

后来框架写久了,开始对框架的命名有讲究了,就默默低调的把Sqlhelper给改名了...

上个月的某一天,我给以前的同事传授知识时,不自觉的提到这个Helper悖论问题。

今天,无意中看到了这样的一篇文章,于是觉得可以分享一下自己的观点了:

文章里只有一个帮助类的代码,这里只截一小段:

不该活着的SqlHelper跟DBHelper

 

这些年框架写多了,对面向对象相关的很多定义和使用,在潜意识里已经自有一套模式,以下分享两个小点:

1:定义Static变量需要考量的两个因素:内存和并发:

1:定义static变量:意味着该对象从始致终,都存在内存中,因此,你需要思考对象可预计或不可预计的大小,是否全局,若否,需要在何处需要将对象置Null?以便垃圾回收!

2:定义static变量:意思着在(Web)多线程环境下必然需要思考:是否有线程访问冲突?问题需要解决?需要Lock吗?需要双重判断?

若写代码时没有这两种考量,容易导致static乱用问题。

因此,上面的代码对Connection对象定义为static,明显错误有二:

1:资源只能用一个。

2:多线程下挂掉或抛异常是必然的,因为共用一个对象(场景如:A操作完Close,B操作到一半发现被Close了,好囧......)。

2:数据库操作类不应该为设计为static:

在现实的项目中,数据库的并发和事务是一件很自然就存在的事情。

因此:

1:并发的存在:意味着数据库操作类(ADO.NET)对象不能设置为static。

2:事务的存在:意味着数据库操作类不能将方法定义为static。

因此,数据库操作类合适的方式,应该设计成实例方式。

这里再补充一下,为什么不应该设计为static:

1:通过在static里方法产生实例,可以避免线程问题,但对象不能复用,事务没法用。

2:把对象提升为参数,外部实例后传入:能复用对事和事务,但根据业务场景需要不断增加重载方法,修改方法以适用,所以这种设计也不合理。

比如你需要增加参数来达到复用:执行的时候是否关闭链接、事务是否提交、参数是否清除、DataReader返回的参数重载等N种场景。

如果你说:你的业务场景就是不需要事务,场景相当简单,那我想,也不存在设计的问题了,爱咋咋的了......

3:关于XXXHepler或XXXUtility的思维定义:

我们可以用Reflector在微软的内库里搜Helper或Utitliy结尾定义的类,可以随便挑着看:

不该活着的SqlHelper跟DBHelper

不该活着的SqlHelper跟DBHelper

结论都一个样:

1:这个类应该是个帮助类或定义为static类。

2:内部应该(或大部分)是静态方法。

悖论出来了:

我在园子里扫了一下,发现大部分的SqlHelper类或DbHelper在经过项目的实战后,都知道该转成实例方式提供。

可是,既然都转成了实例,为啥还叫SqlHelper或DbHelper???

为啥?为啥?为啥?独独就对SqlHelper这个特殊呢?(那是我们从小就被教坏了。。。)

 

总结:因果论:

因为:数据库操作设计不应该为Static,同时Helper后缀的不该设计为实例类。

所以:在数据库操作类设计里,SqlHelper和DBHelper不该存活。

 

于是:如果你有幸运看到这篇文章,在教导新人的时候......切记,切记!记得把他教坏,哈!!!

12楼春色园
楼主最近很闲
Re: 路过秋天
@春色园,引用楼主最近很闲,很忙,只是没去赚钱,用情怀在和你们分享知识。。。
11楼双鱼座
一派胡言!,1.Helper类未必必须是static,事实上大部分都不是static的,2.static类也没有什么问题,只是尽量设计为无状态的,也就是不要给类设计任何静态field,除非确定不会有冲突。,例如DBHelper很可能是这样使用的:,public static IConnection CreateConnection(string connectionString)...,public static T ExecScalarlt;Tgt;(IConnection connection, string statement, params IParameters[] parameters)...,不会有任何问题,不会进入单例陷阱。
Re: 路过秋天
@双鱼座,引用一派胡言!,1.Helper类未必必须是static,事实上大部分都不是static的,2.static类也没有什么问题,只是尽量设计为无状态的,也就是不要给类设计任何静态field,除非确定不会有冲突。,例如DBHelper很可能是这样使用的:,public static IConnection CreateConnection(string connectionString)...,public static T ExecScalarlt;Tgt;(IConnection connection, string statement, params IParameters[] parame...,一派胡言这四个字去掉,然后留下你的意见就可以了,带这么明显的偏见讨论问题并不是什么好事。,1:Helper类可以不定义为static(如果你是针对类名的定义,没啥问题,定义为abstract也是场景问题,但内部必然是提供静态方法为主),2:你提供的场景和原版微软提供的sqlhelper一致,我避开这个,是因为这种这种设计方式不科学:你的事务呢?你怎么决定一个事务要关联的业务多少?你怎么决定一个链接的打开与关联,你怎么决定一个Reader执行后的状态与链接的关联?通过增加N个参数来处理?明显设计就会陷入一种不断增加重载的陷阱。
10楼朝向远方
其实这个应该改装,利用单例模式,数据连接利用数据库连接池,设置连接的最大和最小值,如果写成静态方法如果多线程肯定是会出现线程安全问题,而且利用事务的话无法明确是哪个连接创建事务问题。所以现在看来这个方法确实不可取
9楼fangjun
微软当初提供的sqlhelper类是抽象类,里面的方法大多是静态方法,在静态方法里面实例化sqlconnection,sqlcommand,这个也有问题吗?
Re: 路过秋天
@fangjun,引用微软当初提供的sqlhelper类是抽象类,里面的方法大多是静态方法,在静态方法里面实例化sqlconnection,sqlcommand,这个也有问题吗?,如果把一个helper类定义为抽象类,自身提供静态方法,表面上是避开了本文的问题。,但关键是sqlhelper自身提供的静态方法不合理,因为提供的并不是对各种不同的数据库抽象出的共性方法,而是提供只针对mssql自身的实现,所以,所以定义为抽象,和自身提供的方法就是矛盾的。
Re: 路过秋天
@fangjun,另外我网上搜了一下,原版的并没有定义为抽象类。
8楼倚天照海- -
假期归来 前来膜拜大神
Re: 路过秋天
@倚天照海- -,引用假期归来 前来膜拜大神,什么假期放这么久???
7楼新一年
楼主第一个的代码, 既然是写成static成员了, 作者明显就是不打算支持多线程了.这世界上又不是所有项目都是Web项目,,另外C#有种叫做ThreadStatic的static成员用来支持多线程的.
Re: 路过秋天
@新一年,引用楼主第一个的代码, 既然是写成static成员了, 作者明显就是不打算支持多线程了.这世界上又不是所有项目都是Web项目,,另外C#有种叫做ThreadStatic的static成员用来支持多线程的.,你的看法也太友善了吧,宁愿相信人家是不打算支持多线程,也不愿相信人家是被教坏了...
Re: 路过秋天
@新一年,另外,用ThreadStatic来处理静态变量,需要考量的因素很多:,业务是否有线程池(Web就有),如果有,对象资源怎么清除等?,所以门槛还是有的,也不能随处说用就用。
6楼zeus2
按照道理楼主说的都是对的。,,1、 尽量减少static对象的使用,在多线程处理不好容易造成银星问题。,2、 SQLHelper类应该设计为 即时在多线程的情况下,也能够正确处理并发的数据库请求, 而SQLhelper本本身是否 多例就不是太重要。
5楼Dream_Li
我自己感觉,上面给的示例代码是错误的。但是,这里的错误不是用错了static,而是不应该用单例模式。因为, if(_MyConnection==null), {, _MyConnection=new SqlConnection(connStr),
4楼马三小伙儿
不错,前几天老师让我们写了一个信息管理软件练练手,我就把数据库操作类写成static单例类了,老师说不对,直到看到这篇博文才恍然大悟啊。
3楼一叶兰舟飘
不该存在的不应该是XXXHelper吧, 不该存在的应该是内部的Static才对
Re: 路过秋天
@一叶兰舟飘,引用不该存在的不应该是XXXHelper吧, 不该存在的应该是内部的Static才对,没认真看文啊,如果叫XXXHelper,必然应该存在Static。
2楼沈赟
谢谢分享
1楼xmj112288
关静态什么事,这个明显是用法的问题。,帮助类里面肯定全是静态函数,除了常量属性或字段,其他属性和字段肯定是写在静态函数里面,作为内部参数封闭起来。,connection 这种变量肯定是做成内部参数封闭的,而且一个项目可能需要多个数据库。
Re: 路过秋天
@xmj112288,对的。