Monday, October 13, 2014

Head-scratching auto-wired-up event


If you received this kind of error..

Server Error in '/' Application.


Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:


[NullReferenceException: Object reference not set to an instance of an object.]
   System.Web.PipelineModuleStepContainer.GetStepArray(RequestNotification notification, Boolean isPostEvent) +22
   System.Web.PipelineStepManager.ResumeSteps(Exception error) +1324
   System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb) +95
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +186


Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.34212




..chances are that explicitness in code is important to you too, hence when following a how-to post, you'll be inclined to believe that when an event is missing a subscription process the author just forgot to mention it in his post. Naturally, when trying out those code, we will wire the method to the event:

using System;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

namespace ReadyAspNetMvc
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);


            this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
        }

        void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Hello");
        }
    }
}


Alas, the world is not gracious to folks who wants explicitness. Doing that code results to yellow screen of death above. Good thing there is stackoverflow, there are two solutions to the problem. One is to just accept the magical auto-wired-up events based on the exact method signature, doing that, we have to remove the event subscription from our Global.asax.cs:

using System;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

namespace ReadyAspNetMvc
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);           
        }

        void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Hello");
        }
    }
}


Oops.. not so fast, that still has an error. The magical signature is not dependent on the class name, the magical method signature for PostAuthenticateRequest is exactly Application_PostAuthenticateRequest:
using System;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

namespace ReadyAspNetMvc
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);           
        }

        void Application_PostAuthenticateRequest(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Hello");
        }
    }
}


Another method is we wire the event explicitly, it can't be done on Application_Start though, it must be done on Init:

using System;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

namespace ReadyAspNetMvc
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);           
        }

        public override void Init()
        {
            this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
            base.Init();
        }

        void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Hello");
        }

        
    }
}


Application_Start is an auto-wired-up event, it looks like we can't make it explicit though. See: http://stackoverflow.com/questions/4677866/how-does-global-asax-postauthenticaterequest-event-binding-happen#comment5156961_4677905



Happy Coding!

No comments:

Post a Comment