我想知道以下NHibernate代码在什么情况下会失败:
var session = NHibernateSessionManager.CurrentSession;
var foo = session.Linq<Foo>.ToList()[0];
foo.SomeProperty = "test";
session.SaveOrUpdate(foo);
var reloadedFoos = session.Linq<Foo>
.Where(x => x.SomeProperty == "test");
Assert.That(reloadedFoos.Count > 0);
Assert语句总是失败。
如果我在SaveOrUpdate之后手动调用session.Flush,那么select查询会成功,但我认为我们不必手动调用flush?我的理解是NHibernate应该足够聪明才能意识到Foo已经更新,所以第二个选择查询应该会成功。
观察生成的SQL,看来第二个选择查询的SQL在第一个SaveOrUpdate的sql之前执行。
事实上,如果我将整个方法包装在一个事务中,那么它就会成功:
using(NHibernateSessionManager.CurrentSession.BeginTransaction()
{
// Same code as above
}
现在,SaveOrUpdate的sql将在Linq.Where sql之前执行。这有点奇怪,因为我甚至不必在两者之间提交交易。
发生了什么事?
答案 0 :(得分:3)
我建议您利用NHibernate事务。完全有可能在没有使用它们的情况下,NHibernate无法确定何时发出SaveOrUpdate调用。
您会发现即使是只读语句在使用事务时也能表现得更好。有关详细信息,请参阅http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactions。
例如:
using(var session = NHibernateSessionManager.CurrentSession)
{
using(var transaction = session.BeginTransaction())
{
var foo = session.Linq<Foo>.ToList()[0];
foo.SomeProperty = "test";
session.SaveOrUpdate(foo);
transaction.Commit();
}
}
答案 1 :(得分:3)
请注意,您需要NHibernate的交易“聪明”。
以下是它的工作原理:
var session = NHibernateSessionManager.CurrentSession;
using(NHibernateSessionManager.CurrentSession.BeginTransaction()) {
var foo = session.Linq<Foo>.ToList()[0];
foo.SomeProperty = "test";
var reloadedFoos = session.Linq<Foo>()
.Where(x => x.SomeProperty == "test");
Assert.That(reloadedFoos.Count > 0);
}
另请注意,当您要保存对对象所做的更改时,不调用Save
,Update
或SaveOrUpdate
Session
已经追溯到数据库。 NHibernate与其他ORM的工作方式不同:如果它跟踪一个对象,那么它将确定何时将更改发送到数据库,而不需要告诉它这样做。
答案 2 :(得分:1)
“我想知道以下NHibernate代码在什么情况下会失败:”我认为你已经为自己的问题提供了至少一个答案:当代码在隐式事务中运行时。请参阅this post from Ayende,其中提到了隐式事务中的不一致行为。我有许多类似于你的代码的单元测试,除了测试驱动程序提供包装事务,例如,
[Test]
public void Can_Update_Account() {
Account account = PersistenceContext.Get<Account>(TEST_ACCOUNT_ID);
string accountNumber = RandomString(10);
account.AccountNumber = accountNumber;
Account account1 = PersistenceContext.GetAll<Account>().Where(x => x.AccountNumber == accountNumber).SingleOrDefault();
Account account2 = PersistenceContext.Get<Account>(account.Id);
Assert.AreEqual(account.Id, account1.Id);
Assert.AreEqual(accountNumber, account2.AccountNumber);
}
[GetAll&lt;&gt;()是一个超过Linq&lt;&gt;的薄包装器。]我有很多这样的测试经常通过。
答案 3 :(得分:0)
您可能设置了不正确的flushmode。您需要将会话中的flushmode设置为Auto,以便在每次查询之前自动刷新会话,否则您需要手动刷新会话以强制保存更改。
答案 4 :(得分:0)
如果我手动调用session.Flush之后 SaveOrUpdate,然后是选择查询 成功。
首先:甚至不需要调用SaveOrUpdate()。
使用NH会话时需记住以下事项:
因此,在您的情况下,因为您已经从会话中加载了一个对象,所以调用session.Update()不会执行任何操作,因为它已被跟踪。您只需执行以下操作即可强制更新数据库:
var session = NHibernateSessionManager.CurrentSession;
var foo = session.Linq<Foo>.ToList()[0];
foo.SomeProperty = "test";
session.Flush();
var reloadedFoos = session.Linq<Foo>
.Where(x => x.SomeProperty == "test");
Assert.That(reloadedFoos.Count > 0);
答案 5 :(得分:0)
您必须关闭会话并在Assert之前创建一个会话。
using(var session = NHibernateSessionManager.CurrentSession)
{
using(var tx = session.BeginTransaction())
{
var foo = session.Linq<Foo>.ToList()[0];
foo.SomeProperty = "test";
session.SaveOrUpdate(foo);
tx.Commit();
}
}
//create a new session here, the code depend if you use RhinoCommons (like me), no Rhino
using(var session = NHibernateSessionManager.CurrentSession)
{
using(var tx = session.BeginTransaction())
{
var reloadedFoos = session.Linq<Foo>
.Where(x => x.SomeProperty == "test");
Assert.That(reloadedFoos.Count > 0);
tx.Commit();
}
}