【问题标题】:Don't flush the session after an exception occurs - NHibernate发生异常后不要刷新会话 - NHibernate
【发布时间】:2009-11-30 10:36:30
【问题描述】:

我正在开发一个基于 .NET 3.5、NHibernate 并托管在 Windows Azure 上的 ASP.NET MVC Web 应用程序。当 web 应用程序从本地开发结构运行时,它可以正常工作。然而,当我将它移动到 Windows Azure 时,从 MVC Web 角色执行的每个插入都以下面列出的异常结束。

知道我的 NHibernate 逻辑有什么问题吗? (可能是会话管理,不确定)

[AssertionFailure:Lokad.Translate.Entities.User 条目中的空 id(发生异常后不刷新 Session)] NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(对象 obj,IEntityPersister 持久化,对象 id,EntityMode entityMode)+292 NHibernate.Event.Default.DefaultFlushEntityEventListener.GetValues(Object entity, EntityEntry entry, EntityMode entityMode, Boolean mightBeDirty, ISessionImplementor session) +93 NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent 事件) +158 NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent 事件)+469 NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent 事件)+339 NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent 事件) +85 NHibernate.Impl.SessionImpl.Flush() +275 NHibernate.Transaction.AdoTransaction.Commit() +236 Lokad.Translate.Repositories.PageRepository.Create(页面页面) Lokad.Translate.Controllers.PagesController.Create(页面页面) lambda_method(ExecutionScope, ControllerBase, Object[]) +69 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +251 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 参数) +31 System.Web.Mvc.c__DisplayClassa.b__7() +88 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter 过滤器,ActionExecutingContext preContext,Func1 continuation) +534 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList1 过滤器,ActionDescriptor actionDescriptor,IDictionary`2 参数)+312 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +856 System.Web.Mvc.Controller.ExecuteCore() +185 System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +221 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +586 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +177

请注意,我使用的是_session.FlushMode = FlushMode.Commit;,而User 用于自定义RoleProvider

public class SimpleRoleProvider : RoleProvider 
{
    readonly UserRepository Users = new UserRepository();

    public override string[] GetRolesForUser(string username)
    {
        try
        {
            var user = Users.Get(username);

            // no role if user is not registered
            if (null == user) return new string[0];

            // default role for registered user
            return user.IsManager ? new[] { "Manager", "User" } : new[] { "User" };
        }
        catch (Exception)
        {
            // role should not fail in case of DB issue.
            return new string[0];
        }
    }
}

【问题讨论】:

    标签: asp.net-mvc nhibernate azure


    【解决方案1】:

    您不应该在 NHibernate 事务期间捕获异常并忽略它们。

    我试图解释原因。

    可能存在异常,例如由数据库中的约束引起的。 (它也可能是由映射问题、属性抛出的异常或其他任何原因引起的。) NHibernate 尝试将内存中的状态与数据库同步。这是在提交时完成的 - 有时在查询之前完成,以确保查询是在实际数据上完成的。当同步失败时,数据库中的状态是随机的,一些更改会被持久化,而另一些则不会。在这种情况下,您唯一能做的就是关闭会话。

    请考虑代码中的决策和计算是基于内存中的值。但是 - 如果出现忽略的异常,这些值不是数据库中的值,它们永远不会存在。所以你的逻辑将决定和计算“幻想数据”。

    顺便说一句,捕获任何异常(无类型)并忽略它们绝不是一个好主意。您应该始终知道您处理的异常,并确保您可以继续。

    您在这里所做的是吞下编程错误。相信我,系统不会更稳定。问题只是:当错误发生时您是否注意到错误,或者您是否在那里忽略它,甚至将错误结果保存到数据库?当您执行后者时,当您尝试从数据库中获取数据时,当您的数据库不一致以及出现其他错误时,您不必感到惊讶。而且你永远也找不到真正导致错误的代码。

    【讨论】:

    • 嗨 Stefan,非常感谢您的详细解释。我终于找到了解决办法。问题与您的解释无关,但它仍然是一个很好的观点。
    • 非常感谢您提供如此详细的解释,我遇到了同样的错误,这是由吞咽 NHibernate 异常引起的。通过阅读您的帖子,我节省了很多时间。
    【解决方案2】:

    我终于找到了解决我自己问题的方法。如果人们有兴趣,我会在此处发布解决方案。

    public class SimpleRoleProvider : RoleProvider 
    {
        // isolated session management for the RoleProvider to avoid
        // issues with automated management of session lifecycle.
    
        public override string[] GetRolesForUser(string username)
        {
            using (var session = GlobalSetup.SessionFactory.OpenSession())
            {
                var users = new UserRepository(session);
                var user = users.Get(username);
    
                // no role if user is not registered
                if (null == user) return new string[0];
    
                // default role for registered user
                return user.IsManager ? new[] {"Manager", "User"} : new[] {"User"};
            }
        }
    }
    

    基本上,RoleProvider 存储库的生命周期似乎与常规的视图内/控制器内存储库不同。因此,在调用 RoleProvider 时,NHibernate 会话已经被释放,导致上面观察到的异常。

    我已将代码替换为上面的以下代码。这个有自己的 NHibernate 会话管理,最终可以正常工作。

    【讨论】:

      【解决方案3】:

      如果您的列名包含保留字,则可能会发生此异常(例如,使用 status 作为列名,将无法保存)

      【讨论】:

        猜你喜欢
        • 2011-07-15
        • 2016-06-17
        • 2014-05-12
        • 1970-01-01
        • 2023-04-07
        • 2014-10-06
        • 1970-01-01
        • 2012-01-28
        • 1970-01-01
        相关资源
        最近更新 更多