NHibernate FlushMode Auto не очищается перед поиском

Хорошо, я видел несколько постов, в которых говорилось почти об одном и том же, но точки были немного другими.

Это классический случай: я сохраняю / обновляю сущность и в рамках ЖЕ СЕССИИ пытаюсь получить их из базы данных (используя критерии / поиск / перечисление / и т. Д.) С помощью FlushMode = Auto. Дело в том, что NHibernate не сбрасывает обновления перед запросом, поэтому я получаю противоречивые данные из базы данных.

"Достаточно справедливо", некоторые люди скажут, как говорится в документации:

Этот процесс flush происходит по умолчанию в следующих точках:

  • из некоторых вызовов Find() или Enumerable()
  • от NHibernate.ITransaction.Commit()
  • от ISession.Flush()

Смелые "некоторые призывы" ясно говорят о том, что NH не несет никакой ответственности вообще. ИМО, однако, у нас здесь есть проблема согласованности, потому что документ также утверждает, что:

За исключением случаев, когда вы расшифруете Flush(), нет абсолютно никаких гарантий относительно того, когда Session выполняет вызовы ADO.NET, только порядок, в котором они выполняются. Однако NHibernate гарантирует, что методы ISession.Find(..) никогда не будут возвращать устаревшие данные; и при этом они не будут возвращать неправильные данные.

Итак, если я использую CreateQuery (Найти замену) и фильтрую для сущностей со свойством Value = 20, NH не сможет вернуть сущности со значением = 30, верно? Но это то, что происходит на самом деле, потому что сброс не происходит автоматически, когда это должно произойти.

public void FlushModeAutoTest()
{
    ISession session = _sessionFactory.OpenSession();
    session.FlushMode = FlushMode.Auto;

    MappedEntity entity = new MappedEntity() { Name = "Entity", Value = 20 };
    session.Save(entity);

    entity.Value = 30;
    session.SaveOrUpdate(entity);

    // RETURNS ONE ENTITY, WHEN SHOULD RETURN ZERO
    var list = session.CreateQuery("from MappedEntity where Value = 20").List<MappedEntity>();

    session.Flush();
    session.Close();
}

В конце концов: я ошибаюсь, это ошибка или просто непредсказуемая функция, поэтому каждый должен вызвать Flush, чтобы убедиться в ее работе?

Спасибо.

Филипе

3 ответа

Решение

Я не очень знаком с исходным кодом NHibernate, но этот метод из реализации ISession в версии 2.1.2.GA может ответить на вопрос:

/// <summary>
/// detect in-memory changes, determine if the changes are to tables
/// named in the query and, if so, complete execution the flush
/// </summary>
/// <param name="querySpaces"></param>
/// <returns></returns>
private bool AutoFlushIfRequired(ISet<string> querySpaces)
{
    using (new SessionIdLoggingContext(SessionId))
    {
        CheckAndUpdateSessionStatus();
        if (!TransactionInProgress)
        {
            // do not auto-flush while outside a transaction
            return false;
        }
        AutoFlushEvent autoFlushEvent = new AutoFlushEvent(querySpaces, this);
        IAutoFlushEventListener[] autoFlushEventListener = listeners.AutoFlushEventListeners;
        for (int i = 0; i < autoFlushEventListener.Length; i++)
        {
            autoFlushEventListener[i].OnAutoFlush(autoFlushEvent);
        }
        return autoFlushEvent.FlushRequired;
    }
}

Я полагаю, это означает, что автоматическая очистка гарантирует только последовательность внутри транзакции, что имеет некоторый смысл. Попробуйте переписать свой тест с использованием транзакции, мне очень интересно, решит ли это проблему.

Если вы думаете об этом, запрос в вашем примере всегда должен идти в БД. Сессия не является полным кэшем всех записей в БД. Таким образом, на диске могут быть другие объекты со значением 20. А так как вы не зафиксировали () транзакцию или flush(), сессия NH не может узнать, к какому "представлению" вы хотите обратиться (DB | Session).

Кажется, что "Лучшая практика" - делать все (получать и устанавливать) внутри явных транзакций:

using(var session = sessionFactory.OpenSession()) 
using(var tx = session.BeginTransaction()) 
{ 
    // execute code that uses the session 
    tx.Commit(); 
}

Смотрите здесь для получения подробной информации.

Управление и настройка hibernate - это форма искусства.

почему вы устанавливаете начальное значение 20, сохраняете, а затем изменяете его на 30?

На практике, если вы собираетесь изменить сеанс, а затем запросить сеанс, вы можете явно выполнить сброс между этими операциями. У вас может быть небольшое снижение производительности (в конце концов, вы не позволите hibernate оптимизировать очистку сеанса), но вы можете вернуться к нему, если это станет проблемой.

Вы цитировали, что "методы session.find никогда не будут возвращать устаревшие данные". Я бы изменил ваш код, чтобы использовать find вместо createQuery, чтобы посмотреть, работает ли он.

Другие вопросы по тегам