C# NHibernate:防止刷新以触发未初始化的集合
我有一个具有集合的实体,其中包含对实体有更多引用的其他实体。当实体加载并随后刷新时,将触发延迟加载的集合,并从数据库中提取许多我不需要的内容。那么如何防止刷新触发未初始化的集合呢? 我写了一个小测试来说明这一点C# NHibernate:防止刷新以触发未初始化的集合,c#,nhibernate,fluent-nhibernate,C#,Nhibernate,Fluent Nhibernate,我有一个具有集合的实体,其中包含对实体有更多引用的其他实体。当实体加载并随后刷新时,将触发延迟加载的集合,并从数据库中提取许多我不需要的内容。那么如何防止刷新触发未初始化的集合呢? 我写了一个小测试来说明这一点 class SimpleClass { public virtual int Id { get; set; } public virtual IList<ChildClass> Childs { get; set; } } class ChildClass
class SimpleClass
{
public virtual int Id { get; set; }
public virtual IList<ChildClass> Childs { get; set; }
}
class ChildClass
{
public virtual int Id { get; set; }
}
class SimpleClassMap : ClassMap<SimpleClass>
{
public SimpleClassMap()
{
Id(sc => sc.Id).GeneratedBy.Assigned();
HasMany(sc => sc.Childs)
.Cascade.All()
.LazyLoad();
}
}
class ChildClassMap : ClassMap<ChildClass>
{
public ChildClassMap()
{
Id(cc => cc.Id).GeneratedBy.Assigned();
}
}
我在日志中得到了我所期望的,在SQL中删除了别名:
不初始化集合
Session.Get
DEBUG - SELECT Id FROM "SimpleClass" WHERE Id=:p0;:p0 = 1
Refresh
DEBUG - SELECT Id FROM "SimpleClass" WHERE Id=:p0;:p0 = 1
初始化集合simple.Childs.Count
Session.Get
DEBUG - SELECT Id FROM "SimpleClass" WHERE Id=:p0;:p0 = 1
simple.Childs.Count();
DEBUG - SELECT SimpleClass_id, Id, Id FROM "ChildClass" WHERE SimpleClass_id=:p0;:p0 = 1
Refresh
DEBUG - SELECT Id FROM "ChildClass" WHERE Id=:p0;:p0 = 1
DEBUG - SELECT Id FROM "ChildClass" WHERE Id=:p0;:p0 = 2
DEBUG - SELECT Id FROM "ChildClass" WHERE Id=:p0;:p0 = 3
DEBUG - SELECT Id FROM "SimpleClass" WHERE Id=:p0;:p0 = 1
我仍然不确定此操作的其他含义,如果查看为刷新生成的SQL,您将看到它使用左连接而不是第二个查询加载子项,因此它不是延迟加载的,而第一个Get操作没有连接 刷新使用级联设置,并将加载级联设置为“全部”的集合,因此您的问题如下:
HasMany(sc => sc.Childs)
.Cascade.All()
.LazyLoad();
如果您编写Cascade.None或Cascade.SaveUpdate,它应该会得到所需的结果。当然,我不知道你是否真的需要Cascade。在这种情况下,我建议将刷新替换为以下内容:
m_session.Evict(simple);
simple = m_session.Get<Order>(1);
编辑:
您可以检查初始化状态并根据该状态进行刷新。不过,这可能不是最好的解决方案
class SimpleClassDAL
{
[...]
public void Refresh(ISession session, SimpleClass simple)
{
var persistentcollection = simple.Childs as IPersistentCollection;
if (persistentcollection.WasInitialized)
{
// get everything again
session.Refresh(simple);
}
else
{
// only refresh simple
session.Evict(simple);
simple = session.Get<SimpleClass>(simple.Id);
}
}
}
如果查看为刷新生成的SQL,您将看到它使用左联接而不是第二个查询加载子项,因此它不会延迟加载,而第一个Get操作没有联接 刷新使用级联设置,并将加载级联设置为“全部”的集合,因此您的问题如下:
HasMany(sc => sc.Childs)
.Cascade.All()
.LazyLoad();
如果您编写Cascade.None或Cascade.SaveUpdate,它应该会得到所需的结果。当然,我不知道你是否真的需要Cascade。在这种情况下,我建议将刷新替换为以下内容:
m_session.Evict(simple);
simple = m_session.Get<Order>(1);
编辑:
您可以检查初始化状态并根据该状态进行刷新。不过,这可能不是最好的解决方案
class SimpleClassDAL
{
[...]
public void Refresh(ISession session, SimpleClass simple)
{
var persistentcollection = simple.Childs as IPersistentCollection;
if (persistentcollection.WasInitialized)
{
// get everything again
session.Refresh(simple);
}
else
{
// only refresh simple
session.Evict(simple);
simple = session.Get<SimpleClass>(simple.Id);
}
}
}
我已经为现有应用程序实现了DAL。我不能简单地用新获取的实例替换simple,因为我不知道是否还有其他实例需要刷新。@Firo如果我理解正确,您可能需要将刷新逻辑放入DAL中。请参阅我的编辑。不幸的是,可能还有其他对simple的引用,因此我无法将simple切换为新获取的。在我的场景中,我使用了simpleclass 1-lazy->n childclassA 1-nonlazy->n childclassB。刷新似乎总是用所有依赖对象加载simpleclass,而不考虑懒散性,这导致在我的childclassB场景中选择n+1。我切换到lazyload childclassB,这导致了更好的性能,这对我来说已经足够了,尽管我没有解决我的问题,但我接受答案,因为在大多数情况下,您的解决方案都会有所帮助。我已经为现有应用程序实现了DAL。我不能简单地用新获取的实例替换simple,因为我不知道是否还有其他实例需要刷新。@Firo如果我理解正确,您可能需要将刷新逻辑放入DAL中。请参阅我的编辑。不幸的是,可能还有其他对simple的引用,因此我无法将simple切换为新获取的。在我的场景中,我使用了simpleclass 1-lazy->n childclassA 1-nonlazy->n childclassB。刷新似乎总是用所有依赖对象加载simpleclass,而不考虑懒散性,这导致在我的childclassB场景中选择n+1。我切换到lazyload childclassB,这导致了更好的性能,这对我来说已经足够了,虽然我没有解决我的问题,但我接受答案,因为在大多数情况下,您的解决方案会有所帮助。