How to integrate Ninject into ASP.NET Core 2.0 Web applications?

[亡魂溺海] 提交于 2019-12-17 09:48:06

问题


I have found out that Ninject has recently introduced support for .NET Standard 2.0 / .NET Core 2.0.

However, I cannot find any extension to actually integrate it in the Web application (e.g similar to Ninject.Web.Common)

Looking on the code from an old ASP.NET MVC solution, I realized that the whole mechanism is different as the classic one relied on WebActivatorEx.PreApplicationStartMethod and WebActivatorEx.ApplicationShutdownMethodAttribute which are no longer available in ASP.NET Core.

Also, the old Ninject.Web.Common assembly provided several useful classes used for initialization - Bootstrapper, OnePerRequestHttpModule, NinjectHttpModule:

public static void Start()
{
    DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
    DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
    Bootstrapper.Initialize(CreateKernel);
}

Question: is there any example of how to integrate Ninject into an ASP.NET Core 2.0 Web application?


回答1:


Short answer:

check this project. However, it relies on Ninject 4.0.0 which is still in beta version and it seems far from a final version (source). For Ninject 3.3.x look below.

Long answer:

Thanks to @Steven I managed to create a working solution of ASP.NET Core 2.0 and Ninject (both 3.3.x and 4.0). The code is mainly from Missing-Core-DI-Extensions Git repo, so great thanks to dotnetjunkie.

The following must be performed regardless of referenced Ninject version:

1) Include AspNetCoreExtensions.cs and AspNetCoreMvcExtensions.cs in your project.

2) Create a very simple service to use for testing the DI: TestService that implements ITestService:

public class TestService : ITestService
{
    public int Data { get; private set; }

    public TestService()
    {
        Data = 42;
    }
} 

public interface ITestService
{
    int Data { get; }
}

Ninject 3.3.x

Change Startup.cs as indicated below:

1) add these members

private readonly AsyncLocal<Scope> scopeProvider = new AsyncLocal<Scope>();
private IKernel Kernel { get; set; }

private object Resolve(Type type) => Kernel.Get(type);
private object RequestScope(IContext context) => scopeProvider.Value;  

2) Add to ConfigureServices(IServiceCollection services) (at the end)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

services.AddRequestScopingMiddleware(() => scopeProvider.Value = new Scope());
services.AddCustomControllerActivation(Resolve);
services.AddCustomViewComponentActivation(Resolve);

3) Add to Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) (at the beginning)

Kernel = RegisterApplicationComponents(app, loggerFactory);

4) Add the following method and inner class:

private IKernel RegisterApplicationComponents(
    IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    // IKernelConfiguration config = new KernelConfiguration();
    Kernel = new StandardKernel();

    // Register application services
    foreach (var ctrlType in app.GetControllerTypes())
    {
        Kernel.Bind(ctrlType).ToSelf().InScope(RequestScope);
    }

    Kernel.Bind<ITestService>().To<TestService>().InScope(RequestScope);

    // Cross-wire required framework services
    Kernel.BindToMethod(app.GetRequestService<IViewBufferScope>);
    Kernel.Bind<ILoggerFactory>().ToConstant(loggerFactory);

    return Kernel;
}

private sealed class Scope : DisposableObject { }

5) Create BindToMethod extension method

public static class BindingHelpers
{
    public static void BindToMethod<T>(this IKernel config, Func<T> method) => 
        config.Bind<T>().ToMethod(c => method());
}

6) Test DI by injecting custom service into a controller, setting a breakpoint into test service constructor and checking the call stack. Besides providing an instance, the call stack should hit custom code to integrate Ninject (e.g. ConfigureRequestScoping method)

Ninject 4.0.0

IKernel has been deprecated in version 4, so IReadOnlyKernel and IKernelConfiguration classes should be used (although above code should work).

1) Use the new class (IReadOnlyKernel)

private readonly AsyncLocal<Scope> scopeProvider = new AsyncLocal<Scope>();
private IReadOnlyKernel Kernel { get; set; }

private object Resolve(Type type) => Kernel.Get(type);
private object RequestScope(IContext context) => scopeProvider.Value;

2) 3) are the same

4) The method is slightly different:

private IReadOnlyKernel RegisterApplicationComponents(
    IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    IKernelConfiguration config = new KernelConfiguration();

    // Register application services
    foreach (var ctrlType in app.GetControllerTypes())
    {
        config.Bind(ctrlType).ToSelf().InScope(RequestScope);
    }

    config.Bind<ITestService>().To<TestService>().InScope(RequestScope);

    // Cross-wire required framework services
    config.BindToMethod(app.GetRequestService<IViewBufferScope>);
    config.Bind<ILoggerFactory>().ToConstant(loggerFactory);

    return config.BuildReadonlyKernel();
}

5) The extension must use an IKernelConfiguration

public static class BindingHelpers
{
    public static void BindToMethod<T>(
        this IKernelConfiguration config, Func<T> method) =>
            config.Bind<T>().ToMethod(c => method());
}


来源:https://stackoverflow.com/questions/46693305/how-to-integrate-ninject-into-asp-net-core-2-0-web-applications

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!