随笔 - 2  文章 - 0 评论 - 26 trackbacks - 0

我的豆瓣
昵称:Loris
园龄:7年8个月
粉丝:0
关注:0

搜索

 
 

常用链接

最新随笔

随笔分类(2)

随笔档案(2)

      假设
      1)你跟我一样在某种程度上不能容忍“智能UI”的开发方式。
      2)你跟我一样同样不能容忍持久层掺杂进任何的业务逻辑
      3)你跟我一样在面向对象数据技术彻底成熟之前,总是优先倾向于ORM,而不是直接书写SQL语句来完成对象持久相关的工作,你总是认为数据库就是个仓库而已。

      一直以来我对关系数据库总是爱恨交加。最让我烦躁的是,总是不得不把一些业务逻辑放到数据库中去,哪怕就这么一点原因,也足以让我非常非常的不爽。这其中又有两个典型的场景。
      场景一:通过查询条件来获取列表。在这种情况下,有这么一种比较典型的设计,代码类似下面的样子:      

 1/// <summary>
 2        /// 扩展查询机构信息(模糊查询)
 3        /// </summary>
 4        /// <param name="orgName">机构名称片断</param>
 5        /// <param name="orgType">机构类型</param>
 6        /// <param name="isAuthorize">是否授权</param>
 7        /// <param name="orderName">排序字段名称</param>
 8        /// <param name="isASC">是否升序</param
 9        /// <param name="pagingInfo">分页信息</param>
10        /// <returns>机构列表</returns>

11        IList<Organization> GetOrgInfoListByMultiField(string orgName, OrgType orgType, bool? isAuthorize,
12                                                       string orderName, bool isASC, PagingInfo pagingInfo);

这个方法所在的接口,放在了领域层内,但是接口的实现是在数据访问层。在这种场景下面,如果是仅仅单表的查询,还不是最糟糕的,起码各种各样的参数还标明了领域层的印记。最糟糕的是查询里面隐藏了很强的业务逻辑
      场景二:获取在某个项目中,没有参与投标的企业列表(申报的产品数量为0)。在这种情况下,我同样可以设计一个接口方法,象上面的一样,提供一个ProjectId来进行查询。但是申报的产品数量为0这个显而易见的领域逻辑,就以一种令人生厌的Sql形态高度耦合到持久层了。

      我很难想象这种原本属于领域层的逻辑大量的以查询的方式散落到持久层会对业务相对复杂的系统产生的不良影响。令人欣慰的是ORM技术越来越成熟,显然他们针对这个问题做出了一些努力。比如在Nhibernate中提供了HQL,我们可以使用HQL来进行面向对象的查询,通过NHibernate的支持来满足类似上述这两种场景的需求。但是,仅仅是这样,还不够。HQL本质上还是字符串,我们无法象静态类型一样来保证我们的类型安全,保证编译期间排除错误
      
      于是,强类型查询出现了。请看下面的类似代码:      

1var q2 = (from o in db.Orders
2                 where !(new string[] "AROUT""BOLID""FISSA" }).Contains(o.CustomerID)
3                 select o).ToList();

      强类型在HQL之外,给我们带来了更直观,更强壮的查询。这使得我们从本质上可以用相对优雅的方式完全把领域和它的仓库隔离开来(抛开诸如效率,学习曲线,领域建模这些问题先不谈)。我们可以这样理解强类型的查询,用我们熟悉的类型安全的代码方式来表达我们的业务逻辑,然后交给支持强类型的服务去执行。
领域层依赖于持久层提供的服务,但是聚合了领域范围内有实际意义的业务逻辑。
      通过相关的ORM技术(这种技术支持强类型查询,至少也要支持一套描述标准SQL的查询规则)以及相应的配套工具,剥离领域层和持久层的强关联,可以使我们更专心于业务的本身。 

      期待着相对屏蔽数据库的开发时代到来……              



哪怕上帝要发笑,也要学着去思考

posted on 2007-08-30 14:55 Loris 阅读(1938) 评论(26) 编辑 收藏

FeedBack:
#1楼 2007-08-30 15:01 sunrise[未注册用户]
不知道你要说什么!
 回复 引用   
#2楼[楼主2007-08-30 15:06 Loris      
@sunrise

其实总的来说这篇随笔就说了一句话,强类型查询可以更优雅的把持久和业务逻辑隔离开来,使得数据库就是个数据库。

 回复 引用 查看   
#3楼 2007-08-30 15:45 zjw2004112
的确讲的很含糊,看的云里雾里
 回复 引用   
前面说的很好。。。以为有解决方案了呢。。。。。虎头蛇尾。
不过还是帮顶。。
我也在郁闷这个东西。。。。。。。。。老大有续篇没?

 回复 引用   
含糊的很啊
 回复 引用   
#6楼 2007-08-30 16:04 没剑      
题目是不是应该为“DLINQ”来了~
 回复 引用 查看   
#7楼[楼主2007-08-30 16:22 Loris      
@guohao0826

我也没有深入的去了解Dlinq,或者是Nbear相关的技术/框架,所以谈不上任何深入,我想要表达的就“强类型查询可以更优雅的把持久和业务逻辑隔离开来”

@没剑

好像差不多啊……
不过俺不会Dlinq,还没深入的开始看。就看着这个查询挺激动人心的,一个忍不住就发发意见

 回复 引用 查看   
#8楼 2007-08-30 16:49 df[未注册用户]
标题党。

赶紧撤下首页吧

 回复 引用   
#9楼[楼主2007-08-30 17:08 Loris      
@df老师

我不觉得这个标题有多么多么的“大”。
相反我要表达的是,思想永远比具体的技术重要。
如果博客园的首页完全是具体的技术的Step by Step,我想也是一种悲哀。
就像博客堂现在的首页总是充斥着,利用XXX技术,做了个YYYdemo一样,难道不是浮躁的一种表现吗?

 回复 引用 查看   
数据库怎么可能一点业务逻辑都没有呢?
数据库怎么可能就是一个仓库呢?

要知道仓库也是要分好多种类型的,放书的(图书馆)、放大米的(粮仓)......

它们的外观和内部结构是完全不一样的!为什么?很简单,放的东西是不一样的。
也就是说要根据要存放的东西(业务逻辑)来决定仓库(数据库)的结构(数据库设计)。


>>最糟糕的是查询里面隐藏了很强的业务逻辑。

因为数据库就是根据业务逻辑设计的呀。查询(SQL语句)自然会带有很强的业务逻辑,即使你使用了ORM、Linq,最终还是要“转换”成SQL语句的。



 回复 引用 查看   
#11楼 2007-08-31 08:37 henry      
基于数据库的强类型查询主要是一进步保证程序的安全性(借助于IDE功能可以提高编写的效率).
对于直观,更强壮似乎拉不上关系.学习成本也不见得比SQL少.

 回复 引用 查看   
#12楼[楼主2007-08-31 09:13 Loris      
@金色海洋(jyk)

我觉得数据库应该在软件中扮演更简单的角色,相对于目前的大部分开发方式而言。许多项目的数据库往往承担了太多的业务逻辑。如果可能,我甚至反对唯一约束的出现,当然这个想法可能太过极端。

@henry

不知道henry是不是henry的fans,反正俺是:)

强类型查询最大的意义我觉得反而不在借助IDE提高编程效率,而是在数据库外面又外包了一层。业务逻辑可以用这层的相关定义来更好的纳入领域层而不是“仓库”里。


 回复 引用 查看   
#13楼[楼主2007-08-31 09:33 Loris      
--->即使你使用了ORM、Linq,最终还是要“转换”成SQL语句的。

即使用了诸如C#,JAVA这种高级语言,最终还是要转换为机器码的……

 回复 引用 查看   
#14楼 2007-08-31 10:39 xc#      
--->即使你使用了ORM、Linq,最终还是要“转换”成SQL语句的。

即使用了诸如C#,JAVA这种高级语言,最终还是要转换为机器码的……





 回复 引用 查看   
#15楼 2007-08-31 10:43 搜索人生      
lz辛苦了!不过,我也觉得不太适合放首页啊!
lz都写着,学会思考,勇于思考,这帖子放首页确实需要勇气。。。

 回复 引用 查看   
#16楼 2007-08-31 10:43 henry      
@Loris
你能告诉大家SQL和强类型查询有什么本质区别吗?我一直从事这方面的设计想听一下你的看法.

 回复 引用 查看   
#17楼[楼主2007-08-31 12:33 Loris      
@henry

我觉得本质的区别就是表达业务逻辑的位置。
我可能对于SQL的理解有些狭隘,我比较喜欢将其划分到持久层。
我觉得领域层里面应该包含从仓库取数据的所有逻辑。

比如说我在领域层可以定义一些取数据的接口,然后在持久层来实现这些接口。
这是任何没有问题的。但是有一些情况下,我需要通过一些业务规则的约束来取出数据,这个时候,我的接口(包括签名和参数),都不足以完全的描述这些业务规则(比如Post里面提到的第二种情况,当然有些情况会更复杂)。这个时候如果我们使用这种面向强类型的查询,就可以把这个业务规则纳入到领域层,而不是持久层了。

当然上面所有的这些的前提是,领域层不应该包括持久的具体实现。
“在领域层直接用SQL,然后调用类似一个SQLHELPER的持久层”的这种做法,虽然也是把业务逻辑全部纳入了领域层,但是我觉得还是有一定的局限性。

1)如果持久方式发生了变化呢?假如干脆不使用数据库来持久对象了。或者有些情况下要去读取一些文件,有些情况下又要去读数据库。
2)如果我要进行一次数据库重构呢?大量的散落的SQL存在于我的领域层内,我不得不为了这种重构来修改代码。
当然应该还有更多更充分的理由,鉴于我比较肤浅的认识,列举不出来了。

我认为领域层要集中解决的是这个软件映射在现实的领域中的业务规则,而不应该把精力放在从持久媒介中获取对象这个点上。


我觉得ORM就等同于薄薄的一层,由于这层的存在,可以使我们开发领域层的时候目标更单纯。
而强类型查询的出现,使得我们在编写代码的效率,控制出错几率上都有了很好的提高。

本质上是一层解耦。

 回复 引用 查看   
#18楼[楼主2007-08-31 12:36 Loris      
@搜索人生

不放在首页上,如何“勾引”大家来讨论(反驳),从而更好的来论证呢^_^
没办法,其他的板块关注度低呀。

 回复 引用 查看   
#19楼 2007-08-31 14:01 henry      
@Loris
SQL语句是数据库查询语言并不是某种数据库的查询语言.就是说SQL是个标准,相信每一个类型查询最终不会脱离SQL.如果脱离就说明得不到其他数据的支持(除非其他数据库厂支持),这样根本达不到程序和数据库解偶的目的.
为什么换数据库类型要改写SQL语句?很简单在写SQL用上了该数据库的扩展功能(当针对ORACLE特性编写的强类型查询,想在MSSQL上就能用上是不可能的事情).
作为一种标准的查询言语也完全用它来做数据库以外的文件查询,和强类型查询一样做一个内核解释处理.
对于如何解决不同数据库的ado.net访问对象类型问题,可以了解一下ado.net的数据访问接口。
两者最大的差别在于强类型可以得到编译器的接管和检测,提高其编写效率和降低调试成本。
由于中间多了一层解释处理运行效率必然有所降低。

 回复 引用 查看   
#20楼[楼主2007-08-31 14:18 Loris      
@Henry

好像我更多的考虑到了层次划分上去了,我努力要表达的是,有了这种方式,可以以一种更容易更抽象的形式来表达业务逻辑。这反而偏离了这个“SQL和强类型查询的本质区别”这个论点。

“两者最大的差别在于强类型可以得到编译器的接管和检测,提高其编写效率和降低调试成本”

确实如此,精辟。

 回复 引用 查看   
即使你使用了ORM、Linq,最终还是要“转换”成SQL语句的。

即使用了诸如C#,JAVA这种高级语言,最终还是要转换为机器码的……


我现在可以直接写 SQL语句,谁现在可以直接写机器码?

如果都不能的话,那我只能说第二句话是在抬杠。

两句话根本就没有可比性!希望大家冷静一点。

我用SQL语句可以实现使用ORM、Linq 相同的功能,而且复杂程度差不多。
那么你能用机器码 轻松的实现 C#,JAVA可以实现的功能吗?


 回复 引用 查看   
#22楼 2007-09-01 02:13 deerchao      
不幸的是Linq带来的不是解决分层问题的良药,而是把多层合一的可能.

把Expression从UI层传递到DAL层,和把Sql这么传过去有什么区别?--唯一的区别是编译器对类型信息的掌握.和分层不分层那是八杆子也打不着.

很早就有人在讨论有了linq,我们还要不要DAL层了?

 回复 引用 查看   
那倒是,我觉得有了 类似于 SQLHelp 这样的东东就可以了。
linq 只是SQL语句的一种变化,或者是封装吧。其实质还是 SQL语句。

var q2 = (from o in db.Orders
where !(new string[] { "AROUT", "BOLID", "FISSA" }).Contains(o.CustomerID)
select o).ToList();

Orders 是不是表名?"AROUT", "BOLID", "FISSA" CustomerID 是不是字段名?

如果是的话:
1、字段名、表名变化的时候这里要不要改?
2、这段代码是写在哪里呢?如果是写在数据层里的话,那么那还是”散落到持久层”。where 后面的不代表一种业务逻辑吗?
“申报的产品数量为0” 是不是也要放在这种语句的 where 后面呢?

如果是写在逻辑层的话,字段名和表名就跑到逻辑层里了。


SQL语句多少还是可以换数据库的,那么linq据我所知只能支持 SQL 2005 。


@ Loris

比较同意你的观点。

>>2)如果我要进行一次数据库重构呢?大量的散落的SQL存在于我的领域层内,我不得不为了这种重构来修改代码。

这句话是有点误区的,即使你把SQL语句完全放在了 数据层,那么当“数据库重构”的时候还是要修改代码!修改的量也差不多,只是放在了数据层比较好找一点。其实还是没有在根本上解决问题。

解决你的这个问题还有更好的方法的。

 回复 引用 查看   
#24楼[楼主2007-09-01 17:05 Loris      
@ 金色海洋(jyk)

强类型查询的话,那个地方肯定不应该是表名称和列名称的。

@deerchao

不幸的是Linq带来的不是解决分层问题的良药,而是把多层合一的可能

愿闻其详。

 回复 引用 查看   
var q2 = (from o in db.Orders
where !(new string[] { "AROUT", "BOLID", "FISSA" }).Contains(o.CustomerID)
select o).ToList();

Orders 是不是表名?"AROUT", "BOLID", "FISSA" CustomerID 是不是字段名?

那么我能问一下, Orders "AROUT", "BOLID", "FISSA" CustomerID 这些是什么吗?如果不是表明和字段名的话,那么核表名字段名有没有关系(假设用数据库来存放数据)?

在我理解 Orders 是一个订单表,CustomerID 是客户ID 。

 回复 引用 查看