How-To: Using the N* Stack, part 4

data 工程、 NHibernate.dll 、 FluentNHibernate.dll 和 NUnit.Framework.dll 添加进来。如果你安装了 NUnit , NUnit.Framework.dll 可以在 .NET 选项卡里看到。如果你安装了多个版本的 NUnit 的话,一定要注意选择的版本。

SQL: 在一个方便的旅行装

如果你之前没有听说过 SQLite ,你会喜欢上它的。这是一个很小的、独立的、并且开源的SQL数据库引擎。更好的是,它可以完全的运行在内存当中并且速度非常快。下面是如何设置并且使用它:

  • 下载 SQLite 提供的 ADO.NET Provider 。下载完整版,文件名类似 SQLLit-1.0.65.0-setup.exe。安装完成之后,将 System.Data.Sqlite.dll 拷贝到Solution Items 文件夹,并且添加到你所有工程的引用当中。
  • 下载 SQLite 类库。文件名类似 sqlitedll-3_6_17.zip。提取 SQLite3.dll 到 Solution Items 文件夹。
  • 将 System.Data.SQLite.dll 添加到测试工程的引用中。
  • 由于 SQLite3.dll 是用 C 编写的,是非托管的,我们不能直接引用它。为了正确的引用它,我们需要其设置成为内容文件。在测试项目上点击右键,选择添加现有项,添加 SQLite3.dll 。在解决方案资源管理器中找到这个文件,点击右键选择属性,将它设置为始终复制。这样就会在每次生成项目的时候会自动的将这个文件复制到 bin\Debug 或 bin\Release 目录里。
  • 我以前的随笔里,有一个 SQLiteDatabaseScope 类的代码,你可以把它添加到你的测试工程里。
一个简单的映射测试
复制代码
using System;
using System.Linq;
using NUnit.Framework;
using NHibernate;

namespace NStackExample.Data.Tests
{
    [TestFixture]
    public class CourseMappingTests
    {
        [Test]
        public void CanSaveAndLoadCourse()
        {
            using (SQLiteDatabaseScope<CourseMapping> Scope = new SQLiteDatabaseScope<CourseMapping>())
            {
                using (ISession Session = Scope.OpenSession())
                {
                    Guid ID;
                    Course Course;

                    using (ITransaction Tran = Session.BeginTransaction())
                    {
                        ID = (Guid)Session.Save(new Course
                        {
                            Subject = "SUBJ",
                            CourseNumber = "1234",
                            Title = "Title",
                            Description = "Description",
                            Hours = 3
                        });
                        Tran.Commit();
                    }
                    Session.Clear();

                    using (ITransaction Tran = Session.BeginTransaction())
                    {
                        Course = Session.Get<Course>(ID);

                        Assert.AreEqual("SUBJ", Course.Subject);
                        Assert.AreEqual("1234", Course.CourseNumber);
                        Assert.AreEqual("Title", Course.Title);
                        Assert.AreEqual("Description", Course.Description);
                        Assert.AreEqual(3, Course.Hours);

                        Tran.Commit();
                    }
                }
            }
        }
    }
}

下面介绍下它如何工作:

  • 首先,在建立架构的时候获取到一个新的内存中的 SQLite 数据库
  • 将 course 保存到数据库中
  • 清空 session
  • 从数据库中获取到 course
  • 测试所有属性,以确保它们都是正确的

这里你需要知道几件事

  • TestFixture 属性。这是告诉 NUnit 在这个类当中包含测试。
  • Test 属性。这是告诉 NUnit 这个方法是一个测试方法。
建议不要使用隐式事务

你可能在疑惑为什么我将这些简单的数据库逻辑封装在一个事务中,特别是 Session.Get ,这只是一个单一的选择语句。在写本系列教程之前,我也不会这样做的,这是一个新手的错误。

在做这项研究的时候,我看到了 Ayende 写的 一个测试示例 。他对所有的操作都使用事务,甚至是 Session.Get 。我问他为什么这样做的时候,他给我发了一个链接,NHProfiler Alert 。这一点是非常重要的,而且不是显而易见的,至少对我来说是这样的。具体的内容大家可以去之前的链接看一下。

一个稍微复杂的映射测试

在某些时候我们的实体需要有一个父亲,例如 section ,所以必须在测试孩子之前需要先创建并插入父亲。我们不在此进行级联测试,这是一个单独的测试。在这里, section 必须要有两个父亲:course 和 term。

复制代码
        [Test]
        public void CanSaveAndLoadSection()
        {
            using (SQLiteDatabaseScope<CourseMapping> Scope = new SQLiteDatabaseScope<CourseMapping>()) {
                using (ISession Session = Scope.OpenSession()) {

                    Guid ID;
                    Section Section;
                    Course Course = new Course { 
                        Subject = "SUBJ", 
                        CourseNumber = "1234", 
                        Title = "Title", 
                        Description = "Description", 
                        Hours = 3};
                    Term Term = new Term {
                        Name = "Fall 2009",
                        StartDate = new DateTime(2009,8,1),
                        EndDate = new DateTime(2009,12,1)};
                    
                    // 我们不在这里进行级联测试,所以显式的保存父对象。
                    using (ITransaction Tran = Session.BeginTransaction()) {
                        Session.Save(Course);
                        Session.Save(Term);
                        Tran.Commit();
                    }

                    Session.Clear();

                    using (ITransaction Tran = Session.BeginTransaction()) {
                        ID = (Guid) Session.Save(new Section {
                                 Course = Course,
                                 FacultyName = "FacultyName", 
                                 RoomNumber = "R1", 
                                 SectionNumber = "W1",
                                 Term = Term});
                        Tran.Commit();
                    }

                    Session.Clear();

                    using (ITransaction Tran = Session.BeginTransaction()) {
                        Section = Session.Get<Section>(ID);

                        Assert.AreEqual(Course, Section.Course);
                        Assert.AreEqual("FacultyName", Section.FacultyName);
                        Assert.AreEqual("R1",Section.RoomNumber);
                        Assert.AreEqual("W1", Section.SectionNumber);
                        Assert.AreEqual(Term, Section.Term);

                        Tran.Commit();
                    }

                }
            }
        }
相关推荐
煎饼学大模型2 小时前
Agent 的“大脑-手“解耦架构:当推理层和工具执行层各自独立演进
数据库·人工智能·oracle·架构·agent
头发还在的女程序员2 小时前
医院陪诊管理系统怎么选择?——2026 年选型避坑与架构参考
java·开发语言·陪诊系统·陪诊app·医院陪诊陪护
CodeStats2 小时前
【Spring事务】Spring事务注解 @Transactional 完整体系:从 MySQL 隔离级别到 MyBatis 原理详解
java·spring·mybatis·事务·transactional
我命由我123453 小时前
Android 开发问题:为 PDFView 设置一个带有黑色边框的背景 drawable,但边框没有生效
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
都叫我大帅哥5 小时前
从Python到Java:为什么企业级Agent最终会选择Java?
java·ai编程
wanderist.5 小时前
Lambda表达式在算法竞赛中的应用
java·开发语言·算法
腻害兔6 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:IM 即时通讯模块,一个被低估的「全功能聊天系统」
java·前端·vue.js·产品经理·ai编程
心念枕惊7 小时前
.NET CORE 授权进阶-角色、策略与动态权限实现
java·前端·.netcore
暖和_白开水8 小时前
数据分析agent(十三_2):业务层和持久层相互转换mapper
数据库·oracle·数据分析
景同学8 小时前
把 AI 用到线上运维:可行、有效,前提是喂足信息——一次 Full GC 排障实录
java·人工智能·后端