[广告]大家的数据访问层一般都是怎么写的?用过哪些框架来减少自己的代码呢?
Ivony 2006-11-19 09:20:00 大家的数据访问层一般都做成数据库访问层,即封装数据库的差异,返回数据集;还是纯正的数据访问层,封装数据逻辑,查询和筛选都自动生成,直接返回包含逻辑的实体而不是数据,也不接受任何SQL语句,包括筛选表达式。
如果用ORM,那么又有多少是ROM而不是真正的ORM呢?(若先设计数据结构,即数据驱动的,都不能称为ORM,而只是ROM)
大家现在都是直接用ADO.NET访问数据库还是用了第三方的Helper如SqlHelper等,或者是自己写的Helper呢?
提这么多问题,其实是想把我以前做的一个数据访问框架开源,与大家一起讨论这个框架的优劣。
大家有没有想过,怎样书写SQL语句是最惬意的呢?
像这样好不好?
new SqlCommand( connection, string.Format( "SELECT * FROM User WHERE ID = {0}", id ) );
肯定有人会说了,这是典型的注入式漏洞。
但我要告诉大家,有一个数据库访问的框架,可以让你这样自如的写任何SQL语句而不用担心任何注入式漏洞。
您只需要这样写:
DataRow data = dbUtility.ExecuteSingleRow( "SELECT * FROM User WHERE ID = {0}", id );
其他的事情,框架都帮你完成了。其他的事情,您根本不用操心。
因为它会帮您办理如下事情。
1、如果您要求每一次都创建Connection对象(对于ASP.NET来说,这个要求很常见),那么他首先会帮你创建这个Connection对象。
2、如果Connection没有打开,他会帮你打开,并在执行完后关闭,您根本不用操心这个事情。
3、他会自动帮您生成SqlCommand对象,并将Connection给它,当然,这是很普通的功能,不过下面的就不这么简单了。
4、他会分析您的命令字符串,并且将它解析成这个样子:"SELECT * FROM WHERE ID = @Param0",这也是他不会产生注入式漏洞的原因。
5、它不仅仅会帮你修改命令,并且会将您的id变量自动包装成SqlParameter对象。
6、他会自动执行这个SQL语句创建一个Reader,并且通过Reader得到架构而产生一个DataTable。
7、他会从Reader中读取数据并填充DataTable。
8、他会将您最关心的东西,即这个DataTable的第一行给你,并且他认为这个结果集只会有一行。
但,这还只是他最基本的功能。
他支持各种模板,如,您也许不想这样写:
dbUtility.ExecuteNonQuery( "INSERT INTO User ( name, password, borndate, CID, color, style, data, vip ) VALUES ( {0}, {1}, {2}, ...", name, password, borndate, CID, color, style, data, vip)//抱歉,我都懒得写完。
也许不会有人喜欢写这种枯燥的{0}, {1},好在我们也想到了,所以您可以用{...}来代替这一长串的东西。
当然,以后会有更多的东西能被替代,如UPDATE语法中的长串 variableName = value。
我们开发这个框架的目的就是,让程序员专注于自己该干的事情,并且我们也会一直贯彻下去。
他还支持您把一个代码片断当成一个参数使用,他会自动地拼合SQL语句,这个东西在拼合筛选表达式和分页时非常有用。
您是否遇到了创建筛选表达式的难题?
您不知道有多少个筛选条件,但您不得不把他们组合在一起。
例如可能会遇到要求搜索年龄在y岁以下,样式为s的用户。您一般会这样做:
string filter = "age = " + y;
filter += "AND style = " + s;
而事实上,您会写一大段通用的代码,来满足前面多变的情况:
string filter = "";
if ( age != null )
filter += (filter == ""?"":" AND ") + "age = " + age;
if ( style != null )
filter += (filter == ""?"":" AND ") + "style = " + style;
写到这里,代码看起来已经不是那么的舒服,但您还是必须处理注入式漏洞的问题。
于是,您的代码变得像裹脚布一样又臭又长。
也许您能自己写个类来简化上面的代码,让他看起来舒服点儿:
FilterBuilder builder = new FilterBuilder();
builder.AddAndFilter ( "age = " + age );
builder.AddAndFilter ( "style = " + style );
然后,您也许也能用参数来做,但这样您就不得不把Command对象传给FilterBuilder,这从逻辑上看起来是非常古怪的。并且,这让您不能先创建一个filter,然后再将其安插到一个现成的SELECT语句中去。这一点让人非常难受。
让我们来看看我们是怎么解决这个问题的,我们刚刚在上面说过了,这个数据访问框架能够帮您处理掉注入式漏洞。但他不能处理参数个数不一定的情况。
于是我们参照CodeDOM的模型,引入了Expression的概念,我们发现,这个解决方案也许不是最完美的,但是的确能解决问题。
TemplateExpression就是能把很多代码片断组成一个SQL语句的类。事实上,我们上面所说的所有用法,都是隐式创建了一个TemplateExpression,并将那些参数都转换成了ParameterExpression,ParameterExpression就是一个代码片断,并且它所生成的SQL语句是在执行的时候动态生成的,也就是说实际上这个东西是跨数据库兼容的,在SqlServer会生成@Param0,但在别的数据库却会生成变得东西。
如果您没有看懂上面这一段话也没有问题,因为您只需要知道怎么用就可以了。
如,我们现在创建一个filter。
Expression filter = TemplateExpression.Create( "ID = {0}", id );
当然,这个filter我们现在是用固定的语句创建的,您可以动态来创建这个东西。我们需要的只是一个Expression,即Sql代码片断。
然后,您就可以这样用:
DataRow data = dbUtility.ExecuteSingleRow( "SELECT * FROM User WHERE {0}", filter );
当然,这样也可以:
DataRow data = dbUtility.ExecuteSingleRow( "SELECT * FROM User WHERE {0} AND style = {1}", filter, style );
是不是很灵活?
并且,SqlParameter的名字,是全局编号的哦,即如果您的filter是"name = {0} AND borndate < {1}",然后放在上面的语句中执行,最后实际上会创建@Param0 = name、@Param1 = borndate、@Param2 = style。即,style并不是看起来的那样是@Param1。
不过说了这么多,都还在他的核心打转转哦。
至于他的外围,那就厉害了,例如Schema和FilterBuilder,能让您这样生成查询表达式:
FilterExpression filter = ( User.name == name ) & ( User.style == style );
是不是很直观?重载一下运算符这是能做到的哦。
不过这样也许看起来就更爽了:
SelectQueryExpression query = new SelectQueryExpression();
query.Fields = SchemaHelper.GetAllFields( User );
query.Where = new WhereClause( filter );
query.From = new FromClause( User ).InnerJoin( Data, onExpresssion );
可惜的是,这些很爽的外围应用……
需要大家的努力哦……
也就是说,
他们都还没完成……
说了这么多,不知道大家是不是都有那么一丁点儿的兴趣了呢?
其实我还有很多东西没拿出来哦,如数据分页方面的。
不过今天敲这么多东西实在是太累了,下次再拿出来与大家分享吧。
差点儿忘了一件最重要的事情。
这个东西的代码铁定是开源了。
在恰当的时机,我会拿出来与大家分享的。
如果想要先睹为快的,可以直接与我联系。