实例解析继承体系重构及ORM映射

本文以双鱼座同学的再说继承关系一文中提到的一组三元继承关联关系为基础。

首先,分别实现本人的ORM中的继承关系映射全解一文中提到的三种实体继承体系到关系数据库的映射方案实例。

接着,使用接口分离以上继承体系中的实体类中的相同概念,对该继承体系进行重构,并同样给出对重构后的继承体系的三种到关系数据库的映射方案实例。

全部实例代码基于NBear的接口式实体定义方式实现。

1. 背景

首先,给出双鱼座同学原文中的三元继承关联关系的等价接口定义。见下图1:

2. 对图1-1的ORM映射

2.1 单表继承体系

采用单表继承体系映射时,我们用一个AllInOne表包含所有实体的所有字段,并用一个FilterMark过滤标志,区分该条纪录的类型,注意只有所有的User,包括抽象的User和所有的UserGroup是可以查询的,其余接口仅仅用于概念抽象。 

 

代码1
        [Table(IsView 
=   true )]
        
public   interface  PrivilegeOwner : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string Name getset; }
        }


        [Table(
" AllInOne " , AdditionalWhere  =   " FilterMark <= 20 " , IsView  =   true )]
        
public   interface  User : PrivilegeOwner
        
{
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 1 " , AdditionalWhere  =   " FilterMark = 1 " )]
        
public   interface  LocalUser : User
        
{
            
string LoginId getset; }
            
string Password getset; }
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 2 " , AdditionalWhere  =   " FilterMark = 2 " )]
        
public   interface  AgentUser : User
        
{
            
string LoginId getset; }
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 3 " , AdditionalWhere  =   " FilterMark = 3 " )]
        
public   interface  GhostUser : User
        
{
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 21 " , AdditionalWhere  =   " FilterMark = 21 " )]
        
public   interface  UserGroup : PrivilegeOwner
        
{
            
string Comment getset; }
        }

2.2 一实体一具体表

采用一实体一具体表思路进行映射时,要为每个继承层次中的具体的实体定义一个重复包含所有父类字段的表,并在保存数据时,同时,更新字表和所有的父表。表示抽象契约的接口,在每个实现类包含一份重复字段定义,不需为这样的接口定义独立的表。

 

代码2
        [Table(
" PrivilegeOwner " )]
        
public   interface  PrivilegeOwner : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string Name getset; }
        }


        [Table(
" User " )]
        
public   interface  User : PrivilegeOwner
        
{
        }


        [Table(
" LocalUser " )]
        
public   interface  LocalUser : User
        
{
            
string LoginId getset; }
            
string Password getset; }
        }


        [Table(
" AgentUser " )]
        
public   interface  AgentUser : User
        
{
            
string LoginId getset; }
        }


        [Table(
" GhostUser " )]
        
public   interface  GhostUser : User
        
{
        }


        [Table(
" UserGroup " )]
        
public   interface  UserGroup : PrivilegeOwner
        
{
            
string Comment getset; }
        }

2.3 一实体一扩展表

采用一实体一扩展表思路进行映射时,注意将表示真正的继承概念中的基类或子类分离为独立的表,而将表示抽象契约的接口,在每个实现类包含一份重复字段定义,不需为这样的接口定义独立的表。对于没有扩展任何基类的属性的子类,也至少需要包含一个Id字段和它的父类关联。注意每一个XXXExtedned实体对应了实体扩展表相对于父类扩展的字段,而不带Extended后缀的表则大多是代表逻辑实体的视图。

3. 重构继承体系

图1-1的继承体系定义客观地说是比较简洁的,我相信也能满足当前的应用需求。重构的目的不是要推翻或者批驳原来的设计,而是尝试使得模型的可扩展性更强。例如,图1-1所示的模型中LocalUser和AgentUser都有一个LoginId,表示他们是可以登录的,那么,完全可以将可登陆语义抽象出来;再如,现在User和UserGroup都继承自PrivilegeOwner对象,代表他们都能赋权限,但是,将PrivilegeOwner作为User和UserGroup的基类会对User和UserGroup将来的扩展带来很大的限制,例如,在这个模型基础上,不能定义一个“不能赋权限”的GuestUser类型,也不能为UserGroup类指定另一个更抽象的Group作为基类,等等。

为了解除这样的限制,我们就要对这个模型进行重构,重构的核心就是使用接口分离以上继承体系中的实体类中的相同概念。重构后的继承体系见下图2:

注意图中表示继承的箭头。

首先,我们将Id和Name属性抽象为一个IdentableEntity接口,它代表了,继承类拥有一个唯一Id和一个Name描述的契约。User和UserGroup都从IdentableEntity继承,遵守该契约。抽象出该契约的目的是,将来有新的,拥有同样Id标识语义的实体时,可以通过继承该契约减少重复定义。

接着,我们抽象出PrivilegeAssignable接口,包含一个PrivilegeOwnerId用以和权限分配表关联,为可赋权限的对象分配权限。这样做的好处还在于,当需要定义新的拥有“可赋权限”概念的实体时,可以直接继承该接口,避免重复定义。

再接着,我们抽象出Loginable和PasswordLoginable接口,分别代表一个可登录对象和一个需要密码才能登录的对象的抽象契约。PasswordLoginable本身也是继承自Loginable的。

最后,我们可以看到,现在User和UserGroup都从IdentableEntity和PrivilegeAssinable继承,遵守“可标识”和“可赋权限”这两个契约;GhostUser,LocalUser和AgentUser都继承自User,并且,LocalUser和AgentUser分别继承自Loginable和PasswordLoginable接口,遵守“需要密码的可登录”和“可登录”契约。

我们可以发现,新的继承体系模型,比原始模型大大增强了可扩展性。例如,如果我们要添加一个不可设置权限的GuestUser对象,只需让User类不继承PrivilegeAssignable而成为所有User的基类,而使用一个新的继承User并实现PrivilegeAssignable的PrivilegeAssignableUser类作为GhostUser、LocalUser和AgentUser的基类;再如,此时,如果要增加一个不能赋权限的Group抽象,我们也可以非常方便的增加一个Group抽象类作为UserGroup的基类,并将原来UserGroup到IdentableEntity的继承,改为Group到IdentableEntity的继承。

4. 对图2的ORM映射

4.1 单表继承体系

采用单表继承体系映射时,我们用一个AllInOne表包含所有实体的所有字段,并用一个FilterMark过滤标志,区分该条纪录的类型,注意只有所有的User,包括抽象的User和所有的UserGroup是可以查询的,其余接口仅仅用于概念抽象。

 

      [Table(IsView = true )]
        
public   interface  IdentableEntity : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string Name getset; }
        }


        [Table(IsView
= true )]
        
public   interface  Loginable : IEntity
        
{
            
string LoginId getset; }
        }


        [Table(IsView
= true )]
        
public   interface  PasswordLoginable : Loginable
        
{
            
string Password getset; }
        }


        [Table(IsView 
=   true )]
        
public   interface  PrivilegeAssignable
        
{
            
int PrivilegeOwnerId getset; }
        }


        [Table(
" AllInOne " , AdditionalWhere  =   " FilterMark <= 20 " , IsView  =   true )]
        
public   interface  User : IdentableEntity, PrivilegeAssignable
        
{
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 1 " , AdditionalWhere  =   " FilterMark = 1 " )]
        
public   interface  LocalUser : User, PasswordLoginable
        
{
        }


        [Table(
" AllInOne " , AdditionalInsert = " FilterMark = 2 " , AdditionalWhere = " FilterMark = 2 " )]
        
public   interface  AgentUser : User, Loginable
        
{
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 3 " , AdditionalWhere  =   " FilterMark = 3 " )]
        
public   interface  GhostUser : User
        
{
        }


        [Table(
" AllInOne " , AdditionalInsert  =   " FilterMark = 21 " , AdditionalWhere  =   " FilterMark = 21 " )]
        
public   interface  UserGroup : IdentableEntity, PrivilegeAssignable
        
{
            
string Comment getset; }
        }

4.2 一实体一具体表

采用一实体一具体表思路进行映射时,要为每个继承层次中的具体的实体定义一个重复包含所有父类字段的表,并在保存数据时,同时,更新字表和所有的父表。表示抽象契约的接口,在每个实现类包含一份重复字段定义,不需为这样的接口定义独立的表。

   [Table(IsView=true )]
        
public interface
 IdentableEntity : IEntity
        
{
            [PrimaryKey]
            
int Id get; }

            
string Name getset; }
        }


        [Table(IsView
=true )]
        
public interface
 Loginable : IEntity
        
{
            
string LoginId getset; }

        }


        [Table(IsView
=true )]
        
public interface
 PasswordLoginable : Loginable
        
{
            
string Password getset; }

        }


        [Table(IsView 
= true )]
        
public interface
 PrivilegeAssignable
        
{
            
int PrivilegeOwnerId getset; }

        }


        [Table(
"User" )]
        
public interface
 User : IdentableEntity, PrivilegeAssignable
        
{
        }


        [Table(
"LocalUser" )]
        
public interface
 LocalUser : User, PasswordLoginable
        
{
        }


        [Table(
"AgentUser" )]
        
public interface
 AgentUser : User, Loginable
        
{
        }


        [Table(
"GhostUser" )]
        
public interface
 GhostUser : User
        
{
        }


        [Table(
"UserGroup" )]
        
public interface
 UserGroup : IdentableEntity, PrivilegeAssignable
        
{
            
string Comment getset; }

        }

4.3 一实体一扩展表

 

代码6
        [Table(IsView
= true )]
        
public   interface  IdentableEntity : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string Name getset; }
        }


        [Table(IsView
= true )]
        
public   interface  Loginable : IEntity
        
{
            
string LoginId getset; }
        }


        [Table(IsView
= true )]
        
public   interface  PasswordLoginable : Loginable
        
{
            
string Password getset; }
        }


        [Table(IsView 
=   true )]
        
public   interface  PrivilegeAssignable
        
{
            
int PrivilegeOwnerId getset; }
        }


        [Table(
" User " )]
        
public   interface  UserExtended : IdentableEntity, PrivilegeAssignable
        
{
            [PrimaryKey]
            
new int Id get; }
        }


        [Table(
" User " , IsView = true )]
        
public   interface  User : UserExtended
        
{
        }


        [Table(
" LocalUser " )]
        
public   interface  LocalUserExtended : PasswordLoginable
        
{
            [PrimaryKey]
            
int Id get; }
        }


        [Table(
" select * from LocalUser inner join User on LocalUser.Id = User.Id " , IsView = true )]
        
public   interface  LocalUser : LocalUserExtended, User
        
{
            [PrimaryKey]
            
new int Id get; }
        }


        [Table(
" AgentUser " )]
        
public   interface  AgentUserExtended : Loginable
        
{
            [PrimaryKey]
            
int Id get; }
        }


        [Table(
" select * from AgentUser inner join User on AgentUser.Id = User.Id " , IsView  =   true )]
        
public   interface  AgentUser : AgentUserExtended, User
        
{
        }


        [Table(
" GhostUser " )]
        
public   interface  GhostUserExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
        }


        [Table(
" select * from GhostUser inner join User on GhostUser.Id = User.Id " , IsView  =   true )]
        
public   interface  GhostUser : GhostUserExtended, User
        
{
        }


        [Table(
" UserGroup " )]
        
public   interface  UserGroupExtended : IdentableEntity, PrivilegeAssignable
        
{
            [PrimaryKey]
            
new int Id get; }
            
string Comment getset; }
        }


        [Table(
" UserGroup " , IsView = true )]
        
public   interface  UserGroup : UserGroupExtended
        
{
        }
5. 使用映射的实体

对于以上各种方案定义的实体,我们都可以使用NBear.Data.Gateway和NBear.Data.ActiveEntity类进行查询和更新。详细信息请参考 NBear中文用户手册

另外, 注意,所有代码示例中的select *在实际使用环境请用字段名列表代替,以避免可能的数据表字段名重名错误。

[Table( " PrivilegeOwner " )]
        
public   interface  PrivilegeOwnerExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string Name getset; }
        }


        [Table(
" PrivilegeOwner " , IsView = true )]
        
public   interface  PrivilegeOwner : PrivilegeOwnerExtended
        
{
        }


        [Table(
" User " )]
        
public   interface  UserExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
        }


        [Table(
" select * from User inner join PrivilegeOwner on PrivilegeOwner.Id = User.Id " , IsView = true )]
        
public   interface  User : UserExtended, PrivilegeOwnerExtended
        
{
        }


        [Table(
" LocalUser " )]
        
public   interface  LocalUserExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string LoginId getset; }
            
string Password getset; }
        }


        [Table(
" select * from LocalUser inner join User on User.Id = LocalUser.Id inner join PrivilegeOwner on PrivilegeOwner.Id = LocalUser.Id " , IsView = true )]
        
public   interface  LocalUser : LocalUserExtended, User
        
{
            [PrimaryKey]
            
new int Id get; }
        }


        [Table(
" AgentUser " )]
        
public   interface  AgentUserExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string LoginId getset; }
        }


        [Table(
" select * from AgentUser inner join User on User.Id = AgentUser.Id inner join PrivilegeOwner on PrivilegeOwner.Id = AgentUser.Id " , IsView  =   true )]
        
public   interface  AgentUser : AgentUserExtended, User
        
{
            [PrimaryKey]
            
new int Id get; }
        }


        [Table(
" GhostUser " )]
        
public   interface  GhostUserExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
        }


        [Table(
" select * from GhostUser inner join User on User.Id = GhostUser.Id inner join PrivilegeOwner on PrivilegeOwner.Id = GhostUser.Id " , IsView = true )]
        
public   interface  GhostUser : GhostUserExtended, User
        
{
            [PrimaryKey]
            
new int Id get; }
        }


        [Table(
" UserGroup " )]
        
public   interface  UserGroupExtended : IEntity
        
{
            [PrimaryKey]
            
int Id get; }
            
string Comment getset; }
        }


        [Table(
" select * from UserGroup inner join PrivilegeOwner on PrivilegeOwner.Id = UserGroup.Id " , IsView = true )]
        
public   interface  UserGroup : UserGroupExtended, PrivilegeOwner
        
{
            [PrimaryKey]
            
new int Id get; }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值