回滚Fluent NHibernate中PersistenceSpecifications创建的记录

时间:2009-03-10 21:15:07

标签: c# fluent-nhibernate

我正在学习一些Fluent 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();
            }

5 个答案:

答案 0 :(得分:9)

尝试在事务上设置IsolationLevel。这个片段对我有用:

using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
    new PersistenceSpecification<Event>(_session)
        .CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1))
        .VerifyTheMappings();
    trans.Rollback();
}

答案 1 :(得分:2)

PersistenceSpecification通常与内存数据库(如SQLite)一起使用,这就是为什么它不会回滚任何内容。我相信有一个构造函数重载需要一个ISession实例,你试过从那里获得一个事务然后再回滚吗?

答案 2 :(得分:2)

我认为这里的问题是VerifyTheMappings()调用TransactionSave(),它对数据库执行tx.Commit()。正如James指出的那样,这种技术似乎非常适合丢弃内存中的测试技术。在针对旧数据库测试映射的情况下,这将工作。

答案 3 :(得分:1)

设置IsolationLevel.ReadUncommitted将起作用,但只是偶然的,因为它所做的就是告诉会话它可以读取而不需要新的事务(脏读,用DBMS的说法) - 所以Session.Transaction.Commit ()在验证读取之前不必提交数据库事务。这也意味着它不一定测试你认为它正在测试的东西! (我也认为这可能是非MS SQL数据库中可疑的支持)。 leebrandt的答案是有效的,因为显式回滚,而不是隔离级别(nb。在答案时,这比现在更有帮助,请参见下面的注释)。

好消息是,正确的方法是手动回滚事务。提交事务时会自动替换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 ();
    }
}

显然,只要在任何地方插入您自己的特定于测试框架的术语和属性/注释,您就会明白这一点。


我刚才注意到这个问题有多久了:这种行为在09年7月的this commit中已得到修复,以便很好地处理现有交易,以便上述工作正常进行!很明显,这就是你最初所做的事情。

答案 4 :(得分:0)

我认为用真正的数据库进行测试非常重要,看看他的表定义是好的,所以我开发了一个非常简单的类,它对映射的实体执行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();
            }
        }
    }
}

我的项目中可识别的II是我实体的最基本界面

该类正在使用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();

    }
}