NHibernate没有发现变化

时间:2009-09-21 00:42:34

标签: c# .net nhibernate flush

我想知道以下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之前执行。这有点奇怪,因为我甚至不必在两者之间提交交易。

发生了什么事?

6 个答案:

答案 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);
}

另请注意,当您要保存对对象所做的更改时,调用SaveUpdateSaveOrUpdate 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(entity)只告诉NHibernate会话它应该启动跟踪对象,它不会去写db更改

因此,在您的情况下,因为您已经从会话中加载了一个对象,所以调用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();
  }
}