我正在使用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的黑客攻击通常是“在兔子洞里面的仙境中的爱丽丝”。
答案 0 :(得分:1)
我发现对于胖客户端或桌面应用程序,NHibernate会话管理详细here的方法是唯一“可行”的解决方案。这个解决方案的主旨是保持大多数对象与会话分离,除非他们:
请注意,根据MSDN文章,我无法使无状态会话正常工作,因此我只使用普通会话,然后在事务完成时将其清除。