Skip to content

2. Spring 与 NHibernate 的集成

Spring框架提供了用于与NHibernate框架交互的工具类。使用这些类可以简化SGBD数据访问代码的编写。考虑以下多层架构:

接下来,我们将通过注释一个可运行的解决方案的代码,构建一个基于 [dao] 的 [Spring / NHibernate] 层。 我们不会详尽介绍 [Spring / Nhibernate] 框架的所有配置或使用方法。读者可参考 Spring.NET [Spring.NET | Homepage ](2011 年 12 月)的文档,将所提出的解决方案调整以适应自身需求。

2.1. [dao]数据访问层

该数据库即第1.2节中已介绍的MySQL [dbpam_nhibernate]数据库。[dao]层实现了以下C#接口:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // 所有员工身份信息列表 
        Employe[] GetAllIdentitesEmployes();
        // 特定员工及其津贴 
        Employe GetEmploye(string ss);
        // 所有缴费明细 
        Cotisations GetCotisations();
    }
}

2.1.1. [dao] 层的 Visual Studio C# 项目

[dao] 层的 Visual Studio 项目如下:

  • 在 [1] 中,整个项目
    • 文件夹 [pam] 包含项目的类以及 NHibernate 实体的配置
    • 文件 [App.config] 和 [Dao.xml] 用于配置 Spring 框架 / NHibernate。我们将详细说明这两个文件的内容。
  • 在 [2] 中,包含项目的各个类
    • 在 [entites] 文件夹中,我们发现了前一个项目中研究过的 NHibernate 实体(参见第 14 页)
    • 在文件夹 [service] 中,我们找到了接口 [IPamDao] 及其基于 Spring 框架的实现 NHibernate 和 [PamDaoSpringNHibernate]。
    • 文件夹 [tests] 包含接口 [IPamDao] 的测试用例。
  • 在 [3] 中,包含项目引用。Spring / NHibernate 的集成需要新的 DLL 和 [4]。

在项目 [3] 的引用中,包含以下 DLL:

  • NHibernate:用于 ORM 和 NHibernate
  • MySql.Data:SGBD 的驱动程序 ADO.NET MySQL 5
  • Spring.Core:用于 Spring 框架,负责各层的集成
  • log4net:日志库
  • nunit.framework:单元测试库
  • Spring.Aop、Spring.Data Spring.Data.NHibernate32:确保对 Spring / NHibernate 的支持。

我们将确保所有这些 DLL 的“本地副本”属性均指向 True

2.1.2. C# 项目的配置

项目配置如下:

  • 在 [1] 中,该项目的程序集名称为 [pam-dao-spring-nhibernate]。该名称出现在项目的各种配置文件中。

2.1.3. [dao] 层的实体

[dao]图层所需的实体(对象)已汇总到项目中的[entites] [1]文件夹中。 这些实体与前一个项目中的相同(参见第 1.3.2 节),唯一的区别在于它们位于 NHibernate 配置文件中。以文件 [Employe.hbm.xml] 为例:

  • 在 [2] 中,该文件被配置为嵌入到项目的程序集(assembly)中

其内容如下:


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-spring-nhibernate">
    <class name="Employe" table="EMPLOYES">
        <id name="Id" column="ID">
            <generator class="native" />
        </id>
        <version name="Version" column="VERSION"/>
        <property name="SS" column="SS" length="15" not-null="true" unique="true"/>
        <property name="Nom" column="NOM" length="30" not-null="true"/>
        <property name="Prenom" column="PRENOM" length="20" not-null="true"/>
        <property name="Adresse" column="ADRESSE" length="50" not-null="true" />
        <property name="Ville" column="VILLE" length="30" not-null="true"/>
        <property name="CodePostal" column="CP" length="5" not-null="true"/>
        <many-to-one name="Indemnites" column="INDEMNITE_ID" cascade="all" lazy="false"/>
    </class>
</hibernate-mapping>
  • 第 3 行:assembly 属性表明文件 [Employe.hbm.xml] 将位于程序集 [pam-dao-spring-nhibernate] 中

此外,在 [entites] 文件夹中,我们发现了一个该项目使用的异常类:


using System;
namespace Pam.Dao.Entites {

    public class PamException : Exception {

        // 错误代码 
        public int Code { get; set; }

        // 构建器 
        public PamException() {
        }

        public PamException(int Code)
            : base() {
            this.Code = Code;
        }

        public PamException(string message, int Code)
            : base(message) {
            this.Code = Code;
        }

        public PamException(string message, Exception ex, int Code)
            : base(message, ex) {
            this.Code = Code;
        }
    }
}

类 [PamException] 继承自类 [Exception](第 4 行),并为其添加了一个错误代码(第 7 行)。

2.1.4. Spring 配置 / NHibernate

回到 Visual C# 项目:

  • 在 [1] 中,文件 [App.config] 和 [Dao.xml] 用于配置 Spring 集成 / NHibernate

2.1.4.1. 文件 [App.config]

文件 [App.config] 内容如下:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- 配置部分 -->
    <configSections>
        <sectionGroup name="spring">
            <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
        </sectionGroup>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
    </configSections>


    <!-- Spring 配置 -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>

    <!-- 本节包含 log4net 配置设置 -->
    <!-- NOTE IMPORTANTE:日志默认处于非活动状态。需通过程序将其启用
    avec l'instruction log4net.Config.XmlConfigurator.Configure();
    ! -->
    <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%-5level %logger - %message%newline" />
            </layout>
        </appender>

        <!-- 将默认日志级别设置为 DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>

        <!-- 为 Spring 设置日志记录。Spring 中的日志器名称对应于命名空间 -->
        <logger name="Spring">
            <level value="INFO" />
        </logger>

        <logger name="Spring.Data">
            <level value="DEBUG" />
        </logger>

        <logger name="NHibernate">
            <level value="DEBUG" />
        </logger>
    </log4net>

</configuration>

上述文件 [App.config] 配置了 Spring(第 5-9 行、第 15-22 行)和 log4net(第 10 行、第 28-53 行),但未配置 NHibernate。 Spring 对象并非在 [App.config] 中配置,而是在文件 [Dao.xml](第 20 行)中配置。因此,Spring / NHibernate 的配置(即声明特定 Spring 对象)将在此文件中找到。

2.1.4.2. 文件 [Dao.xml]

汇总了由 Spring 管理的对象的文件 [Dao.xml] 如下:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">

    <!-- 由主应用上下文配置文件引用 -->
    <description>
        Application Spring / NHibernate
    </description>

    <!-- 数据库和 NHibernate 配置 -->
    <db:provider id="DbProvider"
                   provider="MySql.Data.MySqlClient"
                   connectionString="Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;"/>

    <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate32">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="MappingAssemblies">
            <list>
                <value>pam-dao-spring-nhibernate</value>
            </list>
        </property>
        <property name="HibernateProperties">
            <dictionary>
                <entry key="dialect" value="NHibernate.Dialect.MySQL5Dialect"/>
                <entry key="hibernate.show_sql" value="false"/>
            </dictionary>
        </property>
        <property name="ExposeTransactionAwareSessionFactory" value="true" />
    </object>

    <!-- 事务管理器 -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>

    <!-- Hibernate 模板 -->
    <object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
        <property name="SessionFactory" ref="NHibernateSessionFactory" />
        <property name="TemplateFlushMode" value="Auto" />
        <property name="CacheQueries" value="true" />
    </object>

    <!-- 数据访问对象 -->
    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
    </object>
</objects>
  • 第 11-13 行配置了与数据库 [dbpam_nhibernate] 的连接。其中包含:
    • 连接所需的提供程序 ADO.NET,此处为 SGBD 的提供程序 MySQL。 这意味着项目引用中必须包含 DLL 和 [Mysql.Data]。
    • 数据库连接字符串(服务器、数据库名、连接所有者及其密码)
  • 第 15-29 行配置了 SessionFactory 的 NHibernate,该对象用于获取 NHibernate 会话。 需要提醒的是,所有数据库操作均在 NHibernate 会话内进行。 第15行可见,SessionFactory由位于DLL中的Spring类Spring.Data.NHibernate.LocalSessionFactoryObject实现。
  • 第 16 行:属性 DbProvider 设置了数据库连接参数(提供程序 ADO.NET 和连接字符串)。此处该属性引用了之前在第 11-13 行定义的对象 DbProvider
  • 第 17-20 行:指定包含 [*.hbm.xml] 文件的程序集列表,这些文件配置了由 NHibernate 管理的实体。第 19 行表明这些文件将位于项目程序集中。 请注意,该名称可在 C# 项目的属性中找到。另请注意,所有 [*.hbm.xml] 文件均已配置为包含在项目程序集内。
  • 第 22-27 行:NHibernate 的特定属性。
    • 第 24 行:将使用的 SQL 方言为 MySQL
    • 第 25 行:由 NHibernate 生成的 SQL 不会出现在控制台日志中。 将此属性设置为 true,即可查看由 NHibernate 发出的 SQL 命令。这有助于理解,例如,应用程序在访问数据库时为何运行缓慢。
  • 第 28 行:将 ExposeTransactionAwareSessionFactory 属性设置为 true,将使 Spring 管理在 C# 代码中找到的事务管理注解。我们在编写实现 [dao] 层的类时会再次讨论这一点。
  • 第 32-36 行定义了事务管理器。同样,该管理器是 DLL 中的一个 Spring 类。 该处理器需要知道数据库连接参数(第 34 行)以及 SessionFactory 的 NHibernate(第 35 行)。
  • 第39-43行定义了HibernateTemplate类的属性,该类同样是Spring类。该类将在实现[dao]层的类中作为辅助类使用。它简化了与NHibernate对象的交互。 该类具有一些需要初始化的属性:
    • 第 40 行:NHibernate 的 SessionFactory
    • 第 41 行:TemplateFlushMode 属性用于设置持久化上下文 NHibernate 与数据库的同步模式。Auto 模式将触发以下同步操作:
      • 事务结束时
      • 执行操作之前
    • 第 42 行:HQL(Hibernate 查询语言)查询将被缓存。这可能带来性能提升。
  • 第 46-48 行定义了 [dao] 层的实现类
    • 第 46 行:[dao] 层将由 DLL 中的 [PamdaoSpringNHibernate] 类实现。 类实例化后,该类的 init 方法将立即执行。Spring 容器关闭时,该类的 destroy 方法将被执行。
    • 第 47 行:类 [PamDaoSpringNHibernate] 将拥有一个属性 HibernateTemplate,该属性将使用第 39 行的属性 HibernateTemplate 进行初始化。

2.1.5. [dao] 层的实现

2.1.5.1. 实现类的骨架

接口 [IPamDao] 如下所示:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // 所有员工身份列表 
        Employe[] GetAllIdentitesEmployes();
        // 特定员工及其津贴 
        Employe GetEmploye(string ss);
        // 所有缴费的列表 
        Cotisations GetCotisations();
    }
}
  • 第 1 行:导入 [dao] 层实体的命名空间。
  • 第 3 行:[dao] 层位于 [Pam.Dao.Service] 命名空间中。 命名空间 [Pam.Dao.Entites] 中的元素可以创建多个实例。命名空间 [Pam.Dao.Service] 中的元素仅创建一个实例(单例)。这正是选择这些命名空间名称的依据。
  • 第4行:该接口名为[IPamDao]。它定义了三个方法:
    • 第 6 行,[GetAllIdentitesEmployes] 返回一个 [Employe] 类型的对象数组,该数组以简化形式(姓、名、SS)表示保育员列表。
    • 第 8 行,[GetEmploye] 返回一个 [Employe] 对象:该对象包含作为方法参数传入的社会保险号对应的员工及其与指数相关的津贴。
    • 第10行,[GetCotisations] 返回对象 [Cotisations],该对象封装了从毛工资中扣除的各项社会保险费率。

使用 Spring / NHibernate 支持实现该接口的类骨架可能如下:


using System;
using System.Collections;
using System.Collections.Generic;
using Pam.Dao.Entites;
using Spring.Data.NHibernate.Generic.Support;
using Spring.Transaction.Interceptor;

namespace Pam.Dao.Service {
    public class PamDaoSpringNHibernate : HibernateDaoSupport, IPamDao {
        // 私有字段 
        private Cotisations cotisations;
        private Employe[] employes;

        // 初始化 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }

        // 删除对象
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }

        // 所有员工身份信息列表
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }


        // 特定员工及其津贴 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }

        // 缴费清单 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • 第 9 行:类 [PamDaoSpringNHibernate] 正确实现了 [dao] [IPamDao] 层的接口。它还继承自 Spring 类 [HibernateDaoSupport]。 该类有一个 [HibernateTemplate] 属性,该属性由已完成的 Spring 配置(下文第 2 行)初始化:

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • 在上文第 1 行中,我们可以看到 [pamdao] 对象的定义表明,[PamDaoSpringNHibernate] 类的 init destroy 方法必须在特定时刻执行。 这两个方法确实存在于该类的第16行和第21行。
  • 第 15、34 行:这些注解确保被注解的方法将在事务中执行。属性 ReadOnly=true 表示该事务为只读事务。 在事务中执行的方法可能会抛出异常。此时,Spring 会自动对事务进行回滚。该注解消除了在方法内部管理事务的必要性。
  • 第 16 行:类实例化完成后,Spring 会立即执行 init 方法。我们将看到,该方法的目的是初始化第 11 行和第 12 行中的私有字段。该方法将在事务中执行(第 15 行)。
  • 接口 [IPamDao] 的方法分别在第 28、35 和 40 行实现。
  • 第 28-30 行:方法 [GetAllIdentitesEmployes] 仅将第 12 行中由方法 init 初始化的属性返回。
  • 第40-42行:方法[GetCotisations]仅将第11行的属性返回,该属性由方法init初始化。

2.1.5.2. HibernateTemplate 类的实用方法

我们将使用 HibernateTemplate 类的以下方法:

IList<T> Find<T>(string requete_hql)
执行查询 HQL 并返回一个 T 类型的对象列表
IList<T> Find<T>(string requete_hql, object[])
执行由 ? 参数化的 HQL 查询。这些参数的值由对象数组提供。
IList<T> LoadAll<T>()
返回所有类型为 T 的实体

还有其他一些有用的方法,虽然我们没有机会使用,但它们允许检索、保存、更新和删除实体:

T Load<T>(object id)
将主键为 id 的类型 T 实体放入会话 NHibernate 中。
void SaveOrUpdate(object entité)
根据对象 entité 是否具有主键(UPDATE),分别插入(INSERT)或更新(UPDATE)该对象 (INSERT)。是否设置主键可通过实体配置文件中的 unsaved-values 属性进行配置。 在执行操作 SaveOrUpdate 之后,对象 entité 位于会话 NHibernate 中。
void Delete(object entité)
从会话 NHibernate 中删除对象 entité

2.1.5.3. init 方法的实现

类 [PamDaoSpringNHibernate] 中的方法 init 根据配置,是在 Spring 实例化该类后执行的方法。 该方法旨在将员工的简化身份信息(姓、名、SS)和缴费率缓存到本地。其代码可能如下所示。


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // 获取员工简化列表
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // 将其放入表格中
                employes = new Employe[lignes.Count];
                int i = 0;
                foreach (object[] ligne in lignes) {
                    employes[i] = new Employe() { SS = ligne[0].ToString(), Nom = ligne[1].ToString(), Prenom = ligne[2].ToString() };
                    i++;
                }
                // 将缴费率放入一个对象中 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // 处理异常 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • 第 5 行:执行 HQL 查询。该查询获取所有 Employé 实体的 SS、姓、名字段,并返回一个对象列表。 如果使用“select e from Employe e”的形式查询员工完整信息,则会返回一个类型为 Employe 的对象列表。
  • 第 7-12 行:将该对象列表复制到一个类型为 Employe 的对象数组中。
  • 第 14 行:查询所有类型为 Cotisations 的实体列表。已知该列表仅包含一个元素。因此,我们获取列表中的第一个元素以获取缴费率。
  • 第 7 行和第 14 行初始化了该类的两个私有字段。

2.1.5.4. 方法 GetEmploye 的实现

方法 GetEmploye 需返回具有给定编号 SS 的员工实体。其代码可能如下:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // 查询
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // 转换异常 
                throw new PamException(string.Format("Erreur d'accès à la BD lors de la demande de l'employé de n° ss [{0}] : [{1}]", ss, ex.ToString()), 41);
            }
            // 是否已检索到员工? 
            if (employés.Count == 0) {
                // 报告该情况 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • 第 6 行:获取具有给定编号 SS 的员工列表
  • 第 12 行:通常如果该员工存在,应返回一个包含一个元素的列表
  • 第 14 行:若不存在,则抛出异常
  • 第16行:如果是这种情况,则返回列表中的第一位员工

2.2. [dao] 层测试

2.2.1. Visual Studio 项目

Visual Studio 项目此前已介绍过。在此回顾一下:

  • 在 [1] 中,该项目整体
  • 在 [2] 中,是项目的各个类。文件夹 [tests] 包含一个控制台测试 [Main.cs] 和一个单元测试 [NUnit.cs]。
  • 在 [3] 中,编译了程序 [Main.cs]。
  • 在 [4] 中,未生成文件 [NUnit.cs]。
  • 该项目是一个控制台应用程序。执行的类是 [5] 中指定的类,即 [Main.cs] 文件中的类。

2.2.2. 控制台测试程序 [Main.cs]

测试程序 [Main.cs] 在以下架构下运行:

其负责测试 [IPamDao] 接口的方法。一个基本示例如下:


using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;

namespace Pam.Dao.Tests {
    public class MainPamDaoTests {
        public static void Main() {
            try {
                // 实例化 [dao] 层
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // 员工身份列表 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // 一名员工及其津贴 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // 缴费清单 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // 异常显示 
                Console.WriteLine(ex.ToString());
            }
            //暂停 
            Console.ReadLine();
        }
    }
}
  • 第 11 行:向 Spring 请求 [dao] 层的引用。
  • 第13-15行:测试接口[IPamDao]中的方法[GetAllIdentitesEmployes]
  • 第 18 行:测试接口 [IPamDao] 中的方法 [GetEmploye]
  • 第 21 行:测试接口 [IPamDao] 中的方法 [GetCotisations]

SpringNHibernatelog4net 由第 2.1.4.1 节中讨论的文件 [App.config] 进行配置。

使用第 1.2 节所述的数据库进行执行,将得到以下控制台输出:

1
2
3
4
5
6
[254104940426058,Jouveinal,Marie,,,,]
[260124402111742,Laverti,Justine,,,,]
------------------------------------
[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]]
------------------------------------
[3,49,6,15,9,39,7,88]
  • 第 1-2 行:2 名类型为 [Employe] 的员工,仅包含 [SS, Nom, Prenom] 的信息
  • 第4行:类型为[Employe]的员工,其社会保险号为[254104940426058]
  • 第5行:缴费率

2.2.3. 使用 NUnit 进行单元测试

现在我们进行 NUnit 的单元测试。[dao] 层的 Visual Studio 项目将按以下方式进行调整:

  • 为 [1],测试程序 [NUnit.cs]
  • 将更新为 [2,3],该项目将生成一个名为 [pam-dao-spring-nhibernate.dll] 的 DLL
  • 在 [4] 中,对框架 NUnit 中 DLL 的引用:[nunit.framework.dll]
  • 在 [5] 中,类 [Main.cs] 将不会包含在 DLL [pam-dao-spring-nhibernate]
  • 在 [6] 中,类 [NUnit.cs] 将包含在 DLL [pam-dao-spring-nhibernate] 中

测试类 NUnit 如下:


using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;

namespace Pam.Dao.Tests {

    [TestFixture]
    public class NunitPamDao : AssertionHelper {
        // 待测试的 [dao] 层 
        private IPamDao pamDao = null;

        // 构造函数 
        public NunitPamDao() {
            // [dao] 层的实例化
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // 初始化 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // 员工人数验证 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // 缴费率核查 
            Cotisations cotisations = pamDao.GetCotisations();
            Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
            Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
            Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
            Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites() {
            // 个人核验 
            Employe employe1 = pamDao.GetEmploye("254104940426058");
            Employe employe2 = pamDao.GetEmploye("260124402111742");
            Expect("Jouveinal", EqualTo(employe1.Nom));
            Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
            Expect("Laverti", EqualTo(employe2.Nom));
            Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites2() {
            // 不存在的个人核查 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}
  • 第 11 行:该类具有 [TestFixture] 属性,这使其成为测试类 [NUnit]。
  • 第 12 行:该类继承自 NUnit 框架(自 2.4.6 版本起)中的辅助类 AssertionHelper
  • 第 14 行:私有字段 [pamDao] 是访问层接口 [dao] 的一个实例。 需要注意的是,该字段的类型是接口而非。这意味着 [pamDao] 实例仅提供接口 [IPamDao] 中的方法。
  • 在该类中被测试的方法是具有 [Test] 属性的方法。对于所有这些方法,测试流程如下:
    • 首先执行带有 [SetUp] 属性的方法。该方法用于准备测试所需的资源(网络连接、数据库连接等)。
    • 随后执行待测方法
    • 最后执行带有属性 [TearDown] 的方法。该方法通常用于释放由属性为 [SetUp] 的方法所占用的资源。
  • 在我们的测试中,每个测试之前无需分配资源,之后也无需释放资源。因此,我们不需要带有 [SetUp] 和 [TearDown] 属性的方法。 在示例中,我们在第 23-26 行展示了一个带有 [SetUp] 属性的方法。
  • 第17-20行:类构造函数利用Spring和[App.config]初始化私有字段[pamDao]。
  • 第 29-32 行:测试方法 [GetAllIdentitesEmployes]
  • 第 35-42 行:测试方法 [GetCotisations]
  • 第 45-53 行:测试方法 [GetEmploye]
  • 第 56-65 行:在发生异常时测试方法 [GetEmploye]。

项目生成会在 [bin/Release] 文件夹中创建 DLL [pam-dao-spring-nhibernate.dll]:

使用版本为2.5的[NUnit-Gui]工具加载DLL和[pam-dao-spring-nhibernate.dll],并执行测试:

如上所示,测试已成功完成。

2.2.4. 生成 ,并从[dao]层生成DLL

编写并测试完类 [PamDaoNHibernate] 后,将按以下方式生成来自 [dao] 层的 DLL:

  • [1],测试程序不包含在项目汇编中
  • [2,3],项目配置
  • [4],生成项目
  • DLL 生成在 [bin/Release] [5] 文件夹中。