从LinQ看我们的ORM设计

一直以来有些闭门造车,今天看了微软的一篇重要的ORM文章《下一代数据访问:使概念级别成为现实》,详细的讲解了微软下一代数据访问的设想和实现。我对其中的关于概念模型的一段代码相当感兴趣,因为他和我设计的Torridity(我们的ORM框架名)非常的相似。

  • 关键字的描述的位置

在MS的关键字描述中,一个重要的改变是:关键字被描述到实体中,而不是字段中:

<!-

典型的实体定义,具备标识 [关键字] 和一些成员

-->

<EntityType Name="Product" Key="ProductID">

<Property Name="ProductID" Type="System.Int32" Nullable="false" />

在很多ORM实现中,都是放在字段中,我认为那是错误的。现在看看我们的定义:

<dataEntityType name="Team" primaryKey="TeamId" >

<simpleProperties>

<simpleProperty dbType="String" isUnique="False" index="False" name="TeamId" isBrowsable="False" />

<simpleProperty dbType="String" isUnique="False" index="False" name="Code" />

<simpleProperty dbType="String" isUnique="False" index="False" name="Name" />

</simpleProperties>

</dataEntityType>

  • 支持继承性

既然是做ORM,继承性当然是不能缺少的,MS使用下面的方式定义:

<!-

派生产品,可以映射 TPH、TPC、TPT

-->

<EntityType Name="DiscontinuedProduct" BaseType="Product">

<Property Name="DiscReason" Type="System.String"

Nullable="false" Size="max" />

</EntityType>

我们使用了相同的方式:

<dataEntityType name="ComparisonExpense" baseType="ComparisonBase">

<simpleProperties>

<simpleProperty dbType="Decimal" isUnique="False" index="False" name="EstimateExpense" />

<simpleProperty dbType="Decimal" isUnique="False" index="False" name="ActualExpense" />

</simpleProperties>

</dataEntityType>

  • 支持复合类型

复合类型是对简单类型的扩展,实际上是支持对象化的属性。MS的定义如下:

<!-

复杂类型只定义结构而不定义标识。可以

在 0 个或更多实体定义中内嵌使用

-->

<ComplexType Name="CtAddress" >

<Property Name="Address" Type="System.String"

Nullable="false" Size="max" />

<Property Name="City" Type="System.String"

Nullable="false" Size="max" />

<Property Name="PostalCode" Type="System.String"

Nullable="false" Size="max" />

<Property Name="Region" Type="System.String"

Nullable="false" Size="max" />

<Property Name="Fax" Type="System.String"

Nullable="false" Size="max" />

<Property Name="Country" Type="System.String"

Nullable="false" Size="max" />

<Property Name="Phone" Type="System.String"

Nullable="false" Size="max" />

</ComplexType>

 

<EntityType Name="Customer" Key="CustomerID">

<!- 地址是引用内嵌式复杂类型的成员 -->

<Property Name="Address" Type="CNorthwind.CtAddress"

Nullable="false" />

<Property Name="CompanyName" Type="System.String"

Nullable="false" Size="max" />

<Property Name="ContactName" Type="System.String"

Nullable="false" Size="max" />

<Property Name="ContactTitle" Type="System.String"

Nullable="false" Size="max" />

<Property Name="CustomerID" Type="System.String"

Nullable="false" Size="max" />

</EntityType>

复合类型在Torridity并没有被明确标明:ComplexType,你可以使用任何实体作为结构复合进来。

<dataEntityType name="CallResult" primaryKey="CallResultId" isRoot="False" >

<simpleProperties>

<simpleProperty dbType="String" isUnique="False" index="False" name="CallResultId" />

<simpleProperty dbType="String" isUnique="False" index="False" name="ResultDescription" />

</simpleProperties>

<complexProperties>

<complexProperty dataEntityType="PickListEntityCpx" name="Result" />

<complexProperty dataEntityType="PickListEntityCpx" name="ObjectiveType" />

<complexProperty dataEntityType="ProductCpx" name="MainProduct" />

</complexProperties>

</dataEntityType>

  • 支持集合属性

在MS的设计中,集合属性是这样描述的:

<!-

Product [如上定义] 和 OrderDetails [出于简明需要而未显示] 之间

关联的示例

-->

<Association Name="Order_DetailsProducts">

<End Name="Product" Type="Product" Multiplicity="1" />

<End Name="Order_Details" Type="OrderDetail" Multiplicity="*" />

</Association>

在Torridity中,一对一被称为引用属性,而一对多称为集合属性,使用下面的方式定义:

<dataEntityType name="Orders" baseType="QuotationBase" primaryKey="OrderId" >

<simpleProperties isExpansion="True">

<simpleProperty dbType="String" isUnique="False" index="False" name="OrderId" />

<referenceProperty referenceTo="Quotations" dbType="String" isUnique="False" index="False" name="QuatationId" />

<simpleProperty dbType="Decimal" isUnique="False" index="False" name="TotalMargin" />

</simpleProperties>

<collectionProperties>

<collectionProperty itemDataEntityType="RevenueAssignment" name="RevenueAssignments" />

</collectionProperties>

<complexProperties>

<complexProperty dataEntityType="PickListEntityCpx" name="SourceId" />

</complexProperties>

</dataEntityType>

注意引用属性的区别:MS指向了一个类型,而我们指向了一个实体(TypeKey)。我认为MS的设计有些欠妥。

  • 实体容器

在MS中支持实体容器,功能好像强一些:

<!-

实体容器可定义

EntitySets(某一类型(可能)多形态实例的集合)和

AssociationSets(用于关联两个或更多实体实例的逻辑链接表)的

逻辑封装

-->

<EntityContainerType Name="CNorthwind">

<Property Name="Products" Type="EntitySet(Product)" />

<Property Name="Customers" Type="EntitySet(Customer)" />

<Property Name="Order_Details" Type="EntitySet(OrderDetail)" />

<Property Name="Orders" Type="EntitySet(Order)" />

 

<Property Name="Order_DetailsOrders"

Type="RelationshipSet(Order_DetailsOrders)">

<End Name="Order" Extent="Orders" />

<End Name="Order_Details" Extent="Order_Details" />

</Property>

 

<Property Name="Order_DetailsProducts"

Type="RelationshipSet(Order_DetailsProducts)">

<End Name="Product" Extent="Products" />

<End Name="Order_Details" Extent="Order_Details" />

</Property>

</EntityContainerType>

在Torridity中,集合类是自动创建的,没有专门的定义部分。

  • 对接口的支持

我非常奇怪的问题,ORM既然是对象化的,为什么没有包含一个非常重要的概念:接口。(也许作者忘记了吧)。

在Torridity提供了支持:

<dataEntityType name="Quotations" baseType="QuotationBase" primaryKey="QuotationId">

<interfaces>

<interface name="ICreateObject" />

<interface name="IOwnerObject" />

<interface name="INoteAccessoryObject" />

<interface name="ICodeObject" />

<interface name="IVersionObject" />

</interfaces>

<simpleProperties>

<simpleProperty dbType="String" isUnique="False" index="False" name="QuotationId" />

<simpleProperty dbType="DateTime" isUnique="False" index="False" name="StartDate" />

<simpleProperty dbType="DateTime" isUnique="False" index="False" name="ExpireDate" />

</simpleProperties>

</dataEntityType>

通过支持接口将大大简化实体的设计,而且是程序设计更加条例。我们知道真真的领域模型是不能通过继承性描述的,而支持通过特性(即接口)来描述。

posted @ 2006-07-24 13:32 编写人生 阅读(2055) 评论(27)  编辑 收藏

  回复  引用  查看    
#1楼 2006-07-24 16:00 | 双鱼座      
呵呵,还真没有见过让实体对象支持接口的,开眼界了。一直以来都以为接口的功能用来描述行为而不是状态,但实体对象则应该描述状态。所以,“真正的领域模型是不能通过继承性描述的”这一点非常令我质疑。
  回复  引用  查看    
#2楼 2006-07-24 18:00 | 老翅寒暑      
一直以来我对BO必须包含Id有些敏感,其实id在BO中根本没有必要存在。

另外,楼主为什么不把ms的原文链结贴出来呢
  回复  引用  查看    
#3楼 2006-07-24 20:43 | 双鱼座      
@老翅寒暑
从业务概念上讲无意义的Id的确没有必要,权当时为mapping过程所付的代价吧。通常可以在列表中用Id作唯一性定位。
  回复  引用    
#4楼 2006-07-24 21:54 | .progame [未注册用户]
从数据库的设计出发 逻辑主键优于业务主键(至少我持这种观点,尤其在分布式数据库存在时,或者是多业务主键存在于要建立关系时)

对于BO中好像就是gethashcode了 不过object默认hashcode是可能重复的
  回复  引用  查看    
#5楼 [楼主]2006-07-25 09:25 | 编写人生      
TO 老翅寒暑:
链接在文章上啊,怎么没有?
  回复  引用  查看    
#6楼 2006-07-25 09:29 | 老翅寒暑      
@双鱼座
但是我觉得总会有一个更好的办法来代替BO中那个无用的id的。我自己也尝试了一个xml转db的ORM工具,不过废弃很久了。我当时就觉得这个id在BO中可以作为“指针”使用(学过C++的朋友应该很容易理解)。
  回复  引用  查看    
#7楼 [楼主]2006-07-25 09:31 | 编写人生      
TO 双鱼座:
实体对象应该描述状态,我同意;
接口的功能用来描述行为而不是状态,理论上是对的,但我们不能抱着课本是天书。
接口可以认为是一种契约,符合某个接口意味着符合某种业务的契约。
  回复  引用  查看    
#8楼 2006-07-25 09:53 | 老翅寒暑      
@编写人生
我已经发现了,但是我发言当时提交了2次,第二次提交把这句话删了的,谁知道还是第一次提交的内容。
  回复  引用  查看    
#9楼 2006-07-25 12:01 | 双鱼座      
@编写人生
从哪里看出来我抱着什么课本?“接口是一种契约”是没有错,不过契约由于没有状态所以不需要由实体来持久化。
其实我更希望领教的是关于“真正的领域模型是不能通过继承性描述的”。
  回复  引用  查看    
#10楼 [楼主]2006-07-25 15:41 | 编写人生      
To @双鱼座:
针对你的问题,我写了一篇《真正的领域模型是不能通过继承性描述的》
http://tansm.cnblogs.com/archive/2006/07/25/459311.html
  回复  引用  查看    
#11楼 2006-07-25 16:00 | wuChang      
你可以向MS收版权税了
  回复  引用    
#12楼 2006-07-25 17:07 | ywer [未注册用户]
# re: 从LinQ看我们的ORM设计
@编写人生
从哪里看出来我抱着什么课本?“接口是一种契约”是没有错,不过契约由于没有状态所以不需要由实体来持久化。
其实我更希望领教的是关于“真正的领域模型是不能通过继承性描述的”。
http://www.xmucic.cn
http://www.uermusic.cn
http://ww.mp3r966music1.cn
http://www.mp3r966music1.cn
  回复  引用  查看    
#13楼 2006-07-25 22:05 | gozh2002      
最近看Ruby on rails,觉得它的ORM模型对应才是最强大的。
它连Tree的Mapping都有。


  回复  引用    
#14楼 2006-07-27 11:39 | 假山看月 [未注册用户]
RoR的确实不错。
不过针对.NET平台的这款ORM也很不错,尤其它的OQL巨经典:
http://www.macrobject.com/cn/NObject/OQL_-_Select.htm
这儿还有在线演示呢,注意看它的上下文提示:
http://www.macrobject.com/cn/NObject/OQL_In_Action.htm

  回复  引用  查看    
#15楼 2006-07-27 13:43 | 双鱼座      
@假山看月
最见不得你们这些做广告的,一点技术含量都没有
  回复  引用    
#16楼 2006-08-08 11:46 | 央视评论员 [未注册用户]
哇,小日本的Ruby好热啊,建议全国人民警惕之。。。

呵呵,我也看了上面的广告链接,技术含量还是有点的吧。

@双鱼座,建议兄台好好看看吧,你未必能做的出来。
  回复  引用  查看    
#17楼 2006-08-08 14:50 | 双鱼座      
@央视评论员
一、别的我肯定不敢说,但是对于你广告里的那些东西我不仅已经做了出来,而且肯定比你的那些强。二、我不仅免费,而且开源。不象你,要钱已经是很过份了,还到处做广告散布垃圾。三、我有名有姓,不象你总是匿名,还不断换马甲。
  回复  引用    
#18楼 2006-08-08 17:50 | 央视评论员 [未注册用户]
@双鱼座

首先声明,广告不是我做的,我也不是做ORMapping的。

再有,我觉得人家做的东西有技术含量,这只是我的感觉,我没看过你的东西。

后来我也去看了你的东西,觉得也有技术含量。

佩服,佩服!

只是觉得你老是鄙视别人,未必自己的就很好。

当然了,好用不好用,等我用了之后再来评价。
  回复  引用  查看    
#19楼 2006-08-08 18:21 | 双鱼座      
@央视评论员
第一,我说的“一点技术含量”都没有,我是指“做广告”这件事,借用了《天下无贼》里的一句台词,你连这也没看明白这不能怪我;第二,不是你做的广告你接什么茬呢?如果说我“鄙视别人”,那一定是鄙视那些言过其实的人。那个所谓的OQL其实henry早就实现了,呵呵,拿到这儿来却是商业产品了。我当然也不反对你做商业产品,XPO和ECO都是优秀的商业产品,但是人家不会那么下流到处做广告。
  回复  引用    
#20楼 2006-08-08 18:30 | 央视评论员 [未注册用户]
@双鱼座

呵呵,再得罪你一次,我觉得上面广告的 OQL 可以基本模拟完整的 SQL,你的查询确实很简洁,但是功能弱了很多啊。他们的 OQL 用在查询中,跟你的差不多简洁吧。

我就事论事,只说功能,跟价格开源等不相关。


  回复  引用    
#21楼 2006-08-08 18:35 | 央视评论员 [未注册用户]
@双鱼座

恕我孤陋寡闻,不知道 henry 是谁。

我就是看不惯一些人自以为有点水平,动不动就贬低别人。
  回复  引用  查看    
#22楼 2006-08-09 09:03 | 双鱼座      
@央视评论员
当枪手也应该当得聪明一点,不能显得这么愚蠢弱智。你为了一个做广告的贴子三番五次和我叫劲,已经违背了本园子技术为主的原则,恕我不能奉陪了。
  回复  引用    
#23楼 2006-08-09 09:52 | 央视评论员 [未注册用户]

呵呵,咱们别较劲了,反正又不是我的广告,爱咋咋。

兄弟们别伤了和气啊。

就是我们公司最近项目里要用ORM,我迫不得已到这儿来找找,就看到你们的东西了。

还望双鱼座兄指点一二。

不知双鱼座兄的东西能否用作商业用途?我也只是问问而已啦。

还有,henry 是谁?
  回复  引用  查看    
#24楼 2006-08-09 15:39 | 双鱼座      
@央视评论员
呵呵,欢迎和解。
1.Kanas.net可以用于商业用途不过风险自担。现正在构建基于.net2.0的2.0版,目前的最新版本是基于.net1.1的1.3.2版所有的核心部分开源(包括Framework、ObjectScope及AuthX),但VS.NET插件及工具不开源,仅可以免费使用。
2.henry是.net广州俱乐部的一个比较牛的家伙,不过最近好象对flex很着迷。这是他的博客:http://henryfan.cnblogs.com/
  回复  引用    
#25楼 2006-08-10 12:05 | 央视评论员 [未注册用户]
@双鱼座

呵呵,不知我如何才能拿到Kanas.net,粗粗看了一下,不知跟NHibernate相比有何优势和劣势,还望兄台指点。

公司里面对ORM的争议也很大,我也很头疼,所以不管大的、小的、商业的、开源的、公司的、个人的,我全都要比较一下,才能决定如何使用啊。

如果双鱼座兄能够将园子里好的ORM框架介绍一下,那就再好不过了。

我原先一直从事PB开发,最近才转入dotNet,对园子里的情况很不熟悉啊。

要是说话不妨头,得罪了各位,还请海涵啊。

  回复  引用    
#26楼 2006-08-12 23:48 | Kimphi [未注册用户]
央视评论员 和 双鱼座 两位兄台为我们的“广告”发生了些争论,令在下着实感激。

◎双鱼座
你的查询语句非常简洁,确实不错,我们在 OQL.NET 中也加入了这种查询方式的支持。除了自我创新外,我们很重视借鉴和学习别人的长处。

我们要养活自己和弟兄,所以我们走商业模式。

我们也会尽力奉献,所以我们也提供免费和开源的产品(比如CodeAuto就是开源的)。

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-07-24 13:36 编辑过