5

I am using a Ninject DI in my web application with a bunch of technoligies from Asp.Net stack (MVC, Web Api 2, SignalR).

I have managed to make DI work for all technologies in use with the following approach:

public static class NinjectWebCommon 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }
    
    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }
    
    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    internal static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));

        // Binding services here
    }        
}

So far so good.

All this was working with Web Api being initialized in Global.asax.

Now I'm switching to OWIN pipeline. So I removed GlobalConfiguration.Configure(WebApiConfig.Register); from Global.asax and added

HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);

to my OwinStartup class. DI for Web Api stoped working.

I started searching for appropriate solution and found Ninject.Web.WebApi.OwinHost package. So in order to have a single Kernel resolving dependencies for all technologies, I've made the following changes:

in OwinStartup:

app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(config);

in NinjectWebCommon:

//[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(App.TradingServer.ConfiguratorApp.App_Start.NinjectWebCommon), "Start")]
//[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(App.TradingServer.ConfiguratorApp.App_Start.NinjectWebCommon), "Stop")]

These lines were disabled to avoid initializing kernel twice.

This fixed DI for Web Api but not for SignalR. When client tries to connect to hub I get the following exception:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNet.SignalR.PersistentConnection.ProcessNegotiationRequest(HostContext context)
   at Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequest(HostContext context)
   at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.ProcessRequest(HostContext context)
   at Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequest(IDictionary`2 environment)
   at Microsoft.AspNet.SignalR.Owin.Middleware.HubDispatcherMiddleware.Invoke(IOwinContext context)
   at Microsoft.Owin.Infrastructure.OwinMiddlewareTransition.Invoke(IDictionary`2 environment)
   at Microsoft.Owin.Mapping.MapMiddleware.<Invoke>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at System.Web.Http.Owin.HttpMessageHandlerAdapter.<InvokeCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Ninject.Web.Common.OwinHost.OwinBootstrapper.<Execute>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContextStage.<RunApp>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.<DoFinalWork>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow()
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar)
   at Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.EndFinalWork(IAsyncResult ar)
   at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) | RuntimeMethodInfo.UnsafeInvokeInternal => RuntimeMethodHandle.InvokeMethod => Application.Application_Error

I am kind of lost. I'we read around two dozens of articles but none of them gave me the solution. Would appreciate any help.

My final aim is to have a single kernel that serves Web Api, MVC and SignalR and supports OWIN pipeline.

Edit: Since I've got a comment that my case might be a duplicate of another question I believe I need to give some explanations.

I have three scenarios.

  1. WebApi initialization in Global.asax with GlobalConfiguration.Configure(WebApiConfig.Register), Ninject initialization with NinjectWebCommon and Bootstrapper.

    This gives me both injection in WebApi and SignalR. But since I would like to move WebApi initialization to OWIN startup this approach is obsolete.

  2. WebApi initialization with OWIN Startup, Ninject initialization with NinjectWebCommon and Bootstrapper.

    SignalR injection works, WebApi injection does not work.

  3. WebApi initialization with OWIN Startup, Ninject initialization with UseNinjectMiddleware, UseNinjectWebApi.

    WebApi injection works, SignalR injection does not work.

So basically I need to figure out how to put this together so that both WebApi and SignalR injection work when I initialize WebApi on OWIN pipeline.

The code for NinjectWebCommon is in original question below. It contains code for creating SignalR resolver but it does not help in scenario 3.

Edit 2: After a couple of hours of trial and error method I came to a conclusion that calling

app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(config);

conflicts with this call:

GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);

So problem description narrows to this. When I use the following pattern SignalR stops working:

public void Configuration(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();
    WebApiConfig.Register(config);

    app.UseNinjectMiddleware(CreateKernel);
    app.UseNinjectWebApi(config);

    GlobalHost.HubPipeline.AddModule(new GlobalSignalRExceptionHandler());
    app.MapSignalR();
}


private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();

    GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
    DependencyResolver.SetResolver(new Ninject.Web.Mvc.NinjectDependencyResolver(kernel));

    return kernel;
}

But if I comment the line

    //GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);

SignalR starts working again. But no injection inside hubs of course.

Community
  • 1
  • 1
user1921819
  • 3,290
  • 2
  • 22
  • 28
  • possible duplicate of [SignalR 2 Dependency Injection with Ninject](http://stackoverflow.com/questions/21285934/signalr-2-dependency-injection-with-ninject) – mason Apr 23 '15 at 13:31
  • 1
    The mentioned topic does not cover integration with OWIN pipe. All the stuff with app.UseNinjectMiddleware and app.UseNinjectWebApi. Basically, they describe the approach I was using prior to switching to OWIN. – user1921819 Apr 23 '15 at 14:45
  • A general comment about this type of problem: My research shows that a `NullReferenceException` with that stack trace is thrown if the DI container used by SignalR hands out multiple instances of certain types that are expected to be singletons. This can happen for various reasons, one being that `GlobalHost.DependencyResolver` is modified after the default SignalR container already has been used. In OWIN you may want to call `MapSignalR` late in the pipeline. E.g. with Nancy `app.UseNancy(o => o.PerformPassThrough = c => c.Request.Path.ToUpperInvariant().StartsWith("/SIGNALR")).MapSignalR()`. – Martin Liversage Jun 06 '16 at 11:20

3 Answers3

5

Finally I managed to get the working Ninject configuration that supports OWIN pipe, WebApi, MVC and SignalR.

By the time when I posted the question I had a work-around (which was disabling DI in SignalR hubs) so I decided to waste no more time on this and moved on.

But when I tried running OWIN in-memory Test Server with my Startup class it occurred that DI was not working. CreateKernel method was called too late which resulted in creating several instances of an object that was used in sengleton scope.

After playing with different variations of initialization I've made DI work for OWIN Test Server and it also fixed the SignalR DependencyResolver.

The Solution:

I stopped using packages Ninject.Web.Common.OwinHost and Ninject.Web.WebApi.OwinHost so these calls were removed from my Configuration method:

//app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
//app.UseNinjectWebApi(config);

Instead I do the following:

public void Configuration(IAppBuilder app)
{
    ConfigureOAuth(app);

    HttpConfiguration config = new HttpConfiguration();
    WebApiConfig.Register(config);
    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

    NinjectWebCommon.Start();
    config.DependencyResolver = new NinjectDependencyResolver(NinjectWebCommon.bootstrapper.Kernel);
    app.UseWebApi(config);

    app.MapSignalR();
}

public static class NinjectWebCommon 
{
    private static bool _isStarted;

    internal static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        // When creating OWIN TestService instances during unit tests
        // Start() method might be called several times
        // This check ensures that Ninject kernel is initialized only once per process
        if (_isStarted)
            return;

        _isStarted = true;

        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    internal static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        // DI for SignalR
        GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
        // DI for MVC
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));

        // Binding code here
        kernel.Bind<Something>().ToSelf().InSingletonScope();
    }        
}
user1921819
  • 3,290
  • 2
  • 22
  • 28
1

in order to use a dependency resolver for both WebApi and SignalR you need to implement a class that looks like this:

    public class NinjectDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver,
    System.Web.Http.Dependencies.IDependencyResolver
{
    public readonly IKernel Kernel;

    public NinjectDependencyResolver(string moduleFilePattern)
        : base()
    {
        Kernel = new StandardKernel();
        Kernel.Load(moduleFilePattern);

    }
    public override object GetService(Type serviceType)
    {
        var service = Kernel.TryGet(serviceType) ?? base.GetService(serviceType);
        return service;
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        IEnumerable<object> services = Kernel.GetAll(serviceType).ToList();
        if (services.IsEmpty())
        {
            services = base.GetServices(serviceType) ?? services;
        }
        return services;
    }

    public System.Web.Http.Dependencies.IDependencyScope BeginScope()
    {
        return this;
    }

    public void Dispose()
    { }
}

then in your startup class you should register NinjectDependencyResolver for both WebApi and SignalR, like this:

public void Configuration(IAppBuilder app)
{
    var dependencyResolver = new NinjectDependencyResolver("*.dll");

    var httpConfiguration = new HttpConfiguration();
    httpConfiguration.DependencyResolver = dependencyResolver;
    app.UseWebApi(httpConfiguration);

    var hubConfig = new HubConfiguration { Resolver = dependencyResolver };
    app.MapSignalR(hubConfig);
}
hmd.ai
  • 1,163
  • 11
  • 13
  • where does your IsEmpty() come from? I just used ```services.Count() == 0```, also the Dispose() is an override of DefaultDependencyResolver. – tofutim Oct 26 '16 at 00:26
  • Error activating ModelValidatorProvider using binding from ModelValidatorProvider to NinjectDefaultModelValidatorProvider A cyclical dependency was detected between the constructors of two services. :P – tofutim Oct 26 '16 at 00:43
  • I liked this solution, I found that I had to get rid of Ninject.Web.* for this to work. – tofutim Oct 26 '16 at 01:11
0

SignalR must be configured after your dependency injection configuration. So, in your OWIN Startup class, make sure that app.MapSignalR() is called after setting the MVC dependency resolver (System.Web.MVC.DependencyResolver), the WebApi dependency resolver (System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver) and the SignalR dependency resolver (Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver).

Wayne Allen
  • 1,605
  • 1
  • 10
  • 16