English 中文(简体)
如何关闭NHibernate的自动(脏检查)更新行为?
原标题:
  • 时间:2009-03-23 14:23:31
  •  标签:

我刚刚发现,如果我从NHibernate会话中获取对象并更改对象的属性,NHibernat将在提交时自动更新对象,而无需调用session.update(myObj)

我可以看到这会有多大帮助,但作为默认行为,这似乎很疯狂

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

我该如何阻止这种情况的发生?这是默认的NHibernate行为还是来自Fluent NHibernat的AutoPersistence模型?

如果没有办法阻止这种情况,我该怎么办?除非我没有抓住要点,否则这种行为似乎会造成一场混乱。

我使用的是NHibernate 2.0.1.4版本和2009年3月18日的Fluent NHibernat版本

这家伙说得对吗http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/#comment-3403“rel=”noreferrer“>他的答案?

我还读到,覆盖事件侦听器可能是解决此问题的一种方法。但是,在这种情况下不会调用IDirtyCheckEventListener.OnDirtyCheck。有人知道我需要覆盖哪个听众吗?

问题回答

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

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

对您编辑的回应:是的,那家伙给了您更多的控制选项。

我的解决方案:

  1. In your initial ISession creation, (somewhere inside your injection framework registrations) set DefaultReadOnly to true.
  2. In your IRepository implementation which wraps around NHibernate and manages the ISession and such, in the Insert, Update, InsertUpdate and Delete (or similar) methods which call ISession.Save, Update, SaveUpdate, etc., call SetReadOnly for the entity and flag set to false.

调用SaveOrUpdate()或Save()会使对象持久化。如果您使用ISession或从对持久对象的引用中检索到它,那么该对象是持久的,刷新会话将保存更改。您可以通过对对象调用Evict()来防止这种行为,这会使对象变得瞬态。

编辑后补充:我通常认为ISession是一个工作单元。这很容易在网络应用程序中实现。每个请求使用会话,但需要在WinForms中进行更多控制。

我们通过使用NH的事件监听器来做到这一点(这不是我的工作,但我找不到我做这件事的链接…)。

我们有一个EventListener,用于在读取数据时将其设置为ReadOnly,然后有一个用于Save(和SaveOrUpdate),用于将它们设置为已加载,因此当我们手动调用Save()时,该对象将持久存在。

也可以使用没有状态/更改跟踪的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() });
        }
    }




相关问题