C# 在Fluent NHibernate中回滚PersistenceSpecification创建的记录

C# 在Fluent NHibernate中回滚PersistenceSpecification创建的记录,c#,fluent-nhibernate,C#,Fluent Nhibernate,我正在学习一些流利的NHibernate,并且我已经完成了半真棒的PersistenceSpecification课程 我在一个单元测试中设置了它来验证我的映射,它工作得很好。但是,完成后,它会将记录保留在数据库中。我尝试将其放入事务中,以便回滚更改,但出现错误: System.ObjectDisposedException:无法访问已处置的对象。 对象名称:“AdoTransaction” 如果没有事务,我必须找出记录的ID,检索它们并删除它们,这似乎不是很优雅 有什么想法吗 编辑: 以下是代

我正在学习一些流利的NHibernate,并且我已经完成了半真棒的PersistenceSpecification课程

我在一个单元测试中设置了它来验证我的映射,它工作得很好。但是,完成后,它会将记录保留在数据库中。我尝试将其放入事务中,以便回滚更改,但出现错误:

System.ObjectDisposedException:无法访问已处置的对象。 对象名称:“AdoTransaction”

如果没有事务,我必须找出记录的ID,检索它们并删除它们,这似乎不是很优雅

有什么想法吗

编辑:

以下是代码片段:

            var factory = GetSessionFactory();
            using (var session = factory.OpenSession())
            using (var transaction = session.BeginTransaction())
            {
                new PersistenceSpecification<TimePeriod>(session)
                        .CheckProperty(x => x.EndDate, DateTime.Today)
                        .VerifyTheMappings();
                transaction.Rollback();
            }
var-factory=GetSessionFactory();
使用(var session=factory.OpenSession())
使用(var transaction=session.BeginTransaction())
{
新PersistenceSpecification(会话)
.CheckProperty(x=>x.EndDate,DateTime.Today)
.验证应用程序();
transaction.Rollback();
}

PersistenceSpecification
通常与类似SQLite的内存中数据库一起使用,这就是它不回滚任何内容的原因。我相信存在一个构造函数重载,它接受一个
ISession
实例,您是否尝试从那里获取一个事务,然后将其回滚?

我认为这里的问题是验证Mappings()调用TransactionSave(),它对数据库执行tx.Commit()。正如James所指出的,这种技术对于一次性内存测试技术来说似乎非常有效。这在针对遗留数据库测试映射的情况下不起作用

我认为使用真实数据库进行测试非常重要,以确保他的表定义正确,因此我开发了一个非常简单的类,该类对映射实体执行crud测试,并在最后回滚

 internal class GenericMappingTesterWithRealDB<T> where T : IIdentifiable
{
    public T EntityToTest { get; set; }
    public Func<T, object> PerformEntityManipulationBeforeUpdate { get; set; }
    public GenericMappingTesterWithRealDB()
    {
        Assume.That(SessionFactoryProvider.NewSession,Is.Not.Null);
    }

    public void RunTest()
    {
        using (ISession session = SessionFactoryProvider.NewSession)
        using (ITransaction transaction = session.BeginTransaction())
        {
            try
            {
                session.Save(EntityToTest);
                var item = session.Get<T>(EntityToTest.ID);
                Assert.IsNotNull(item);
                if (PerformEntityManipulationBeforeUpdate != null)
                {
                    PerformEntityManipulationBeforeUpdate.Invoke(EntityToTest);
                }
                session.Update(EntityToTest);
                session.Delete(EntityToTest);
                session.Save(EntityToTest);
            }
            catch (Exception e)
            {
                Assert.Fail(e.Message, e.StackTrace);
            }
            finally
            {
                transaction.Rollback();
            }
        }
    }
}
内部类GenericMappingTesterWithRealDB,其中T:IIdentifiable
{
公共T EntityToTest{get;set;}
公共函数PerformementManipulationBeforeUpdate{get;set;}
公共GenericMappingTesterWithRealDB()
{
假设.That(SessionFactoryProvider.NewSession为.Not.Null);
}
公共无效运行测试()
{
使用(ISession会话=SessionFactoryProvider.NewSession)
使用(ITransaction transaction=session.BeginTransaction())
{
尝试
{
session.Save(EntityToTest);
var item=session.Get(EntityToTest.ID);
Assert.IsNotNull(项);
if(PerformementyManipulationBeforeUpdate!=null)
{
PerformementyManipulationBeforeUpdate.Invoke(EntityToTest);
}
会话更新(EntityToTest);
删除(EntityToTest);
session.Save(EntityToTest);
}
捕获(例外e)
{
Assert.Fail(e.Message,e.StackTrace);
}
最后
{
transaction.Rollback();
}
}
}
}
IIIdentifiable在我的项目中是我的实体最基本的接口

该类正在使用nunit.framework,但您可以使用任何您想要的测试框架

sessionfactoryprovider需要提供isession obj

这是一个使用示例

/// <summary>
/// Testing the mapping of our entities.
/// there must be a server connection for this kind of test.
/// </summary>
[TestFixture]
internal class someMappingTest
{
    [Test(Description = "Check the Encoding Profile FluentNHibernate Mapping")]
    [Timeout(20000)]
    public void checkthatMappingWorks()
    {
        // creatw the new entity
        TestedType testOn = new TestedType();

        // set the initialization values
        testOn.Name = "TestProfileExecution";

        // create the test object
        new GenericMappingTesterWithRealDB<TestedType>
        {
            // assign an entity
            EntityToTest = testOn,

            // assign new values for update check
            PerformEntityManipulationBeforeUpdate =
                delegate(TestedType testedTypeBeingTested)
                    {
                        return testedTypeBeingTested.Name = "Updateing Test";
                    }
        }.
        // call run test to perform the mapping test.
        RunTest();

    }
}
//
///测试实体的映射。
///此类测试必须有服务器连接。
/// 
[测试夹具]
内部类映射测试
{
[测试(Description=“检查编码配置文件FluentNHibernate映射”)]
[超时(20000)]
公共无效检查映射工程()
{
//创建新实体
TestedType testOn=新的TestedType();
//设置初始化值
testOn.Name=“TestProfileExecution”;
//创建测试对象
新的GenericMappingTesterWithRealDB
{
//分配实体
EntityToTest=testOn,
//为更新检查分配新值
更新前的性能操作=
代表(已测试类型已测试类型正在测试)
{
返回testedTypeBeingTested.Name=“更新测试”;
}
}.
//调用RunTest以执行映射测试。
RunTest();
}
}

尝试设置事务的隔离级别。这个片段对我很有用:

using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
    new PersistenceSpecification<Event>(_session)
        .CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1))
        .VerifyTheMappings();
    trans.Rollback();
}
使用(var trans=\u session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
新的PersistenceSpecification(_会话)
.CheckProperty(p=>p.StartTime,新日期时间(2010,1,1))
.验证应用程序();
trans.Rollback();
}

设置
IsolationLevel.ReadUncommitted
将起作用,但只是顺便说一句,因为它所做的只是告诉会话它可以在不需要新事务的情况下读取(用DBMS的说法是脏读)
session.transaction.Commit()
不必在验证读取之前提交数据库事务。这也意味着它不一定测试你认为它正在测试的东西!(我还认为这在非MS SQL数据库中的支持可能有问题)。leebrandt的答案之所以有效,是因为明确的回滚,而不是隔离级别(注意,在给出答案时,这比现在更有用,请参见下面的注释)

好消息是,正确的方法是手动回滚事务
Session.Transaction
在事务提交时会自动被替换,因此您需要保留对它的引用,并且您必须以任何方式显式打开一个引用,因为
TransactionalSave()
检查当前事务是否处于活动状态,如果未处于活动状态,则创建(并处理!)自己的事务。我通常在同一个装置中测试所有映射,其中我还
class TestFixture {
    static ISessionFactory factory = CreateMyFactorySomehowHere();

    ISession session;
    ITransaction tx;

    public void Setup ()
    {
        session = factory.OpenSession ();
        tx = session.BeginTransaction ();
    }

    public void Cleanup ()
    {
        tx.Rollback ();
        tx.Dispose ();
        session.Close ();
    }

    public void TestAMappingForSomething ()
    {
        var spec = new PersistenceSpecification<Something> (session);
        spec.VerifyTheMappings ();
    }
}