有没有办法停止在NHibernate中引用子对象而不刷新会话或从会话缓存中删除其父对象?

时间:2010-06-29 22:04:29

标签: c# nhibernate

我正在使用NHibernate构建一个标准的旧胖客户端,其中我的大部分实体(请参阅class A)在应用程序启动时都会脱水并在应用程序生命周期内保持不变。这些偶然的引用是一些非常重量级的类(class B),它们封装了许多延迟加载代理的数据,因此它们只是根据需要从数据库中获取。

这很好用。问题是我想要稍后明确地卸载这些B类型 - 从我的角度来看,我想回到它们是未初始化的代理而不占用内存的状态。

由于似乎没有办法取消初始化代理,我计划简单地从缓存中删除B,删除对B的所有现有引用(我意识到我我必须自己跟踪它们,这很好)用一个从session.Load<B>获得的新的(未初始化的)B代理替换它们(即a.MyB = session.Load<B>(oldId);

然而,似乎NHibernate在缓存中保留对旧B的引用,并且在我从缓存中删除B或刷新之前,A不会被垃圾收集。会话。

我不确定为什么。我怀疑它为了属性比较的目的保留了引用,因此如果我请求session.SaveOrUpdate(a),它可以看到它是否需要返回数据库。但是,如果将关系设置为不级联,那么它真的重要吗?

流畅的映射&amp;下面的测试说明了这个问题。使用Make()创建数据库,然后Load()运行测试。请注意,如果您取消注释,则需要再次重新Make()

public class A
{
    internal int Id { get; private set; }

    public B B { get; set; }

    internal class Map : ClassMap<A>
    {
        public Map()
        {
            Id(x => x.Id);
            Not.LazyLoad();
            References(x => x.B)
                .Cascade.None()
                .LazyLoad(Laziness.Proxy);
        }
    }
}

public class B
{
    protected internal virtual int Id { get; private set; }

    public virtual A A { get; set; }

    public virtual void Fizz()
    {
    }

    internal class Map : ClassMap<B>
    {
        public Map()
        {
            Id(x => x.Id);
            LazyLoad();
            References(x => x.A)
                .Cascade.None();
        }
    }

    private static ISessionFactory BuildSF(bool rebuilddb)
    {
        return Fluently.Configure()
             .Database(SQLiteConfiguration.Standard.UsingFile("v.sqldb"))
             .Mappings(m =>
             {
                 m.FluentMappings.Add<A.Map>();
                 m.FluentMappings.Add<B.Map>();
             })
             .ExposeConfiguration(cfg =>
             {
                 //cfg.Interceptor = new Interceptor();
                 if (rebuilddb)
                     new SchemaExport(cfg).Create(false, true);
             })
             .BuildSessionFactory();
    }

    [Test]
    public void Make()
    {
        var sessionFactory = BuildSF(true);
        var sess = sessionFactory.OpenSession();

        var a = new A();
        var b = new B();
        a.B = b;
        b.A = a;

        sess.Save(a);
        sess.Save(b);
        sess.Flush();
    }


    [Test]
    public void Load()
    {
        var sessionFactory = BuildSF(false);
        var sess = sessionFactory.OpenSession();

        var a = sess.Load<A>(1);

        // just loaded a, B should be a uninitialized proxy
        Assert.That(! NHibernateUtil.IsInitialized(a.B)); 

        a.B.Fizz();
        // invoke on B, should now be initialized
        Assert.That(NHibernateUtil.IsInitialized(a.B)); 

        var weakB = new WeakReference(a.B);

        sess.Evict(a.B);
        //sess.Evict(a); // uncomment this to pass the final test
        a.B = null;
        //sess.Flush(); // or this

        System.GC.Collect();

        Console.WriteLine("Entities: " + sess.Statistics.EntityCount);
        foreach (var ek in sess.Statistics.EntityKeys)
            Console.WriteLine("\t" + ek.EntityName + " " + ek.Identifier);

        if (sess.Contains(a))
            Console.WriteLine("Session still contains a in cache");
        else
            Console.WriteLine("Session no longer holds a");

        Assert.IsFalse(weakB.IsAlive);
    }

在较低级别,我的问题是:我可以以某种方式删除对旧B的引用而不删除A或刷新吗?

在更高层次我的问题:我怎么能做我想要的?我立即想到解决这个问题的方法可能只是在A类上映射和id并手动管理B的持久性/持久性。但是当然这会将持久性问题流入我的域模型中,我的经验一直在尝试应用'对NHibernate的黑客攻击通常是“在兔子洞里面的仙境中的爱丽丝”。

1 个答案:

答案 0 :(得分:1)

我发现对于胖客户端或桌面应用程序,NHibernate会话管理详细here的方法是唯一“可行”的解决方案。这个解决方案的主旨是保持大多数对象与会话分离,除非他们:

  1. 正由用户主动编辑
  2. 已在应用中的其他位置更新。使用事件通知其他视图他们需要刷新数据
  3. 请注意,根据MSDN文章,我无法使无状态会话正常工作,因此我只使用普通会话,然后在事务完成时将其清除。