Вопрос

Мне интересно при каких обстоятельствах следующий код 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 всегда терпит неудачу.

Если я вручную позвоню в Session.flush после SaveTorUpDate, то запрос SELECT добивается успеха, однако я думал, что нам не нужно было вручную вызовать флеш? Насколько я понимаю, Nhibernate должен быть достаточно умным, чтобы понять, что Foo был обновлен, поэтому второй запрос Select должен быть успешным.

Наблюдая за созданным SQL, кажется, что SQL второго запроса SQL выполняется перед первым SQL SavtorUpdate.

На самом деле, если я оберную весь метод в транзакцию, то он удастся:

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

Теперь SQL SaveorUpdate будет выполнен до Linq.where SQL. Это немного странно, так как мне не нужно даже совершать транзакцию между ними.

Что здесь происходит?

Это было полезно?

Решение

Я рекомендую вам использовать транзакции 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();
  }
}

Другие советы

Обратите внимание, что вам нужна транзакция, чтобы 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: если он отслеживает объект, то он выяснит, когда отправлять изменения в базу данных, и вам не нужно говорить, что это сделать.

«Мне интересно, при каких обстоятельствах следующий код nhibernate может потерпеть неудачу:« Я думаю, что вы предоставили хотя бы один ответ на свой собственный вопрос: когда код запускается в неявной транзакции. Видеть этот пост от Айенде, который упоминает непоследовательное поведение внутри неявных транзакций. У меня есть много модульных тестов, которые напоминают ваш код, за исключением тестового драйвера, предоставляя транзакцию обертывания, например,

[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 <>.] У меня есть много таких тестов, которые проходят регулярно.

У вас, вероятно, есть неправильный набор Flushmode. Вам необходимо установить FlushMode на сеансе на Auto, чтобы он автоматически промывал сеанс перед каждым запросом, в противном случае вам нужно промыть сеанс вручную, чтобы сохранения изменений.

Вы должны закрыть сеанс и создать теперь один перед утверждением.

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

Если я вручную позвоню в session.flush после SaveTorUpDate, то запрос SELECT добивается успеха.

Для начала: ваш звонок SaveTorUpDate () даже не нужен.

Вот некоторые вещи, которые нужно помнить при использовании сеанса NH:

  • Когда вы загрузили объект из сеанса, сеанс продолжит отслеживать изменения в этом объекте
  • Вызов Session.Update (объект) только сообщает сеансу 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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top