如何解决NHibernate延迟加载错误“没有会话或会话被关闭”?

时间:2010-03-25 22:41:32

标签: asp.net-mvc nhibernate fluent-nhibernate lazy-loading

我正在使用ASP.NET MVC,NHibernate和Fluent Hibernate开发一个网站,并在我尝试访问子对象时收到错误“没有会话或会话已关闭”。

这些是我的域类:

public class ImageGallery {
    public virtual int Id { get; set; }
    public virtual string Title { get; set; }
    public virtual IList<Image> Images { get; set; }
}

public class Image {
    public virtual int Id { get; set; }
    public virtual ImageGallery ImageGallery { get; set; }
    public virtual string File { get; set; }
}

这些是我的地图:

public class ImageGalleryMap:ClassMap<ImageGallery> {
    public ImageGalleryMap() {
        Id(x => x.Id);
        Map(x => x.Title);
        HasMany(x => x.Images);
    }
}

public class ImageMap:ClassMap<Image> {
    public ImageMap() {
        Id(x => x.Id);
        References(x => x.ImageGallery);
        Map(x => x.File);
    }
}

这是我的Session Factory助手类:

public class NHibernateSessionFactory {
    private static ISessionFactory _sessionFactory;
    private static ISessionFactory SessionFactory {
        get {
            if(_sessionFactory == null) {
                _sessionFactory = Fluently.Configure()
                    .Database(MySQLConfiguration.Standard.ConnectionString(MyConnString))
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ImageGalleryMap>())
                    .ExposeConfiguration(c => c.Properties.Add("hbm2ddl.keywords", "none"))
                    .BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }
    public static ISession OpenSession() {
        return SessionFactory.OpenSession();
    }
}

当我使用此代码从数据库中获取 ImageGallery 时,一切正常:

IImageGalleryRepository igr = new ImageGalleryRepository();
ImageGallery ig = igr.GetById(1);

但是,当我尝试使用此代码

访问图像子对象时
string imageFile = ig.Images[1].File;

我收到此错误:

正在初始化[Entities.ImageGallery#1] - 无法初始化角色集合:Entities.ImageGallery.Images,没有关闭会话或会话

有人知道我该如何解决这个问题?

非常感谢!

修改

我的GetById方法是:

    public ImageGallery GetById(int id) {
        using(ISession session = NHibernateSessionFactory.OpenSession()) {
            return session.Get<ImageGallery>(id);
        }
    }

3 个答案:

答案 0 :(得分:8)

据推测,您的GetById正在关闭会话。这可以是明确的,也可以通过使用声明。

更好的会话管理方法是 Open View in View 模式。 NHibernate会话创建起来很便宜,所以在每个请求开始时创建一个会话,并在结束时关闭它。

// in global.asax.cs

public void Application_Start()
{
    BeginRequest += delegate {
        CurrentSessionContext.Bind( sessionFactory.OpenSession());
    };

    EndRequest += delegate {
        var session = sessionFactory.GetCurrentSession();
        if (null != session) {
            session.Dispose();
        }
        CurrentSessionContext.Unbind(sessionFactory);
    };
}

// in your NHibernateSessionFactory class
public static ISession OpenSession() {
    return SessionFactory.GetCurrentSession();
}

使用DI容器,我们可以为每个请求注入带有作用域的实例。

// ninject example
Bind<ISession>()
    .ToMethod( ctx => sessionFactory.GetCurrentSession() )
    .InRequestScope();

答案 1 :(得分:3)

访问ig

时,用于获取ig.Images[1]的会话必须处于活动状态

我通常通过在所有存储库调用之前实例化会话,将会话引用传递给存储库构造函数并在存储库类中使用该引用来实现此目的

答案 2 :(得分:2)

我不确定这是否适用于此,但这是(或至少曾经是)Java MVC框架中的常见问题。通常在您的操作中创建会话时发生。当您的操作完成执行时,会话对象超出范围并关闭/刷新会话。然后,当视图尝试呈现您检索的对象集合时,它会尝试使用已关闭的会话加载延迟加载的集合。

如果你在项目中使用依赖注入,你可以让你的DI框架为你实例化会话,并将它作为构造函数参数传递给你的控制器。您还可以指定DI容器应将NHibernate会话的范围与HTTP请求相同。这将使会话保持活动状态,直到请求结束(视图已完成渲染)。

我在项目和工作中使用了Autofac作为DI容器,对此非常满意。