多对多关系 及 O/R映射小结

这是一个权限验证系统的一部分。对象包括:帐户(Account)、角色(Role)和资源(Resource)。它们之间的关系是,多个帐户对应一个角色,一个角色可以拥有多个资源,一个资源也和多个角色存在关系。所以有 Account : Role = n : 1 ,Role : Resource = n : m 。本文只介绍 Castle AR 的多对多关系解决方案,所以忽略 Account,只研究 Role 和 Resource。又由于多对多关系中,一方对另一方的操作在另一方的角度而言在脱离数据概念时是等价的,所以本例只使用 Role 对象作测试。

数据库结构设计



本例中,两个对象通过一个关联表实现多对多关系,因此实际上有三个数据表。SQL Server 2000 生成的 SQL 建表脚本如下:

 

None.gif CREATE   TABLE   [ WebFolder_Resource ]  (
None.gif    
[ ResourceID ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
None.gif    
[ ResourceName ]   [ varchar ]  ( 50 ) COLLATE Chinese_PRC_CI_AS  NOT   NULL  ,
None.gif    
[ RequestName ]   [ varchar ]  ( 50 ) COLLATE Chinese_PRC_CI_AS  NOT   NULL  ,
None.gif    
[ ShowOrder ]   [ varchar ]  ( 10 ) COLLATE Chinese_PRC_CI_AS  NOT   NULL  
None.gif
ON   [ PRIMARY ]
None.gif
GO
None.gif
None.gif
CREATE   TABLE   [ WebFolder_Role ]  (
None.gif    
[ RoleID ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
None.gif    
[ RoleName ]   [ varchar ]  ( 50 ) COLLATE Chinese_PRC_CI_AS  NOT   NULL  ,
None.gif    
[ RoleDescription ]   [ varchar ]  ( 50 ) COLLATE Chinese_PRC_CI_AS  NOT   NULL  
None.gif
ON   [ PRIMARY ]
None.gif
GO
None.gif
None.gif
CREATE   TABLE   [ WebFolder_RoleResource ]  (
None.gif    
[ RoleID ]   [ int ]   NOT   NULL  ,
None.gif    
[ ResourceID ]   [ int ]   NOT   NULL  
None.gif
ON   [ PRIMARY ]
None.gif
GO
None.gif
None.gif
ALTER   TABLE   [ WebFolder_Resource ]   ADD  
None.gif    
CONSTRAINT   [ PK_WebFolder_Resource ]   PRIMARY   KEY    CLUSTERED  
None.gif    (
None.gif        
[ ResourceID ]
None.gif    )  
ON   [ PRIMARY ]  
None.gif
GO
None.gif
None.gif
ALTER   TABLE   [ WebFolder_Role ]   ADD  
None.gif    
CONSTRAINT   [ PK_WebFolder_Role ]   PRIMARY   KEY    CLUSTERED  
None.gif    (
None.gif        
[ RoleID ]
None.gif    )  
ON   [ PRIMARY ]  
None.gif
GO

依赖的类库

 

You must reference the following set of assemblies to use ActiveRecord:

  • Castle.ActiveRecord.dll
  • Castle.Model.dll
  • Nullables.dll

But ActiveRecord also depends on NHibernate, so you must reference the following as well:

  • NHibernate.dll
  • Castle.DynamicProxy.dll (Curious? Check DynamicProxy)
  • Nullables.NHibernate.dll
  • log4net.dll
  • Iesi.Collections.dll

领域对象

Role.cs (StephenCat.WebFolder.Domain.Role)

 

using  System;
using  System.Collections;

using  Castle.ActiveRecord;

namespace  StephenCat.WebFolder.Domain
{
    
///   <summary>
    
///  Role 的摘要说明。
    
///   </summary>
    [ActiveRecord( " WebFolder_Role " )]
    
public   class  Role : ActiveRecordBase
    {
        
///   <summary>
        
///  创建一个角色
        
///   </summary>
         public  Role()
        {
        }

        
private   int  _id;
        
private  String _name;
        
private  String _description;

        
private  IList _accounts;

        
private  IList _resources;

        
///   <summary>
        
///  角色编号
        
///   </summary>
        [PrimaryKey(PrimaryKeyType.Native,  " RoleID " )]
        
public   int  Id
        {
            
get  {  return  _id; }
            
set  { _id  =  value; }
        }

        
///   <summary>
        
///  角色名称
        
///   </summary>
        [Property( " RoleName " )]
        
public  String Name
        {
            
get  {  return  _name; }
            
set  { _name  =  value; }
        }

        
///   <summary>
        
///  角色说明
        
///   </summary>
        [Property( " RoleDescription " )]
        
public  String Description
        {
            
get  {  return  _description; }
            
set  { _description  =  value; }
        }

        
///   <summary>
        
///  角色所拥有的帐号集合
        
///   </summary>
        
        [HasMany(
typeof (Resource), Table = " Account " , ColumnKey = " RoleID " , Lazy = true )]
        
public  IList Accounts
        {
            
get  {  return  _accounts; }
            
set  { _accounts  =  value; }
        }

        
///   <summary>
        
///  角色所拥有的资源集合
        
///   </summary>
        [HasAndBelongsToMany(  typeof (Resource), 
             Table
= " WebFolder_RoleResource "
             ColumnRef
= " ResourceID " , ColumnKey = " RoleID "  )]
        
public  IList Resources
        {
            
get  {  return  _resources; }
            
set  { _resources  =  value; }
        }


        
///   <summary>
        
///  删除所有角色
        
///   </summary>
         public   static   void  DeleteAll()
        {
            DeleteAll( 
typeof (Role) );
        }

        
///   <summary>
        
///  获取所有角色
        
///   </summary>
        
///   <returns> 角色集合 </returns>
         public   static  Role[] FindAll()
        {
            
return  (Role[]) FindAll(  typeof (Role) );
        }

        
///   <summary>
        
///  获取一个角色
        
///   </summary>
        
///   <param name="id"> 角色编号 </param>
        
///   <returns> 角色实体 </returns>
         public   static  Role Find( int  id)
        {
            
return  (Role) FindByPrimaryKey(  typeof (Role), id );
        }

    }
}

Resource.cs (StephenCat.WebFolder.Domain.Resource)

 

using  System;
using  System.Collections;

using  Castle.ActiveRecord;

namespace  StephenCat.WebFolder.Domain
{
    
///   <summary>
    
///  Resource 的摘要说明。
    
///   </summary>
    [ActiveRecord( " WebFolder_Resource " )]
    
public   class  Resource : ActiveRecordBase
    {
        
///   <summary>
        
///  创建一个资源
        
///   </summary>
         public  Resource()
        {
        }

        
private   int  _id;
        
private  String _name;
        
private  String _requestname;
        
private  String _showorder;

        
private  IList _roles;

        
///   <summary>
        
///  资源编号
        
///   </summary>
        [PrimaryKey(PrimaryKeyType.Native,  " ResourceID " )]
        
public   int  Id
        {
            
get  {  return  _id; }
            
set  { _id  =  value; }
        }

        
///   <summary>
        
///  资源名称
        
///   </summary>
        [Property( " ResourceName " )]
        
public  String Name
        {
            
get  {  return  _name; }
            
set  { _name  =  value; }
        }

        
///   <summary>
        
///  请求名称
        
///   </summary>
        [Property( " RequestName " )]
        
public  String RequestName
        {
            
get  {  return  _requestname; }
            
set  { _requestname  =  value; }
        }

        
///   <summary>
        
///  序号
        
///   </summary>
        [Property( " ShowOrder " )]
        
public  String ShowOrder
        {
            
get  {  return  _showorder; }
            
set  { _showorder  =  value; }
        }

        
///   <summary>
        
///  资源所拥有的角色集合
        
///   </summary>
        [HasAndBelongsToMany(  typeof (Role), 
             Table
= " WebFolder_RoleResource "
             ColumnRef
= " RoleID " , ColumnKey = " ResourceID "  )]
        
public  IList Roles
        {
            
get  {  return  _roles; }
            
set  { _roles  =  value; }
        }

        
///   <summary>
        
///  删除所有资源
        
///   </summary>
         public   static   void  DeleteAll()
        {
            DeleteAll( 
typeof (Resource) );
        }

        
///   <summary>
        
///  获取所有资源
        
///   </summary>
        
///   <returns> 资源集合 </returns>
         public   static  Resource[] FindAll()
        {
            
return  (Resource[]) FindAll(  typeof (Resource) );
        }

        
///   <summary>
        
///  获取一个资源
        
///   </summary>
        
///   <param name="id"> 资源编号 </param>
        
///   <returns> 资源集合 </returns>
         public   static  Resource Find( int  id)
        {
            
return  (Resource) FindByPrimaryKey(  typeof (Resource), id );
        }


    }
}

单元测试

测试工具

TestDriven.NET 2.0 

硬件环境

CPU: Intel Pentium IV 2.6c (support hyper-threading)
MB: Asus P4P800
RAM: 512 mb DDR
HD: Seagate ATA 120G x 2 (no RAID)

软件环境

OS: Microsoft Windows Server 2003 Enterprise Edition with Service Pack 1
IDE: Microsoft Visual Studio.NET 2003 Enterprise Architecture Edition
DB: Microsoft SQL Server 2000 with Service Pack 4
Virtual Memory: 2048 ~ 3096 MB on Drive C and 2048 ~ 3096 MB on Drive D

测试用例及结果

 

using  System;   //  用于控制台输出

using  System.Collections;   //  用于 IList 操作

using  NUnit.Framework;   //  用于单元测试

using  System.Reflection;
using  Castle.ActiveRecord.Framework.Config;   //  用于读取内嵌的 Castle AR 配置

using  Castle.ActiveRecord;   //  用于启动 AR 特性

using  StephenCat.WebFolder.Domain;   //  领域对象

namespace  StephenCat.WebFolder._TestUnitCase
{
    
///   <summary>
    
///  RoleTest 的摘要说明。
    
///   </summary>
    [TestFixture]
    
public   class  RoleTest
    {
        
public  RoleTest()
        {
            
//
            
//  TODO: 在此处添加构造函数逻辑
            
//
        }

        
///   <summary>
        
///  测试创建一个角色
        
///   </summary>
        [Test]
        
public   void  RoleCreateTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            
// using(new SessionScope())
            
// {
                Role role  =   new  Role();

                role.Name 
=   " sysadmin " ;
                role.Description 
=   " administrator1234 " ;

                role.Save();
            
// }
        }

        
///   <summary>
        
///  测试不清除原来记录的情况下向角色添加三个资源(循环 n 次测试)。
        
///   </summary>
        [Test]
        
public   void  RoleAddAgainResourceTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            Role role 
=  Role.Find( 3 );

            role.Resources.Add(Resource.Find(
2 ));
            role.Resources.Add(Resource.Find(
3 ));
            role.Resources.Add(Resource.Find(
5 ));

            role.Update();  
//  循环三次会出现九条记录。AR 不会考虑仅仅保留唯一记录。

            
/*
             * 控制台输出:
            ------ Test started: Assembly: StephenCat.WebFolder.dll ------

            1 passed, 0 failed, 0 skipped, took 2.27 seconds.
             
*/
        }

        
///   <summary>
        
///  测试删除所有角色所拥有的资源
        
///   </summary>
        [Test]
        
public   void  RoleRemoveAllResourceTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            Role role 
=  Role.Find( 3 );

            role.Resources.Clear(); 
//  到这步仍未真正删除所有拥有的资源

            role.Update(); 
//  更新角色信息,真正实施从数据表删除记录。

            Console.WriteLine(role.Resources.Count); 

        }

        
///   <summary>
        
///  测试给角色从无到有地添加资源的作用
        
///   </summary>
        [Test]
        
public   void  RoleAddResourceTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            Role role 
=  Role.Find( 3 );   //  本来就有三个资源
            Console.WriteLine(role.Resources.Count);

            Role role4 
=  Role.Find( 4 );  //  本来没有资源
            Console.WriteLine(role4.Resources.Count);

            role4.Resources.Add(Resource.Find(
2 ));  //  添加一个资源,未真正添加到数据表
            Console.WriteLine(role4.Resources.Count);

            role4.Update(); 
//  真正添加到数据表
             /*
            控制台输出结果:
            
            ------ Test started: Assembly: StephenCat.WebFolder.dll ------
            3
            0
            1
            
            1 passed, 0 failed, 0 skipped, took 2.23 seconds.
            
            
*/
            
        }

        
///   <summary>
        
///  测试更新角色所拥有的资源
        
///   </summary>
        [Test]
        
public   void  RoleUpdateResourceTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            Role role 
=  Role.Find( 3 );   //  本来有三个资源
            Console.WriteLine(role.Resources.Count);

            role.Resources.Clear(); 
//  尝试删除全部所拥有的资源
            role.Resources.Add(Resource.Find( 1 ));  //  添加一个资源
            Console.WriteLine(role.Resources.Count); 

            role.Update(); 
//  更新到数据表
             /*
            控制台输出结果:
            
            ------ Test started: Assembly: StephenCat.WebFolder.dll ------

            3
            1

            1 passed, 0 failed, 0 skipped, took 2.27 seconds.
            
            
*/
        }

        
///   <summary>
        
///  测试角色是否拥有某个资源,使用 ActiveRecord 本来的方法
        
///   </summary>
        [Test]
        
public   void  RoleHasResourceARTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            Role role 
=  Role.Find( 3 );

            
//  遍历角色所拥有的资源
             foreach (Resource res  in  role.Resources)
            {
                Console.WriteLine(res.Id);
            }
            
/*
            控制台输出:
            
            1
            2
            3
            5
            
            
*/

            Console.WriteLine(role.Resources.Contains(Resource.Find(
2 )));  
            
//  由于 GetHasCode() 的值不同,所以返回 False。
            
//  因此不能使用 Contains 方法直接判断对象是否相等。

            
//  通过判断对象的属性是否相等来判断角色是否包含一个资源,这样才会返回 True。
            
            
foreach (Resource res  in  role.Resources)
            {
                
if (res.Id == Resource.Find( 2 ).Id)
                {
                    Console.WriteLine(
true );
                    
break ;
                }
            }
            
//  比较 stupid,最终还是要面向数据库而非面向对象。
            
//  要面向对象的话,只能自己在每个 domain 类里面手写方法了。
        }

        [Test]
        
public   void  RoleRemoveResourceTest()
        {
            Assembly assembly 
=   typeof (Account).Assembly;
            XmlConfigurationSource src 
=   new  XmlConfigurationSource(assembly.GetManifestResourceStream( " StephenCat.WebFolder.Domain.ActiveRecord.config " ));
            ActiveRecordStarter.Initialize( src, 
typeof (Role), typeof (Resource) );

            
//  注意:由于存在关联,这里要初始化两个实体。
            
//        否则会抛出 unmapped: Resource 的 Exception。

            Role role 
=  Role.Find( 3 );

            role.Resources.Remove(Resource.Find(
2 ));
            role.Update();
            
//  这样也是不能真正删除指定的资源的,只能通过循环检查删除了

            
foreach (Resource res  in  role.Resources)
            {
                
if (res.Id == Resource.Find( 2 ).Id)
                {
                    role.Resources.Remove(res);
                    
break ;
                }
            }
            role.Update();  
//  这样才真正删除了。
        }
    }
}

小结

Castle ActiveRecord 只是把数据表的一行数据映射为一个 .NET 类,把多行数据映射为 Array 数组,数组的元素是 .NET 类。尽管 AR 通过 ActiveRecordBase 类也提供了一些 DAO 应该有的方法,但仍然有某些方法需要自己手工编写。

Castle AR 并没有把对象和关系绝对领域化,领域业务方法甚至某些 DAO 方法仍需要自己编写,因此 Castle AR 在封装了 NHibernate 之后也只是完成了对象关系和数据之间的映射工作而已,也仅此而已

转载于:https://www.cnblogs.com/han-xiao-feng/archive/2006/07/30/463269.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值