NHibernate抛出“Session is closed”错误

4

我在这里询问,因为我感到困惑。我正在构建一个MVC 3网站,当我用一个用户浏览网页时一切正常。但是,如果我疯狂地刷新页面,最终会出现“会话已关闭”的错误。

我已经几乎完全隔离了所有的仓库,以此来排除故障,所以我知道它出错在主页上。仓库中唯一被调用的事情是从数据库表中获取用户姓名。

我使用Postgres作为数据库,并使用NauckIT的ASP.NET会员提供程序。主要数据库也是Postgres(但是另一个数据库)。

以下代码用于管理会话:

public class MvcApplication : System.Web.HttpApplication
{
    public static ISessionFactory SessionFactory = 
             NHibernateHelper.GetSessionFactory();

    public MvcApplication()
    {
        this.BeginRequest += MvcApplication_BeginRequest;
        this.EndRequest += MvcApplication_EndRequest;
    }

    void MvcApplication_BeginRequest(object sender, EventArgs e)
    {
        CurrentSessionContext.Bind(SessionFactory.OpenSession());
    }
    void MvcApplication_EndRequest(object sender, EventArgs e)
    {            
        CurrentSessionContext.Unbind(SessionFactory).Dispose();
    }
}

获取登录信息的代码如下:

    public Login GetCurrentLogin()
    {
        return Session.Query<Login>().FirstOrDefault(l => l.UserID == UserAccessRepository.UserID);
    }
UserAccessRepository仅从表单身份验证cookie中获取userid
会话是使用以下方式注入存储库的:
        ninjectKernel.Bind<IUserRepository>().To<NHUserRepository>();
        ninjectKernel.Bind<ILeagueRepository>().To<NHLeagueRepository>().InThreadScope();
        ninjectKernel.Bind<ISession>()
            .ToMethod(m => MvcApplication.SessionFactory.GetCurrentSession())

SessionFactory 来源于:

public class NHibernateHelper
{
    private static ISessionFactory _sessionFactory;

    public static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
            {       var rawConfig = new Configuration();
                rawConfig.SetNamingStrategy(new PostgresNamingStrategy());
                var configuration = Fluently.Configure(rawConfig)
                    .Database(PostgreSQLConfiguration.Standard.ConnectionString(ConnectionString).ShowSql().Dialect("NHibernate.Dialect.PostgreSQL82Dialect"))
                    .Mappings(m =>
                                m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(new AutoMapConfiguration())
                )).ExposeConfiguration(cfg => 
                    cfg.SetProperty("current_session_context_class", "web")
                _sessionFactory = configuration.BuildSessionFactory();
                Debug.WriteLine("Built SessionFactory");
            }
            return _sessionFactory;

需要澄清的是,在我点击页面浏览时,一切正常,但当我疯狂地按F5时,会出现会话关闭的问题。

更新: 不确定是否相关,但我在BaseController中看到了这个问题,出现在OnActionExecuting方法内部。在上面的方法中似乎已经解决了这个问题。


我的第一个问题是:为什么要使用整个Begin/EndRequest设置,而不是将每个会话生命周期范围的ISession注入控制器? - Patryk Ćwiek
我找到的第一个会话每个请求的示例...如果有其他替代方案,我会很高兴的。有链接吗? - Martin
@Trustme-I'maDoctor 这是一种相当常见的做法...不是说这是最好的方法,但我确实在很多地方看到过它。 - BlakeH
@SonicTheLichen 奇怪,这是我第一次看到它。用正确的生命周期来注入依赖关系似乎更直接...但是无论如何 :) 无论如何,这里有一个我在谷歌上搜索的 Ninject + ASP.NET MVC 的简单链接。我不知道 Ninject,但我相信有一个选项可以将依赖关系的生命周期范围设置为“每个HTTP请求”。 - Patryk Ćwiek
Ninject具有InRequestScope()方法。 - BlakeH
显示剩余3条评论
1个回答

1

在Web应用程序中,不应使用InThreadScope(),而应使用InRequestScope()。编辑阅读Object Scopes-它最近已更新,不了解它将早晚浪费您的时间!

如果您想使东西在会员提供程序和请求处理中工作,您需要搜索Ninject自定义提供程序(可能类似于here)。


会员提供程序是独立的,并且使用直接的ADO.NET而不是NHibernate,因此在这部分中不使用Session。我将尝试替换它并报告结果。 - Martin
@Martin Good - 这样你就少了一个困惑的来源。底线是,你需要在除了[未指定(隐含为瞬态)或]瞬态之外的作用域中绑定需要“Dispose”的东西,但是b)使用InThreadScope()可以防止在请求结束时进行处理,并在将来的某个时间点引起随机处理(并且跨多个请求使用这种对象只是碰巧在同一线程上调度[对于它们的某些处理])。 - Ruben Bartelink
:D 成功了,不得不把所有东西都改成 InRequestScope。随着一些优化(即找出哪些实际上需要 InRequestScope,哪些不需要),我可能能够逐渐减少它,但现在它可以工作了。 - Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接