NHibernate FlushMode Auto не очищается перед поиском - PullRequest
6 голосов
/ 21 июля 2010

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

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

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

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

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

Жирный «некоторые вызовы» ясно говорит, что NH не имеетответственность на всех.IMO, тем не менее, у нас есть проблема согласованности здесь, потому что документ также утверждает, что:

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

Итак, если я использую CreateQuery (Найти замену) и фильтрую для сущностей со свойством Value = 20, NH может NOT вернуть сущности со значением = 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, чтобы убедиться в его работе?

Спасибо.

Filipe

Ответы [ 3 ]

8 голосов
/ 21 июля 2010

Я не очень знаком с исходным кодом 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;
    }
}

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

2 голосов
/ 21 июля 2010

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

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

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

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

0 голосов
/ 21 июля 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...