刷新页面时NHibernate会话关闭

时间:2014-02-12 20:29:40

标签: asp.net-mvc asp.net-mvc-4 session nhibernate

这是我今天遇到的另一个奇怪的问题!!!我使用nhibernate创建了MVC 4应用程序。并在我的HomeController上添加了一个名为[LoggingNHibernateSessionAttribute]的过滤器属性,用于管理每个操作的会话。我遵循了'ASP.NET MVC4和Apress发布的Web API'。

public class LoggingNHibernateSessionAttribute : ActionFilterAttribute
{
    private readonly IActionLogHelper _actionLogHelper;
    private readonly IActionExceptionHandler _actionExceptionHandler;
    private readonly IActionTransactionHelper _actionTransactionHelper;

    public LoggingNHibernateSessionAttribute()
        : this(WebContainerManager.Get<IActionLogHelper>(),
        WebContainerManager.Get<IActionExceptionHandler>(),
        WebContainerManager.Get<IActionTransactionHelper>())
    {
    }

    public LoggingNHibernateSessionAttribute(
        IActionLogHelper actionLogHelper,
        IActionExceptionHandler actionExceptionHandler,
        IActionTransactionHelper actionTransactionHelper)
    {
        _actionLogHelper = actionLogHelper;
        _actionExceptionHandler = actionExceptionHandler;
        _actionTransactionHelper = actionTransactionHelper;
    }

    public override void OnActionExecuting(ActionExecutingContext actionExectingContext)
    {
        _actionLogHelper.LogEntry(actionExectingContext.ActionDescriptor);
        _actionTransactionHelper.BeginTransaction();
    }

    public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
    {
        _actionTransactionHelper.EndTransaction(actionExecutedContext);
        _actionTransactionHelper.CloseSession();
        _actionExceptionHandler.HandleException(actionExecutedContext);
        _actionLogHelper.LogExit(actionExecutedContext.ActionDescriptor);
    }
}

ActionTransactionHelper

  public class ActionTransactionHelper : IActionTransactionHelper
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ICurrentSessionContextAdapter _currentSessionContextAdapter;

    public ActionTransactionHelper(
        ISessionFactory sessionFactory,
        ICurrentSessionContextAdapter currentSessionContextAdapter)
    {
        _sessionFactory = sessionFactory;
        _currentSessionContextAdapter = currentSessionContextAdapter;
    }

    public void BeginTransaction()
    {
        var session = _sessionFactory.GetCurrentSession();
        if (session != null)
        {
            session.BeginTransaction();
        }
    }

    public bool TransactionHandled { get; private set; }

    public void EndTransaction(ActionExecutedContext filterContext)
    {
        var session = _sessionFactory.GetCurrentSession();

        if (session == null) return;
        if (!session.Transaction.IsActive) return;

        if (filterContext.Exception == null)
        {
            session.Flush();
            session.Transaction.Commit();
        }
        else
        {
            session.Transaction.Rollback();
        }

        TransactionHandled = true;
    }

    public bool SessionClosed { get; private set; }

    public void CloseSession()
    {
        if (_currentSessionContextAdapter.HasBind(_sessionFactory))
        {
            var session = _sessionFactory.GetCurrentSession();
            session.Close();
            session.Dispose();
            _currentSessionContextAdapter.Unbind(_sessionFactory);

            SessionClosed = true;
        }
    }
}

运行应用程序时,我可以在dataBase中保存实体。但是当我点击刷新按钮并且异常抛出指示会话关闭时。

我不知道为什么会这样。 (我搜索并找到了这个NHibernate throwing Session is closed,但无法解决我的问题。)

在我的NinjectConfigurator中,我在所有注射中添加了inRequestScope(),但没有回答。我查看刷新页面会话时会打开。但我不知道为什么说会议结束了?!

更新

当我第一次运行应用程序时我可以创建一个新成员。但是当我点击刷新按钮时,会话将意外关闭!! 第一次运行:

  1. 一切正常
  2. 点击刷新按钮后

    1. 新会话绑定到当前上下文。
    2. 新会话将注入存储库(会话已打开)
    3. ActionTransactionHelper调用beginTransaction() 4- customMembership createUser(....)调用 5-但是当在存储库会话中调用的_userRepositoy.save(用户)被关闭时!!!!
    4. 注意:但是当没有调用endTransaction和closeSession时。会议如何结束?  如果我在onActionExecute()中评论closeSession()。会话总是开放的,如果刷新页面,一切都很好。  我检查了很多,尝试了不同的方式。它只发生在第二次我想用我的customMembership进行CRUD操作时。

      对于其他实体来说,它就像一个魅力! enter image description here 我已经上传了我的示例代码。用于测试只是创建和清空数据库并更改连接字符串。然后转到localHost:***** / api / categories(用户和传递不需要)

      下载示例项目: 大小:47 MB https://www.dropbox.com/s/o63wjng5f799fii/Hashem-MVC4ServicesBook.rar

      大小:54 MB 邮编格式:https://www.dropbox.com/s/smrsbz4cbtznx1y/Hashem-MVC4ServicesBook2.zip

2 个答案:

答案 0 :(得分:1)

这里非常重要的一点可能是NHibernate的本质。 NHibernate及其Session在ASP.NET MVC中的寿命更长,可以预期。我的意思不仅仅是在

之内
  • ActionExecuting(控制器操作启动)
  • ActionExecuted(调用视图或重定向)

会话实际上也必须通过呈现的阶段生活。因为,我们可以在“Action()”中加载一些代理,但是它的集合只能在lazily呈现期间加载View。因此,即使在这些阶段,Session必须打开(从请求开始的同一个会话)

  • ResultExecuting(代理可以开始只在这里加载)
  • ResultExecuted(几乎全部完成,让我们关闭会话)

其他的话...... 通过完整的请求保持会话开放。从授权开始,直到呈现内容。

注意:Anohter提示,只是为了确保一切正常,我正在使用这个场景(也许你也这样做)

  1. 客户端FORM即将数据发送到服务器。方法是POST,Action是Update()
  2. 已发送的FORM将发送到服务器,触发Action Update() - 所有事务处理(如上所述)
  3. 一旦NHibernate将数据持久存储到数据库中,Update()操作就会结束,并重定向到操作
    • Detail()如果一切正常或
    • 如果出现问题,
    • Edit()
  4. 用户浏览器已重定向到操作详细信息或编辑。因此,如果用户执行REFRESH,则刷新Detail或Edit。根本没有调用Update()(它是POST方法)
  5. 实际上,步骤 1。是其中一项操作DetailEdit。在这种情况下,我们已经面临这个问题......

答案 1 :(得分:0)

您有此错误,因为 Asp.Net MVC不会为每个请求创建LoggingNHibernateSessionAttribute的新实例。当您第一次请求操作然后使用此实例时,它会创建一个新实例未来的实例。

行为如下:

  1. 首次调用Post - &gt;创建了“LoggingNHibernateSession”的新实例
  2. 首次调用Put - &gt;创建了另一个'LoggingNHibernateSession'实例
  3. 第二次调用Put - &gt;使用上一步中的“LoggingNHibernateSession”实例
  4. 首次调用Delete - &gt;创建了另一个'LoggingNHibernateSession'实例

    [LoggingNHibernateSession]
    public JsonResult Post(Dto data)
    {
        /* ... */
    }
    
    [LoggingNHibernateSession]
    public JsonResult Put(int id, Dto data)
    {
        /* ... */
    }
    
    [LoggingNHibernateSession]
    public JsonResult Delete(int id)
    {
        /* ... */
    } 
    
  5. 可以使用构造函数中的Func<IActionLogHelper>代替IActionLogHelper来解决。 IActionLogHelper的实例可以在OnActionExecuting方法中初始化。

    public class LoggingNHibernateSessionAttribute : ActionFilterAttribute
    {
        /* your code */
        private readonly Func<IActionTransactionHelper> _getActionTransactionHelper;
        private IActionTransactionHelper _actionTransactionHelper;
    
        public LoggingNHibernateSessionAttribute()
            : this(WebContainerManager.Get<IActionLogHelper>(),
            WebContainerManager.Get<IActionExceptionHandler>(),
            () => WebContainerManager.Get<IActionTransactionHelper>())
        {
        }
    
        public LoggingNHibernateSessionAttribute(
            IActionLogHelper actionLogHelper,
            IActionExceptionHandler actionExceptionHandler,
            Func<IActionTransactionHelper> getActionTransactionHelper)
        {
            _actionLogHelper = actionLogHelper;
            _actionExceptionHandler = actionExceptionHandler;
            _getActionTransactionHelper = getActionTransactionHelper;
            _actionTransactionHelper = null;
        }
    
        public override void OnActionExecuting(ActionExecutingContext actionExectingContext)
        {
            _actionTransactionHelper = _getActionTransactionHelper();
            _actionLogHelper.LogEntry(actionExectingContext.ActionDescriptor);
            _actionTransactionHelper.BeginTransaction();
        }
    
        /* your code */
    }