NHibernate 没有接受更改

发布于 2024-08-05 11:02:09 字数 845 浏览 6 评论 0原文

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

到底是怎么回事?

I am wondering under what circumstances the following NHibernate code could fail:

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);

The Assert statement always fails.

If I manually call session.Flush after SaveOrUpdate, then the select query succeeds, however I thought that we did not have to manually call flush? It was my understanding that NHibernate should be smart enough to realise that Foo has been updated, so the second select query should succeed.

Watching the SQL that is generated, it appears the second select query's SQL is executed before the first SaveOrUpdate's sql.

In fact, if I wrap the entire method in a transaction, then it succeeds:

using(NHibernateSessionManager.CurrentSession.BeginTransaction()
{
    // Same code as above
}

Now the SaveOrUpdate's sql will execute before the Linq.Where sql. This is a little strange, as I do not have to even commit the transaction in between.

What is going on?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不即不离 2024-08-12 11:02:10

请注意,您需要一个事务才能使 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 不同:如果它正在跟踪一个对象,那么它会确定何时将更改发送到数据库,并且您不需要告诉它这样做。

Note that you need a transaction for NHibernate to be "smart."

Here is how it works:

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

Note also that you do not call Save, Update, or SaveOrUpdate when you want to save the changes you have made to an object that the Session is already tracking back down to the database. NHibernate works differently from other ORMs: if it is tracking an object, then it will figure out when to send down the changes to the database and you don't need to tell it to do so.

浅笑依然 2024-08-12 11:02:10

“我想知道在什么情况下以下 NHibernate 代码可能会失败:”我认为您已经为自己的问题提供了至少一个答案:当代码在隐式事务中运行时。请参阅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<>() 是 Linq<> 上的薄包装器。] 我有许多定期通过的此类测试。

"I am wondering under what circumstances the following NHibernate code could fail:" I think you have supplied at least one answer to your own question: when the code is run inside an implicit transaction. See this post from Ayende, which mentions inconsistent behavior inside implicit transactions. I have many unit tests that resemble your code except the test driver supplies a wrapping transaction, e.g.,

[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<>() is a thin wrapper over Linq<>.] I have many such tests that pass regularly.

怕倦 2024-08-12 11:02:10

您可能设置了不正确的冲洗模式。您需要将会话上的刷新模式设置为自动,以便在每次查询之前自动刷新会话,否则您需要手动刷新会话以强制保存更改。

You've probably got the incorrect flushmode set. You need to set the flushmode on the session to Auto for it to automatically flush the session before every query otherwise you need to flush the session manually to force the changes to be saved.

携余温的黄昏 2024-08-12 11:02:10

您必须关闭会话并在断言之前创建一个 now 会话。

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();
  }
}

You have to close the session and create an now one before the 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();
  }
}
场罚期间 2024-08-12 11:02:10

如果我手动调用 session.Flush 之后
SaveOrUpdate,然后选择查询
成功。

首先:甚至不需要调用 SaveOrUpdate()。

使用 NH 会话时需要记住以下几点:

  • 当您从会话加载对象时,会话将继续跟踪该对象的更改 调用
  • session.Update(entity) 仅告诉 NHibernate 会话它应该启动 跟踪对象,它不会将更改写入数据库

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

If I manually call session.Flush after
SaveOrUpdate, then the select query
succeeds.

For a start: your call to SaveOrUpdate() is not even needed.

Here are some things to remember when using an NH session:

  • When you have Loaded an object from session, the session will continue to track changes to that object
  • Calling session.Update(entity) only tells the NHibernate session that it should start tracking the object, it does not go and write changes to the db

So, in your case, because you have loaded an object from the session already, calling session.Update() does nothing since it is already being tracked. You could infact force an update to the database by merely doing the following:

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);
青萝楚歌 2024-08-12 11:02:09

我建议您利用 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();
  }
}

I recommend you leverage NHibernate transactions. It's entirely possible that, without their use, NHibernate has no way of determining when to issue your SaveOrUpdate call.

You'll find that even read-only statements perform better when using transactions. Please see http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactions for further details.

For example:

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();
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文