Подтвердить что ты не робот

Как отключить поведение обновления NHibernate (грязная проверка)?

Я только что обнаружил, что если я получу объект из сеанса NHibernate и измените свойство на объекте, NHibernate автоматически обновит объект при совершении без вызова Session.Update(myObj)!

Я вижу, как это может быть полезно, , но по умолчанию поведение кажется сумасшедшим!

Обновление: Теперь я понимаю упорство невежества, так что теперь это явно предпочтительный вариант. Я оставлю здесь этот смущающий вопрос, надеюсь, помочь другим профанам.

Как я могу остановить это? Является ли это поведением NHibernate по умолчанию или что-то из Fluent NHibernate AutoPersistenceModel?

Если нет способа остановить это, что мне делать? Если я не пропущу точку, это поведение создает правильный беспорядок.

Я использую NHibernate 2.0.1.4 и Fluent NHibernate build от 18/3/2009

Этот парень прав с его ответ?

Я также прочитал, что переопределение прослушивателя событий может быть решением этого. Однако IDirtyCheckEventListener.OnDirtyCheck не вызывается в этой ситуации. Кто-нибудь знает, какой слушатель мне нужно переопределить?

4b9b3361

Ответ 1

Вы можете установить Session.FlushMode на FlushMode.Never. Это сделает ваши операции явными

т.е.: на tx.Commit() или session.Flush(). Конечно, это все равно будет обновлять базу данных при фиксации/сбросе. Если вы не хотите этого поведения, вызовите session.Evict(yourObj), и он станет временным, и NHibernate не выдаст для него никаких команд db.

Ответ на ваше редактирование: Да, этот парень дает вам больше опций о том, как его контролировать.

Ответ 2

Мое решение:

  • В вашем первоначальном создании ISession (где-то внутри ваших регистраций фреймворка) установите DefaultReadOnly в true.
  • В вашей реализации IRepository, которая обертывает NHibernate и управляет ISession и т.д., в методах Insert, Update, InsertUpdate и Delete (или подобных), которые вызывают ISession.Save, Update, SaveUpdate и т.д., вызовите SetReadOnly для объекта и флаг установлен на false.

Ответ 3

Вызов SaveOrUpdate() или Save() делает объект постоянным. Если вы извлекли его с помощью ISession или из ссылки на постоянный объект, тогда объект будет постоянным, и очистка сеанса сохранит изменения. Вы можете предотвратить это поведение, вызывая Evict() на объекте, который делает его переходным.

Отредактировано для добавления: Обычно я рассматриваю ISession как единицу работы. Это легко реализовать в веб-приложении. используя сеанс-за-запрос, но требует большего контроля в WinForms.

Ответ 4

Мы сделали это с помощью Слушателей событий с NH (это не моя работа, но я не могу найти ссылку, где я это сделал).

У нас есть EventListener для чтения в данных, чтобы установить его как ReadOnly, а затем один для Save (и SaveOrUpdate), чтобы установить их как загруженные, чтобы объект сохранялся, когда мы вручную вызываем Save() на нем.

Это - или вы можете использовать IStatelessSession, у которого нет состояния /ChangeTracking.

Это устанавливает сущность/элемент как ReadOnly сразу при загрузке.

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

/// <summary>
/// A listener that once an object is loaded will change it status to ReadOnly so that
/// it will not be automatically saved by NH
/// </summary>
/// <remarks>
/// For this object to then be saved, the SaveUpdateEventListener is to be used.
/// </remarks>
public class PostLoadEventListener : IPostLoadEventListener
{
    public void OnPostLoad(PostLoadEvent @event)
    {
        EntityEntry entry = @event.Session.PersistenceContext.GetEntry(@event.Entity);

        entry.BackSetStatus(Status.ReadOnly);
    }
}

При сохранении объекта мы вызываем это, чтобы установить этот объект на Loaded (что означает, что он будет сохраняться)

public class SaveUpdateEventListener : ISaveOrUpdateEventListener
{
    public static readonly CascadingAction ResetReadOnly = new ResetReadOnlyCascadeAction();

    /// <summary>
    /// Changes the status of any loaded item to ReadOnly.
    /// </summary>
    /// <remarks>
    /// Changes the status of all loaded entities, so that NH will no longer TrackChanges on them.
    /// </remarks>
    public void OnSaveOrUpdate(SaveOrUpdateEvent @event)
    {
        var session = @event.Session;
        EntityEntry entry = session.PersistenceContext.GetEntry(@event.Entity);

        if (entry != null && entry.Persister.IsMutable && entry.Status == Status.ReadOnly)
        {
            entry.BackSetStatus(Status.Loaded);
            CascadeOnUpdate(@event, entry.Persister, @event.Entry);
        }
    }

    private static void CascadeOnUpdate(SaveOrUpdateEvent @event, IEntityPersister entityPersister, 
        object entityEntry)
    {
        IEventSource source = @event.Session;
        source.PersistenceContext.IncrementCascadeLevel();
        try
        {
            new Cascade(ResetReadOnly, CascadePoint.BeforeFlush, source).CascadeOn(entityPersister, entityEntry);
        }
        finally
        {
            source.PersistenceContext.DecrementCascadeLevel();
        }
    }
}

И мы реализуем его в NH таким образом:

    public static ISessionFactory CreateSessionFactory(IPersistenceConfigurer dbConfig, Action<MappingConfiguration> mappingConfig, bool enabledChangeTracking,bool enabledAuditing, int queryTimeout)
    {
        return Fluently.Configure()
            .Database(dbConfig)
            .Mappings(mappingConfig)
            .Mappings(x => x.FluentMappings.AddFromAssemblyOf<__AuditEntity>())
            .ExposeConfiguration(x => Configure(x, enabledChangeTracking, enabledAuditing,queryTimeout))
            .BuildSessionFactory();
    }

    /// <summary>
    /// Configures the specified config.
    /// </summary>
    /// <param name="config">The config.</param>
    /// <param name="enableChangeTracking">if set to <c>true</c> [enable change tracking].</param>
    /// <param name="queryTimeOut">The query time out in minutes.</param>
    private static void Configure(NHibernate.Cfg.Configuration config, bool enableChangeTracking, bool enableAuditing, int queryTimeOut)
    {
        config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, "none");
        if (queryTimeOut > 0)
        {
            config.SetProperty("command_timeout", (TimeSpan.FromMinutes(queryTimeOut).TotalSeconds).ToString());
        }

        if (!enableChangeTracking)
        {
            config.AppendListeners(NHibernate.Event.ListenerType.PostLoad, new[] { new Enact.Core.DB.NHib.Listeners.PostLoadEventListener() });
            config.AppendListeners(NHibernate.Event.ListenerType.SaveUpdate, new[] { new Enact.Core.DB.NHib.Listeners.SaveUpdateEventListener() });
            config.AppendListeners(NHibernate.Event.ListenerType.PostUpdate, new[] { new Enact.Core.DB.NHib.Listeners.PostUpdateEventListener() });
            config.AppendListeners(NHibernate.Event.ListenerType.PostInsert, new[] { new Enact.Core.DB.NHib.Listeners.PostInsertEventListener() });
        }
    }