English 中文(简体)
Don t flush the session after an exception occurs - NHibernate
原标题:

I am developing a ASP.NET MVC web app under .NET 3.5, NHibernate and hosted on Windows Azure. When, the webapp is run from the local development fabric it works fine. Yet, when I move it to Windows Azure, every insert performed from the MVC web role ends up with the exception listed below.

Any idea what s wrong with my NHibernate logic? (might be the session management, not sure)

[AssertionFailure: null id in Lokad.Translate.Entities.User entry (don t flush the Session after an exception occurs)] NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(Object obj, IEntityPersister persister, Object 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 event) +158 NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event) +469 NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) +339 NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) +85 NHibernate.Impl.SessionImpl.Flush() +275 NHibernate.Transaction.AdoTransaction.Commit() +236 Lokad.Translate.Repositories.PageRepository.Create(Page page) Lokad.Translate.Controllers.PagesController.Create(Page page) 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 parameters) +31 System.Web.Mvc.<>c__DisplayClassa.b__7() +88 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func1 continuation) +534 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +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

Note that I am using _session.FlushMode = FlushMode.Commit; and that the User is used in a custom 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];
        }
    }
}
最佳回答

I have finally found a solution to my own problem. In case people would be interested, I am posting the solution here.

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"};
        }
    }
}

Basically what was happening is that the RoleProvider repository does not seem to have the same lifecycle than regular in-view / in-controller repositories. As a result, at the time the RoleProvider is called, the NHibernate session has already been disposed causing the exception observed here above.

I have replaced the code by the following one here above. This one has its own NHibernate session management, and ends up working fine.

问题回答

You should never catch exceptions and ignore them during a NHibernate transaction.

I try to explain why.

There could be exceptions for instance caused by constraints in the database. (it could also be caused by mapping problems, exceptions thrown by properties or anything else.) NHibernate tries to synchronize the state in memory with the database. This is done on commit - and sometimes before queries to make sure that queries are done on actual data. When this synchronization fails, the state in the database is something random, some changes are persisted, others are not. The only thing you can do in such a case is closing the session.

Consider that decisions and calculations in your code are based on values in memory. But - in case of an ignored exception, this values are not the values in the database, they will never be there. So your logic will decide and calculate on fantasy-data .

By the way, it is never a good idea to catch any exception (untyped) and ignore them. You should always know the exceptions you handle, and be sure that you can continue.

What you re doing here is swallowing programming errors. Believe me, the system will not be more stable. The question is only: do you notice the error when it occurs, or do you ignore it there and even persist the result of the error to the database? When you do the latter, you don t have to be surprised when your database is inconsistent and other error arise when you try to get the data from the database. And you will never ever find the code that is the actual cause of the error.

This exception can occur if your column names include reserved words (e.g. use status as a column name and it will become impossible to Save)





相关问题
WebForms and ASP.NET MVC co-existence

I am trying to make a WebForms project and ASP.NET MVC per this question. One of the things I ve done to make that happen is that I added a namespaces node to the WebForms web.config: <pages ...

Post back complex object from client side

I m using ASP.NET MVC and Entity Framework. I m going to pass a complex entity to the client side and allow the user to modify it, and post it back to the controller. But I don t know how to do that ...

Create an incremental placeholder in NHaml

What I want to reach is a way to add a script and style placeholder in my master. They will include my initial site.css and jquery.js files. Each haml page or partial can then add their own required ...

asp.net mvc automapper parsing

let s say we have something like this public class Person { public string Name {get; set;} public Country Country {get; set;} } public class PersonViewModel { public Person Person {get; ...

structureMap mocks stub help

I have an BLL that does validation on user input then inserts a parent(PorEO) and then inserts children(PorBoxEO). So there are two calls to the same InsertJCDC. One like this=>InsertJCDC(fakePor)...

ASP.NET MVC: How should it work with subversion?

So, I have an asp.net mvc app that is being worked on by multiple developers in differing capacities. This is our first time working on a mvc app and my first time working with .NET. Our app does not ...

System.Web.Mvc.Controller Initialize

i have the following base controller... public class BaseController : Controller { protected override void Initialize(System.Web.Routing.RequestContext requestContext) { if (...

热门标签