如何关闭NHibernate的自动(脏检查)更新行为?

时间:2009-03-23 14:23:31

标签: nhibernate

我刚刚发现,如果我从NHibernate会话中获取一个对象并更改对象的属性,NHibernate会在提交时自动更新对象,而不会调用Session.Update(myObj)

我可以看到这有什么用,但是默认行为看起来很疯狂!

更新:我现在理解持久性无知,所以这种行为现在显然是首选。我将在这里留下这个令人尴尬的问题,希望能帮助其他亵渎用户。

我怎样才能阻止这种情况发生?这是默认的NHibernate行为还是来自Fluent NHibernate的AutoPersistenceModel?

如果无法阻止这种情况,我该怎么办?除非我忽略了这一点,否则这种行为似乎会造成一个混乱。

我正在使用来自18/3/2009的NHibernate 2.0.1.4和Fluent NHibernate构建

这家伙是his answer吗?

我还读过,重写事件监听器可能是解决方案。但是,在这种情况下不会调用IDirtyCheckEventListener.OnDirtyCheck。有谁知道我需要覆盖哪个听众?

4 个答案:

答案 0 :(得分:13)

您可以将Session.FlushMode设置为FlushMode.Never。这将使您的操作明确

即:tx.Commit()session.Flush()。当然,这仍然会在提交/刷新时更新数据库。如果您不想要这种行为,那么调用session.Evict(yourObj)然后它将变为瞬态,NHibernate将不会为它发出任何db命令。

回复您的修改:是的,那家伙为您提供了更多选项来控制它。

答案 1 :(得分:3)

我的解决方案:

  1. 在您的初始ISession创建中,(在注入框架注册中的某处)将DefaultReadOnly设置为true。
  2. 在包含NHibernate并管理ISession等的IRepository实现中,在调用ISession.Save,Update,SaveUpdate等的Insert,Update,InsertUpdate和Delete(或类似)方法中,为实体调用SetReadOnly并且标志设置为false。

答案 2 :(得分:1)

调用SaveOrUpdate()或Save()会使对象持久化。如果您使用ISession或从对持久对象的引用检索它,则该对象是持久的,并且刷新会话将保存更改。您可以通过在使其成为瞬态的对象上调用Evict()来防止此行为。

编辑补充:我通常认为ISession是一个工作单元。这很容易在Web应用程序中实现。使用session-per-request但需要在WinForms中进行更多控制。

答案 3 :(得分:0)

我们通过使用带有NH的事件监听器来做到这一点(这不是我的工作 - 但我找不到我在哪里做的链接......)。

我们有一个EventListener用于读取数据,将其设置为ReadOnly - 然后一个用于Save(和SaveOrUpdate)将它们设置为已加载,因此 对象将在我们手动时保持不变打电话给Save()

那 - 或者你可以使用没有State / ChangeTracking的IStatelessSession。

这会在加载时立即将实体/项设置为ReadOnly。

我只包含一个Insertion事件监听器,但我的配置代码引用了所有这些监听器。

/// <summary>
/// A listener that once an object is loaded will change it's 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() });
        }
    }