使用 Web Api、SignalR、MVC 和 OWIN 进行 Ninject

use*_*819 5 asp.net ninject signalr asp.net-web-api owin

我在我的 Web 应用程序中使用了 Ninject DI,其中包含来自 Asp.Net 堆栈(MVC、Web Api 2、SignalR)的一系列技术。

我已经通过以下方法使 DI 适用于所有使用的技术:

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
    }        
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。

所有这些都与在 Global.asax 中初始化的 Web Api 一起使用。

现在我要切换到 OWIN 管道。所以我GlobalConfiguration.Configure(WebApiConfig.Register);从 Global.asax 中删除并添加

HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
Run Code Online (Sandbox Code Playgroud)

到我的OwinStartup班级。Web Api 的 DI 停止工作。

我开始寻找合适的解决方案并找到了Ninject.Web.WebApi.OwinHostpackage.json 。因此,为了让单个内核解决所有技术的依赖关系,我进行了以下更改:

在 OwinStartup 中:

app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(config);
Run Code Online (Sandbox Code Playgroud)

在 NinjectWebCommon 中:

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

这些行被禁用以避免两次初始化内核。

此固定 DI 适用于 Web Api,但不适用于 SignalR。当客户端尝试连接到集线器时,我收到以下异常:

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
Run Code Online (Sandbox Code Playgroud)

我有点失落。我读了大约两几十篇文章,但没有一篇给我解决方案。将不胜感激任何帮助。

我的最终目标是拥有一个为 Web Api、MVC 和 SignalR 提供服务并支持 OWIN 管道的内核。

编辑:因为我有评论说我的案例可能是另一个问题的重复,我相信我需要给出一些解释。

我有三个场景。

  1. Global.asax 中的 WebApi 初始化,GlobalConfiguration.Configure(WebApiConfig.Register)使用 NinjectWebCommon 和 Bootstrapper 进行 Ninject 初始化。

    这给了我在 WebApi 和 SignalR 中的注入。但是由于我想将 WebApi 初始化移动到 OWIN 启动,这种方法已经过时了。

  2. 使用 OWIN Startup 进行 WebApi 初始化,使用 NinjectWebCommon 和 Bootstrapper 进行 Ninject 初始化。

    SignalR 注入有效,WebApi 注入无效。

  3. 使用 OWIN 启动的 WebApi 初始化,使用 UseNinjectMiddleware 的 Ninject 初始化,UseNinjectWebApi。

    WebApi 注入有效,SignalR 注入无效。

所以基本上我需要弄清楚如何将它们放在一起,以便在我在 OWIN 管道上初始化 WebApi 时 WebApi 和 SignalR 注入都可以工作。

NinjectWebCommon 的代码在下面的原始问题中。它包含用于创建 SignalR 解析器的代码,但在场景 3 中没有帮助。

编辑 2:经过几个小时的反复试验,我得出的结论是调用

app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
app.UseNinjectWebApi(config);
Run Code Online (Sandbox Code Playgroud)

与此调用冲突:

GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
Run Code Online (Sandbox Code Playgroud)

所以问题描述缩小到这一点。当我使用以下模式 SignalR 停止工作时:

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;
}
Run Code Online (Sandbox Code Playgroud)

但如果我评论这一行

    //GlobalHost.DependencyResolver = new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(kernel);
Run Code Online (Sandbox Code Playgroud)

SignalR 再次开始工作。但当然没有在集线器内注入。

use*_*819 5

最后,我设法获得了支持 OWIN 管道、WebApi、MVC 和 SignalR 的工作 Ninject 配置。

当我发布问题时,我有一个解决方法(在 SignalR 集线器中禁用 DI),所以我决定不再浪费时间并继续前进。

但是,当我尝试使用 Startup 类运行 OWIN 内存中测试服务器时,DI 无法正常工作。CreateKernel 方法调用太晚,导致创建了在 sengleton 范围内使用的对象的多个实例。

在尝试了不同的初始化变体之后,我让 DI 为 OWIN 测试服务器工作,它还修复了 SignalR DependencyResolver。

解决方案:

我停止使用包Ninject.Web.Common.OwinHostNinject.Web.WebApi.OwinHost所以这些调用从我的配置方法中删除:

//app.UseNinjectMiddleware(NinjectWebCommon.CreateKernel);
//app.UseNinjectWebApi(config);
Run Code Online (Sandbox Code Playgroud)

相反,我执行以下操作:

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();
    }        
}
Run Code Online (Sandbox Code Playgroud)