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