16

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?

Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • See: https://stackoverflow.com/a/32797105/264697 – Steven Oct 14 '17 at 16:05
  • Here's an example of how to integrate Ninject with ASP.NET Core: https://github.com/dotnetjunkie/Missing-Core-DI-Extensions/blob/master/src/SampleApplication.Ninject/Startup.cs – Steven Oct 14 '17 at 16:05
  • 1
    @Steven - I have also find this example and tried to use it, but got stuck because it uses some types I could not find: `Scope` and `IReadOnlyKernel `. Last commit was done about 1 year ago, so it might not be compatible with the current stable version of Ninject (3.3.0) and/or ASP.NET Core (2.0). – Alexei - check Codidact Oct 14 '17 at 16:12
  • You need to use [Ninject 4.0](https://www.nuget.org/packages/Ninject/4.0.0-beta-0134) to work with those examples, or do some small changes to get it working with Ninject 3.3. Those examples use Ninject 4, since Ninject 3 is not compatible with .NET Standard. – Steven Oct 14 '17 at 16:13
  • @Steven - latest stable version (3.3.1) lists .NET Standard 2.0 as a dependency and can be installed in a .NET Core 2.0 project. – Alexei - check Codidact Oct 14 '17 at 16:34
  • Ah, it can? I missed that. – Steven Oct 14 '17 at 16:37
  • @Steven - I have tested with both 4.0 and 3.3.1 and it seems to work (not sure if injected instance in controller is caused by Ninject or ASP.NET though, however kernel.Get works, so at least the ninject part is properly initialized. However, I would stick with 3.3.1, as 4.0 is beta and the referenced answer argues about the final version not coming any time soon. – Alexei - check Codidact Oct 14 '17 at 17:26
  • Hi Alexei, try to understand how this integration model works. It's important to understand this. There's no magic. Dive into the code, set break points. – Steven Oct 14 '17 at 17:30
  • @Steven - yes, my test service is being created via the custom code (`ConfigureRequestScoping`), so it works. Should I provide an answer containing working examples for both versions? It is a little bit different for version 3.3.1 like you said and also a minor change is required to fix an issue for 4.0. – Alexei - check Codidact Oct 14 '17 at 17:43
  • It's always good to get your question anwsered, and answering your own question will be highly appreciated by other developers who are googling for an answer. – Steven Oct 14 '17 at 17:44
  • Created a [git repo](https://github.com/JanivZ/NinjectDotnetCore) implementing @Alexei answer above, so the next guy can jut download a complete solution instead of coping from here... - works great! – JanivZ May 01 '18 at 08:59

1 Answers1

21

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());
}
Steven
  • 166,672
  • 24
  • 332
  • 435
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • Step 3) is missing the code that actually needs to be added to the `Configure(..)` method, which I assume to be `Kernel = RegisterApplicationComponents(app, loggerFactory);` – zeroid Dec 01 '17 at 11:45
  • @zeroid - it was indeed missing. Fixed it. Thanks. I am glad that more programmers are fond of Ninject and try to use in ASP.NET Core. – Alexei - check Codidact Dec 01 '17 at 11:53
  • @Alexei: There's a line in `AspNetCoreMvcExtensions` that doesn't compile: `applicationTypeSelector = applicationTypeSelector ?? (type => !type.GetTypeInfo().Namespace.StartsWith("Microsoft"));` Any thoughts? It's trying to apply `??` to `Predicate` and a lambda expression. – zimdanen Feb 14 '18 at 21:18
  • @zimdanen - I checked in my project and it seems to work. I am using Target framewor = .NET Core 2.0. What is your target framework? Also, are you using Visual Studio 2017? – Alexei - check Codidact Feb 15 '18 at 16:37
  • @Alexei: Same thing: .NET Core 2.0 in VS 2017. That exact line compiles for you? – zimdanen Feb 15 '18 at 17:09
  • @zimdanen - yes. Which language version are you using? My version is C# latest major version (default). I think this means C# 7.0. – Alexei - check Codidact Feb 15 '18 at 17:17
  • @Alexei: Mine is also `C# latest major version (default)`. Same build error if I manually set it to `C# 7.2`. – zimdanen Feb 15 '18 at 17:25
  • 1
    @zimdanen - I have no other suggestions other than posting a question here, on StackOverflow. Just make sure to include the whole function body. – Alexei - check Codidact Feb 16 '18 at 08:01
  • Are these external files from a random project still needed? Or have they been combined with a Ninject library/package since this was posted? – IronSean Aug 07 '18 at 17:41
  • I am also running into issues, parsing `RequestScope` into `config.Bind().To().InScope(RequestScope);` tells me `cannot convert from 'method group' to 'func' – Jean-Paul Apr 06 '19 at 21:26
  • @Alexei great feedback, do you know if the same code can be used for .NET Core 3? Or there is an easy way? Thanks – VAAA Sep 27 '19 at 04:25
  • @VAAA - never got to check it against .net core 3 yet (still swamped in 2.2 projects). – Alexei - check Codidact Sep 27 '19 at 04:58
  • @Alexei thanks a lot, last question. Whats the benefit of using ninject instead of the built in DI framework that comes with core? – VAAA Sep 27 '19 at 18:20
  • @VAAA - it provides support for more complex contexts. I am using the built-in DI framework for some time and I recommend using it unless there is a strong reason to switch something else. – Alexei - check Codidact Sep 27 '19 at 19:29
  • @VAAA - it provides support for more complex contexts. I am using the built-in DI framework for some time and I recommend using it unless there is a strong reason to switch something else. – Alexei - check Codidact Sep 27 '19 at 19:31
  • Appreciate it a lot @Alexei – VAAA Sep 30 '19 at 16:32
  • How to register Microsoft.AspNetCore.Identity using this solution? I try to use userManager in my controllers but when I do kernel.Bind>().ToConstant(usrMgr).InRequestScope(); where usrMgr is parameter of Configure method I have an error while using userManager in Controller : ObjectDisposedException: Cannot access a disposed object. Object name: 'UserManager`1'. – ElConrado Mar 18 '20 at 18:22