【问题标题】:NHibernate evict by idNHibernate 按 id 逐出
【发布时间】:2010-11-05 15:01:50
【问题描述】:

大家都知道会话中有缓存。 这个缓存一般可以通过2种方法清除:

  1. Session.Evict
  2. Session.Clear

第二种方法不仅为单个条目删除所有缓存。

我有商业方法。它接收大对象的 id(来自 aspx 站点)或有时接收多个 id。并在数据库中进行本机 sql 操作(使用具有复杂逻辑的 sql-query 以不加载 C# 中的所有数据)。然后我需要使缓存无效。因此,每个潜在的对象负载都无需直接从数据库缓存。

不幸的是驱逐只接受对象。此外,它的实现 DefaultEvictEventListener 在代码路径中有明确的分隔 - 代理类和非代理类分开。我尝试简单地创建实体,手动填写 id 并将其传递给 Evict。这将不起作用。据我了解,Evict by not proxied class 使用 GetHashCode 从缓存中查找和删除对象。因此,如果我不覆盖它,它将无法正常工作。我有很多本机 sql 批处理操作,因此覆盖所有实体对象中的所有 GetHashcode 将创建大量工作。另外我不确定这种情况是否会从缓存中删除代理。 更新:据我尝试覆盖 GetHashCode 也无济于事。 StatefulPersistenceContext.RemoveEntry 未找到实体,因为它使用 RuntimeHelpers.GetHashCode。所以这个解决方案甚至是不可能的

使用 NHibernate 的来源,我产生了以下解决方案:

public static class NHSessionHelper: DefaultEvictEventListener
 public static void RemoveEntityFromCache(this ISession session, Type type, object entityId)
    {
        ISessionImplementor sessionImpl = session.GetSessionImplementation();
        IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
        IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(type.FullName);

        if (persister == null)
        {
            return;
        }

        EntityKey key = new EntityKey(entityId, persister, sessionImpl.EntityMode);
        persistenceContext.RemoveProxy(key);

        object entity = persistenceContext.RemoveEntity(key);
        if (entity != null)
        {
            EntityEntry e = persistenceContext.RemoveEntry(entity);
            DoEvict(entity, key, e.Persister, (IEventSource)sessionImpl);
        }
    }

它只使用了 NHibenate 实现的一部分。但在我看来,复制代码并不是一个好主意。可能有人有其他想法吗?

【问题讨论】:

    标签: nhibernate


    【解决方案1】:

    如果您确定对象在缓存中,Session.Get(id) 将不会命中数据库。这样做可能是最简单的,然后Evict 你得到的对象:

    Model m = Session.Get(id);
    Session.Evict(m);
    

    编辑

    我不清楚你是在谈论一级缓存还是二级缓存。以上将从一级缓存中驱逐一些东西。要从二级缓存中逐出,请使用 SessionFactory 上的 evict 方法。

    根据评论进行编辑

    在这种情况下,你可以试试Session.Load

    Model m = Session.Load(id);
    Session.Evict(m);
    

    如果m 在缓存中,Session.Load 将返回您可以驱逐的实例。如果不是,它会返回一个代理(没有数据库命中)。我的测试表明,如果您尝试驱逐代理,Session.Evict 不会抛出,所以这应该可以工作。

    【讨论】:

    • 这是正确的做法。 Get 返回模型的代理并设置 id
    • 我说的是一级缓存。我不确定它是否在缓存中。有时有时不是。在任何这种情况下,我都想从缓存中清除它。如果它不在缓存中,您的代码可能会对数据库产生真正的影响。所以我想要两种情况下的相同代码。所以数据操作操作只是确保缓存中没有数据。
    • 关于最后的“编辑以回应评论”。如果它在操作时不会命中 Evict 内的数据库,并且不在缓存中的实体也不例外,我会说你的解决方案是最好的。
    • @Yauhen 在我的测试中似乎没有遇到数据库命中。验证这是否适合您,如果是,请不要忘记将我的答案标记为已接受!
    • Session.Load 确实 如果对象尚未在缓存中,则转到数据库,它只是在缓存时避免了无用的查询。它返回的代理使用来自该查询的数据进行初始化。
    【解决方案2】:

    听起来您可以为此使用无状态会话,而根本不用缓存。

    【讨论】:

    • 我在考虑无状态会话。这是个好主意。但是我写了太多的代码(比如 152 种业务方法)。如果我全部更改为无状态会话。我会用很多代码来测试。至少因为所有操作都将使用相同类型的会话。在我看来,某些 DAO 将使用一种类型的 sesison 而其他人将使用另一种类型的会话,这似乎是不明智的。
    • 基本思想是当您要导航关系和更新持久对象时使用普通会话,而当您只想运行查询并返回 POCO 对象时使用无状态会话。
    猜你喜欢
    • 1970-01-01
    • 2012-06-28
    • 2016-04-28
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多