【问题标题】:NHibernate: Prevent Refresh to trigger uninitialized CollectionsNHibernate:防止刷新触发未初始化的集合
【发布时间】:2011-03-24 01:13:12
【问题描述】:

我有一个带有集合的实体,它包含具有更多实体引用的其他实体。当实体被加载并稍后刷新时,延迟加载的集合被触发,并且从数据库中提取了很多我不需要的东西。那么如何防止刷新触发未初始化的集合呢? 我写了一个小测试来显示它。

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();
    }
}

以及下面的测试

    [Fact]
    public void Test()
    {
        m_session.Save(new SimpleClass
        {
            Id = 1,
            Childs = new List<ChildClass>
            {
                new ChildClass { Id = 1 },
                new ChildClass { Id = 2 },
                new ChildClass { Id = 3 },
            }
        });
        m_session.Flush();
        m_session.Clear();
        log.Debug("Start Test");

        var simple = m_session.Get<SimpleClass>(1);

        var persistentcollection = simple.Childs as IPersistentCollection;
        Assert.False(persistentcollection.WasInitialized, "Before Refresh, collection should not be initialized but was");

        log.Debug("Refresh");
        m_session.Refresh(simple);

        Assert.False(persistentcollection.WasInitialized, "After Refresh, collection should not be initialized but was");
    }

输出: 刷新后,集合不应该被初始化,而是被

澄清一下:

也许我可以在不删除 cascade.all 的情况下生活,但绝对不能没有 cascade.refresh。 如果已初始化,我希望刷新级联到集合,否则不会,因为它会在使用时延迟加载新数据。

我在刷新事件中使用了 NHibernate 代码。如果我删除提取配置文件:

DefaultRefreshEventListener {
public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
{
    [...]
    string previousFetchProfile = source.FetchProfile;
    //source.FetchProfile = "refresh";
    object result = persister.Load(id, obj, @event.LockMode, source);
    //source.FetchProfile = previousFetchProfile;
    [...]
}

我在日志中得到了我期望的结果(删除了 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

我仍然不确定它的其他含义

【问题讨论】:

    标签: c# nhibernate fluent-nhibernate


    【解决方案1】:

    如果您查看为 Refresh 生成的 SQL,您会看到它使用左连接加载 Children(而不是使用第二个查询,因此不会延迟加载),而第一个 Get 操作没有加入。

    Refresh() 使用级联设置并将加载级联设置为“全部”的集合,因此您的问题在这里:

    HasMany(sc => sc.Childs)
                .Cascade.All()
                .LazyLoad();
    

    如果你写Cascade.NoneCascade.SaveUpdate,它应该会得到想要的结果。当然,我不知道你是否绝对需要Cascade.All。在这种情况下,我建议将 Refresh() 替换为以下内容:

    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。我不能用新获取的实例简单地替换简单,因为我不知道是否还有其他实例我也必须刷新。
    • @Firo 如果我理解正确,您可能必须将刷新逻辑放在您的 DAL 中。查看我的编辑。
    • 不幸的是,可能还有其他简单的引用,所以我不能用新获取的简单切换。在我的场景中,我有 simpleclass 1-lazy->n childclassA 1-nonlazy->n childclassB。刷新似乎总是加载带有所有依赖对象的 simpleclass,无论懒惰如何导致在我的 childclassB 场景中选择 n+1。我切换到lazyload childclassB,这带来了更好的性能,这对我来说已经足够了(虽然没有解决我的问题)我接受了答案,因为在大多数情况下你的解决方案会有所帮助。
    猜你喜欢
    • 2017-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 2013-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多