[广告]大家的数据访问层一般都是怎么写的?用过哪些框架来减少自己的代码呢?

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 );


可惜的是,这些很爽的外围应用……
需要大家的努力哦……
也就是说,
他们都还没完成……



说了这么多,不知道大家是不是都有那么一丁点儿的兴趣了呢?
其实我还有很多东西没拿出来哦,如数据分页方面的。
不过今天敲这么多东西实在是太累了,下次再拿出来与大家分享吧。

差点儿忘了一件最重要的事情。
这个东西的代码铁定是开源了。
在恰当的时机,我会拿出来与大家分享的。
如果想要先睹为快的,可以直接与我联系。
...全文
2438 161 打赏 收藏 转发到动态 举报
写回复
用AI写文章
161 条回复
切换为时间正序
请发表友善的回复…
发表回复
海底死鱼 2007-04-25
  • 打赏
  • 举报
回复
mark
长江支流 2007-04-12
  • 打赏
  • 举报
回复
有兴趣的朋友可以下载长江支流三层结构,同一代码跨数据访问
www.WebMIS.com.cn

长江支流 2007-04-12
  • 打赏
  • 举报
回复
对于楼主提到的,还要调用者写SQL,然后楼主的在内部为其转换为防注入。

我写的一个模型,是调用者根本不用写SQL,只要实现接口,通用于Orcal、Sql Server...

//实体定义类
public class EntityTest:WebMIS.Data.EntityAccess.DBEntity
{
private int _ID = 2;
private string _Name = "test";

public int ID
{
get{return _ID;}
set{_ID=value;}
}
public string Name
{
get{return _Name;}
set{_Name=value;}
}

public EntityTest():base("TableName","ID"){}

public override IList GetFields()
{
return new string[]{"ID","Name"};
}

public override IList GetFieldValues()
{
return new object[]{_ID,_Name};
}

public override IList GetPrimaryKeyValues()
{
return new string[]{"ID"};
}

public override void LoadFrom(System.Data.DataRow entityDataRow)
{
_ID = int.Parse(entityDataRow["ID"].ToString());
_Name = entityDataRow["Name"].ToString();
}
}

//实体管理类,只要继承WebMIS.Data.EntityAccess.EntityAccess,即自动实现数据的增、删、改、查...
public class EntityTestManagement:WebMIS.Data.EntityAccess.EntityAccess
{
public EntityTestManagement(EntityTest entity):base(entity){}

protected override WebMIS.Data.EntityAccess.IExeSql DoReturnDataAccessInstance()
{
return null;
}
}
长江支流 2007-04-12
  • 打赏
  • 举报
回复
我写的一个东东就是这样,同样的东西,能够应用于Oracle、Sql Server。
前段时间就是因为把它放在QQ群上,导至群爆满了。



---------------------------------------------------------
不过,前天去面试那公司的技术主管说ORM可以不管是Orcal、Sql Server支持的语句都可以统一性支持,达到了即使更改数据库类型,也不用更改任何代码,不知楼主的框架做到这点没?

显然此人不懂ORM

况且我也从来不敢说我在做ORM

sp1234好像在那里做真正ORM的框架。
折射光 2007-04-12
  • 打赏
  • 举报
回复
mark
Ivony 2007-04-03
  • 打赏
  • 举报
回复
关键是4月7日的期限快到了,匆忙结帖,没有细看每一格有价值的回复,见谅……
lxwin01 2007-04-02
  • 打赏
  • 举报
回复
怎么没分啊,好像是我没做过ORM框架?,www.surely.cn看看客户列表,不是所有场合都用ORM,如果你在查询时用ORM我不敢想像,"框架"我认为是半成品,我写的并完全是ORM框架,如果将所需要的功能合起来可以完整的ORM.当在大量查询和计算时,查询这部分我将放弃,但可以采用框架中的部分模板来简化查询操作,在定制查询时,又可以将条件给sql生成器生成sql,因为有时我不需要数据库反射对象,只要一个Table结果。
flyingfz 2007-03-30
  • 打赏
  • 举报
回复
mark
deng2001 2007-03-30
  • 打赏
  • 举报
回复
同意 viena(维也纳n_n)
Ibatis.net
LifeForCode 2007-03-30
  • 打赏
  • 举报
回复
用自己寫的dbUtility,只是封裝Conn、Cmd,沒LZ的功能這麽全
GZ
只看远方 2007-03-30
  • 打赏
  • 举报
回复
lincai 2007-03-30
  • 打赏
  • 举报
回复
这贴好像沉了?
study_boy 2007-03-28
  • 打赏
  • 举报
回复
具体问题具体对待,通用的方法总会有一些损失
webhermit 2007-03-28
  • 打赏
  • 举报
回复
啥也不说,留名先!
MarcuseXiao 2007-03-28
  • 打赏
  • 举报
回复
从上看到下,没有看到架构方面的一个关键性东西:

你的数据库实体呢?

如何定义?如何产生?自己写?还是用工具生成?

上面所有的只是说到了一个问题,数据访问层的问题.离开了实体,谈什么架构?

仅仅因为安全性的考虑(防止注入式攻击),或者为了少写些SQL语句,这是架构吗?
rest1234 2007-03-28
  • 打赏
  • 举报
回复
强贴 留名
mybaby11 2007-03-28
  • 打赏
  • 举报
回复
一直关注楼主的BLOG,支持,期待开源
gugu1981 2007-03-23
  • 打赏
  • 举报
回复
gugu_cz@163.com。多谢楼主。确实需要在这方面有个人来指导一下
ice2927276 2007-03-23
  • 打赏
  • 举报
回复
学习啊
michney 2007-03-23
  • 打赏
  • 举报
回复
michney@sohu.com
加载更多回复(141)

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧