体验ADO.NET Entity Framework的继承

ADO.NET Entity Framework(以下简称ADO.NET EF)有一个非常可信的运行时。之所以不敢在项目中广泛使用是因为其糟糕的设计时。这个DSL设计时糟糕在哪里呢?其一,只能是先设计好数据库后设计实体模型;其二,如果你修改了数据库结构,再更新实体模型时,你所做的修改全部作废,最糟糕的是,很可能会出现映射错误,你必须手工来维护EDMX中的MSL部分。通常数据库结构的修改会成为你的噩梦。

还必须指出一点,ADO.NET EF在MSDN中的文档是有问题的。不信你看我的发现:MSDN,微软怎么会这样啊?

不得不承认,ADO.NET EF的运行时对继承关系的处理是非常让人舒服的。本文带你去体验一下ADO.NET EF的继承。

与通常的继承关系映射一样,支持三种方式:

  • 一体系一表方式
  • 一类一表方式
  • 一具体类一表方式

对于第一种方式和第三种方式,我是非常不屑的。我有文章讲过我的看法:细说继承关系映射

第一种方式有一篇文章已经讲了具体的操作方式:Single Table Inheritance in Entity Framework。这种方式有什么问题呢?那个“Type”字段我们是可以接受的,因为在运行时这个字段完全是透明的;但是那个“Salary”、“Rate”和“Hours”可以为空,这个我们不能接受,因为这意味着例如我们存贮一个SalariedExployee实体,可以堂而皇之地不给Salary赋值,而导致业务错误。这便是我所反感的“可空冗余”。不过凡事都有例外,如果派生类中的属性刚好都是可空的,那这个可空就不冗余了,而单表继承简单高效的好处就可以坐收了。

第三种方式我是无法接受的,因为根本解决不了我多次提到的“关系共享”的问题。

还好,ADO.NET EF提供了完美的第二种方式支持。有文章讲这个方式:

Entity Framework Modeling : Entity Splitting

Entity Framework Modeling: Entity Splitting Part II

不过,我没有按这篇文章所提示的方式设置成功,也许我的环境不同。重要的是,我明白了设置继承的几个要点:第一,不要手工修改BaseType来设置继承,而是要用上下文菜单中的“添加”→“继承”的方式。我们于是相信,除了BaseType以外,ADO.NET EF的DSL为EDMX中的MSL部分加入了某些内容。第二,手工删除派生类中的关键字段属性。第三,注意操作顺序。

现在用一个实例来说明。这个例子用于解决关于权限的问题。业务系统对权限的判断仅根据其是否拥有某个“权柄”。这个权柄用一个字符串表示在Privilege表中。这个权柄只可以授予给角色。用户和用户组都可以隶属于某个角色,用户组中的用户可以从该用户组继承所有的角色。实际上的应用可能会在权柄上再做一些文章,例如加上权柄作用域对象。这与本主题没有关系,就此省略。

下图是数据库的设计:

InheritanceDemo1 在VS2008中建立一个类库项目。新增一个名叫“InheritanceDemoModel.edmx”的实体数据模型,并从建立好的数据库生成,把实体连接设置命名为“DemoEntities”。于是,得到这个最初的成果:

InheritanceDemoDesign

先删除UserOrGroup和User之间的关系,再删除UserOrGroup与Group之间的关系,然后把所有的实体集名称改成复数。

现在加入两个继承关系:一个是UserOrGroup为基,Group为派生;另一个是UserOrGroup为基,User为派生。然后,变成这样:

InheritanceDemoDesign2

下一步,先把UserOrGroup的“抽象”属性改为“true”,再删除User的UserId属性和Group的GroupId属性。删除这两个属性以后,要分别把原来的两个字段(Column)映射到UserOrGroup的Id字段:

InheritanceDemoMapping1

InheritanceDemoMapping2

最后一步,由于删除了User的UserId属性和Group的GroupId属性,Group的Users属性和User的Groups属性映射被破坏了,需要重新映射:

InheritanceDemoMapping3

保存,完成映射。接下来可以做几个测试,例如,测试分别创建几个Privilege、Role、Group、User,然后用一个简单的Linq表达式来获取某个User所有的权柄。

代码就不贴了,有兴趣下载代码吧。

posted @ 2009-09-04 12:54 双鱼座 阅读(...) 评论(...) 编辑 收藏