11

I'm starting a web application with MVC3 and Ninject. There is one dependency that I also need in the Global.asax file that needs to be a singleton.

I thought it should be like this:

public class MvcApplication : NinjectHttpApplication
{
    IUserAuthentication _auth;

    public MvcApplication()
    {
        base.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
    }

    protected override IKernel CreateKernel()
    {
        var _kernel = new StandardKernel(new SecurityModule());
        _auth = _kernel.Get<IUserAuthentication>();

        return _kernel;
    }

    void MvcApplication_AuthenticateRequest(object sender, EventArgs e)
    {
        _auth.ToString();
    }

But then I saw that _auth is null when MvcApplication_AuthenticateRequest is called.

Then I tried like this:

public class MvcApplication : NinjectHttpApplication
{
    ItUserAuthentication _auth;
    IKernel _kernel;

    public MvcApplication()
    {
        _kernel = new StandardKernel(new SecurityModule());
        _auth = _kernel.Get<IUserAuthentication>();
        base.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
    }

    protected override IKernel CreateKernel()
    {
        return _kernel;
    }

    void MvcApplication_AuthenticateRequest(object sender, EventArgs e)
    {
        _auth.ToString();
    }

But now I can see that the constructor is being called several times, therefore I will have several IKernel, and I guess that singleton instances won't be so singleton in my app scope.

How should I do it? Using a static variable?

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
vtortola
  • 34,709
  • 29
  • 161
  • 263

5 Answers5

10

This is how we do it, I did some testing and my AuthService seems to go in his controller only once :

public class MvcApplication : NinjectHttpApplication
    {

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

        protected override IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            kernel.Load(Assembly.GetExecutingAssembly());

            kernel.Bind<ISession>().To<MongoSession>().InRequestScope();
            kernel.Bind<IAuthenticationService>().To<AuthenticationService>().InSingletonScope();
            kernel.Bind<IMailer>().To<Mailer>().InRequestScope();
            kernel.Bind<IFileProvider>().To<MongoFileProvider>().InRequestScope();

            return kernel;
        }

        protected override void OnApplicationStarted()
        {
            base.OnApplicationStarted();

            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
        }

        protected void Application_AuthenticateRequest(Object sender, EventArgs e)
        {
            if (HttpContext.Current.User != null)
            {
                if (HttpContext.Current.User.Identity.IsAuthenticated)
                {
                    if (HttpContext.Current.User.Identity is FormsIdentity)
                    {
                        var id = (FormsIdentity) HttpContext.Current.User.Identity;
                        var ticket = id.Ticket;
                        var authToken = ticket.UserData;
                        var authService = (IAuthenticationService)DependencyResolver.Current.GetService(typeof(IAuthenticationService));
                        var user = authService.GetUserForAuthToken(authToken);
                        if (user != null)
                        {
                            user.SetIdentity(HttpContext.Current.User.Identity);
                            HttpContext.Current.User = (IPrincipal) user;
                        }
                    }
                }
            }
        }
}

Hope it helps!

VinnyG
  • 6,883
  • 7
  • 58
  • 76
  • is it not a kill the fact that is calling DependencyResolver in every single request. – vtortola Apr 15 '11 at 09:57
  • I don't think so, @Remo should be able to tell you more than me but since I inject my _authService in most of my controller in the constructor it probably do the same and does'nt "cost" that mutch... – VinnyG Apr 15 '11 at 14:34
  • great. I think this will do till @Remo fix the issue. Thanks a million. – vtortola Apr 16 '11 at 14:51
5

The MVC extension injects the HttpApplication by default. But only property injection can be used! So just add a property decorated with the Inject attribute.

Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
  • That doesn't work either. _auth is null when MvcApplication_AuthenticateRequest is called – vtortola Apr 01 '11 at 16:01
  • 2
    @vtortola, are you using the latest version of Ninject Mvc3? The property should look like this: [Inject] public ItUserAuthentication Auth { get; set; } – Talljoe Apr 02 '11 at 16:46
  • I got "Ninject.Web.Mvc3-2.2.1.0-release-net-4.0.zip" , I think it's the last version. I tried that and it didn't work – vtortola Apr 02 '11 at 22:17
  • 1
    @vtortola I had a look at the code. With the introduction of the NuGet package this feature has been broken. As I'm currently updating the NuGet packages anyway to NuGet 1.2, I will fix this in the comming days. – Remo Gloor Apr 13 '11 at 21:14
  • BTW give me a shout here when you fix it :D. thanks a million :) – vtortola Apr 15 '11 at 10:05
  • @vtortola Fixed in Ninject.Extensions.MVC3 V2.2.2.0 – Remo Gloor May 10 '11 at 08:22
  • @Remo Gloor That's odd, I just got the latest via Nuget (although interestingly enough the Ninject nuget is version 2.2.1.4 while ninject.org has 2.2.2.0, Ninject.MVC3 via Nuget is 2.2.2.0) and using property injection like: [Inject] public ILogger Logger { get; set; } still leads to a null reference in Application_Start. Is the latest on Nuget not updated, or? And I'm assuming by Ninject.Extensions.MVC3 v2.2.2.0 you meant Ninject.MVC3 v2.2.2.0. – Ted May 27 '11 at 20:08
  • The website is a bit behind (2.2.0.0) because it's complicated atm to update it. MVC3 and Ninject do not have the same versions. They are updated induvidually. But all 2.2.x.x version combinations will work together. On github and nuget you will always find the latest release. They probably differ on the build number but in this case they are built using the same source code and only build scripts or unit tests have changed in this case. – Remo Gloor May 27 '11 at 20:14
  • @RemoGloor Hey Remo, does this work w/ using repositories bound to InRequestScope? It seems like after I tried to add a repository property injection in global.asax I started getting ObjectContext already disposed errors in my apps. No issues if I remove the property injection though... – B Z Feb 19 '12 at 22:57
  • 1
    @B Z Injecting an object with the lifetime of a request into an object that has a longer lifetime makes no sense. An object should only depend on objects with the same or a longer lifecycle. – Remo Gloor Feb 19 '12 at 23:33
  • @RemoGloor ah, I think I see the missing link...so the lifetime for HttpApplication is not InRequestScope? I haven't looked at the src, what is the lifetime for HttpApplication? My use case has to do w/ authentication. I have a repo to get user info for a custom identity. It is requested from PostAuthenticate – B Z Feb 20 '12 at 17:53
  • @B Z HttpApplication is managed by IIS. Normally they are reused for multiple requests but they can be disposed and a new one recreated during the applications lifetime. So its something between a request and a singelton. For detailed information look on msdn. – Remo Gloor Feb 20 '12 at 20:01
0

When you initialise IOC, in ASP.NET MVC if you set the dependency resolver, for example with Simple Injector:

DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

Then in global.asax you can just use the DependencyResolver to get the service, e.g.

var penTestService = DependencyResolver.Current.GetService<IPenTestService>();
Philip Johnson
  • 1,091
  • 10
  • 24
-1

Move the code from the constructor to Application_Start method. I believe even if multiple HttpApplication instances are created, Application_Start is called only once, and that too on the 1st instance only. Let me know if it has solved your problem.

These are the various event handlers you can potentially have in your Global.asax.cs :

public class Global : System.Web.HttpApplication
{
    public Global()
    {
        InitializeComponent();
    }   

    protected void Application_Start(Object sender, EventArgs e)
    {

    }

    protected void Session_Start(Object sender, EventArgs e)
    {

    }

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {

    }

    protected void Application_EndRequest(Object sender, EventArgs e)
    {

    }

    protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {

    }

    protected void Application_Error(Object sender, EventArgs e)
    {

    }

    protected void Session_End(Object sender, EventArgs e)
    {

    }

    protected void Application_End(Object sender, EventArgs e)
    {

    }

    #region Web Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {    
    }
    #endregion
}
Zasz
  • 12,330
  • 9
  • 43
  • 63
  • No it doesn't. I've done that and when MvcApplication_AuthenticateRequest is call, _auth is still null :( – vtortola Apr 01 '11 at 11:45
-1

Can you use the HttpApplication.Appliction property?

public class MyHttpApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        this.Application["auth"] = GetAuthFromContainer();
    }

    protected void Application_AuthenticateRequest()
    {
        IUserAuthentication auth = (IUserAuthentication)this.Application["auth"]; 
        // auth != null
    }
}