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.
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.
WebApi initialization with OWIN Startup, Ninject initialization with NinjectWebCommon and Bootstrapper.
SignalR injection works, WebApi injection does not work.
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.
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 thatGlobalHost.DependencyResolver
is modified after the default SignalR container already has been used. In OWIN you may want to callMapSignalR
late in the pipeline. E.g. with Nancyapp.UseNancy(o => o.PerformPassThrough = c => c.Request.Path.ToUpperInvariant().StartsWith("/SIGNALR")).MapSignalR()
. – Martin Liversage