Skip to content

2. Spring/NHibernate 集成

Spring 框架提供了用于与 NHibernate 框架交互的工具类。使用这些类可以更轻松地编写从数据库管理系统中访问数据的代码。请考虑以下多层架构:

在接下来的章节中,我们将通过注释一个可运行的解决方案的代码,使用 [Spring / NHibernate] 构建 [DAO] 层。我们不会试图涵盖 [Spring / NHibernate] 框架的所有可能配置或用法。读者可以借助 Spring.NET 文档 [Spring.NET | 主页](2011 年 12 月)将提出的解决方案调整为适合自身需求。

2.1. [DAO] 数据访问层

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


using Pam.Dao.Entites;
 
namespace Pam.Dao.Service {
    public interface IPamDao {
         // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();
         // an individual employee with benefits 
        Employe GetEmploye(string ss);
         // list of all contributions 
        Cotisations GetCotisations();
    }
}

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

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

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

在项目引用 [3] 中,我们发现以下 DLL:

  • NHibernate:用于 NHibernate ORM
  • MySql.Data:用于 MySQL 5 数据库管理系统(DBMS)的 ADO.NET 驱动程序
  • 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] 层所需的实体(对象)已汇总在项目的 [entities] 文件夹 [1] 中。这些实体与前一个项目中的实体相同(参见第 1.3.2 节),但 NHibernate 配置文件中存在一个差异。以 [Employee.hbm.xml] 文件为例:

  • 在 [2] 中,该文件被配置为包含在项目程序集内

其内容如下:


<?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] 中

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


using System;
namespace Pam.Dao.Entites {
 
    public class PamException : Exception {
 
         // the error code 
        public int Code { get; set; }
 
         // manufacturers 
        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>
     <!-- configuration sections -->
    <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 configuration -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>
 
     <!-- This section contains the log4net configuration settings -->
     <!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
    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>
 
         <!-- Set default logging level to DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>
 
         <!-- Set logging for Spring . Logger names in Spring correspond to the namespace -->
        <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 对象声明的 Spring/NHibernate 配置将位于此文件中。

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">
 
     <!-- Referenced by main application context configuration file -->
    <description>
        Application Spring / NHibernate
    </description>
 
     <!-- Database and NHibernate Configuration -->
    <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>
 
     <!-- transaction manager -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>
 
     <!-- Hibernate Template -->
    <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>
 
     <!-- Data Access Objects -->
    <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 提供程序,本例中为 MySQL 数据库管理系统 (DBMS) 提供程序。这要求在项目引用中包含 [Mysql.Data] DLL。
    • 数据库连接字符串(服务器、数据库名称、连接所有者和密码)
  • 第 15–29 行配置了 NHibernate SessionFactory,该对象用于获取 NHibernate 会话。请注意,所有数据库操作均在 NHibernate 会话内执行。在第 15 行中,我们可以看到 SessionFactory 由 Spring 类 Spring.Data.NHibernate.LocalSessionFactoryObject 实现该类位于 Spring.Data.NHibernate32 DLL 中。
  • 第 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 行定义了事务管理器。同样,该管理器是来自 Spring.Data.NHibernate32 DLL 的 Spring 类。该管理器需要数据库连接参数(第 34 行)以及 NHibernate SessionFactory(第 35 行)。
  • 第 39–43 行定义了 HibernateTemplate 类的属性,该类也是 Spring 类。该类将在实现 [DAO] 层的类中作为辅助类使用,用于简化与 NHibernate 对象的交互。该类包含一些必须初始化的属性:
    • 第 40 行:NHibernate 的 SessionFactory
    • 第 41 行:TemplateFlushMode 属性用于设置 NHibernate 持久化上下文与数据库的同步模式。Auto 模式确保同步操作发生在:
      • 在事务结束时
      • 在执行 SELECT 操作之前
    • 第 42 行:HQL(Hibernate 查询语言)查询将被缓存。这可以带来性能提升。
  • 第 46–48 行定义了 [dao] 层的实现类
    • 第 46 行:[dao] 层将由 [pam-dao-spring-nhibernate] 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 {
         // list of all employee identities 
        Employe[] GetAllIdentitesEmployes();
         // an individual employee with benefits 
        Employe GetEmploye(string ss);
         // list of all contributions 
        Cotisations GetCotisations();
    }
}
  • 第 1 行:我们导入了 [dao] 层中实体的命名空间。
  • 第 3 行:[dao] 层位于 [Pam.Dao.Service] 命名空间中。[Pam.Dao.Entities] 命名空间中的元素可以创建多个实例,而 [Pam.Dao.Service] 命名空间中的元素则作为单例(singleton)创建。这正是命名空间命名选择的依据。
  • 第 4 行:接口命名为 [IPamDao]。它定义了三个方法:
    • 第 6 行:[GetAllIdentitesEmployes] 返回一个 [Employe] 类型的对象数组,以简化格式(姓、名、社保号)表示托儿服务提供者的列表。
    • 第 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 fields 
        private Cotisations cotisations;
        private Employe[] employes;
 
         // init 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }
 
         // delete object
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }
 
         // list of all employee identities
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }
 
 
         // an individual employee with benefits 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }
 
         // list of contributions 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • 第 9 行:[PamDaoSpringNHibernate] 类正确实现了 [dao] 层接口 [IPamDao]。它还继承自 Spring 类 [HibernateDaoSupport]。该类有一个 [HibernateTemplate] 属性,该属性由下方第 2 行设置的 Spring 配置进行初始化:

    <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 行:[GetAllIdentitiesEmployees] 方法仅返回第 12 行定义的属性,该属性由 init 方法初始化。
  • 第 40–42 行:[GetCotisations] 方法仅返回第 11 行中的属性,该属性由 init 方法初始化。

2.1.5.2. HibernateTemplate 类的常用方法

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

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

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

T Load<T>(object id)
将主键为 id 的类型为 T 的实体添加到 NHibernate 会话中。
void SaveOrUpdate(object entity)
根据实体对象是否具有主键(有主键则执行 UPDATE,无主键则执行 INSERT),分别对该实体进行插入(INSERT)或更新(UPDATE)操作。是否存在主键可通过实体配置文件中的 `unsaved-values` 属性进行配置。执行 `SaveOrUpdate` 操作后,该实体对象将处于 NHibernate 会话中。
void Delete(object entity)
实体对象从 NHibernate 会话中移除。

2.1.5.3. init 方法的实现

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


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                 // retrieve the simplified list of employees
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                 // we put it in a table
                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++;
                }
                 // we put the contribution rates in a 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // on transforme l'exception 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • 第 5 行:执行了一个 HQL 查询。该查询从所有 Employee 实体中检索 SS、LastName 和 FirstName 字段,并返回一个对象列表。如果我们使用 "select e from Employee e" 查询整个 Employee 记录,则会返回一个 Employee 类型的对象列表。
  • 第 7–12 行:将该对象列表复制到一个 Employee 类型的对象数组中。
  • 第 14 行:我们获取所有类型为 Contributions 的实体列表。我们知道该列表仅包含一个元素。因此,我们获取列表的第一个元素以获取贡献率。
  • 第 7 行和第 14 行初始化了该类的两个私有字段。

2.1.5.4. GetEmployee 方法的实现

GetEmployee 方法必须返回具有给定 SSN 的 Employee 实体。其代码如下所示:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                 // request
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // on transforme l'exception 
                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);
            }
             // has an employee been recovered? 
            if (employés.Count == 0) {
                 // we report the fact 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • 第 6 行:检索具有给定社保号的员工列表
  • 第 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 {
                 // layer instantiation [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                 // list of employee identities 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                 // an employee with benefits 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                 // list of contributions 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                 // exception display 
                Console.WriteLine(ex.ToString());
            }
             //break 
            Console.ReadLine();
        }
    }
}
  • 第 11 行:我们向 Spring 请求 [dao] 层的引用。
  • 第 13–15 行:测试 [IPamDao] 接口的 [GetAllEmployeeIDs] 方法
  • 第 18 行:测试 [IPamDao] 接口的 [GetEmployee] 方法
  • 第 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名类型为[Employee]的员工,仅包含[SS, 姓, 名]信息
  • 第4行:类型为[Employee]、社会保险号为[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] 类将不会被包含在 [pam-dao-spring-nhibernate] DLL 中
  • 在 [6] 中,[NUnit.cs] 类将被包含在 [pam-dao-spring-nhibernate] DLL 中

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 {
         // the [dao] layer to be tested 
        private IPamDao pamDao = null;
 
         // manufacturer 
        public NunitPamDao() {
             // layer instantiation [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }
 
         // init 
        [SetUp]
        public void Init() {
 
        }
 
        [Test]
        public void GetAllIdentitesEmployes() {
             // audit no. of employees 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }
 
        [Test]
        public void GetCotisations() {
             // checking contribution rates 
            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() {
             // individual verification 
            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() {
             // non-existent individual verification 
            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 行:测试 [GetAllIdentitiesEmployees] 方法
  • 第 35–42 行:测试 [GetCotisations] 方法
  • 第 45–53 行:测试 [GetEmploye] 方法
  • 第 56–65 行:测试 [GetEmploye] 方法在发生异常时的行为。

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

我们使用 [NUnit-Gui] 工具(版本 2.5)加载 DLL [pam-dao-spring-nhibernate.dll],并运行测试:

Image

如上所示,测试已成功通过。

2.2.4. 使用 生成[dao]层DLL

在编写并测试完 [PamDaoNHibernate] 类后,我们将按以下步骤生成 [dao] 层 DLL:

  • [1],测试程序被排除在项目程序集之外
  • [2,3],项目配置
  • [4],项目生成
  • 生成的 DLL 位于 [bin/Release] 文件夹中 [5]。