Search code examples
model-view-controllerdependency-injectionwebapiasp.net-core-7.0

.NET 7 MVC app, Dependency Injection not working for added WebAPI controller


I have scoured the web looking for an answer, but I could not locate one.

I have a .NET 7 MVC application that I added a Web API controller to. By default, the API functions as expected, but when I try to pass ILogger into the constuctor and call the API, I get an exception:

I have created the default MVC app and added the default API controller with read/write options (which creates the ValuesController for weather data), and I still get the exception:

A suitable constructor for type 'MVCAppWithApiTest.Controllers.ValuesController' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.

Again, it works fine with no ILogger DI, but gives the exception when ILogger added to the constructor. ILogger DI works fine for MVC controllers with Views.

I have tried adding builder.Services.AddControllers() before (and after) builder.Services.AddControllersWithViews() with no change.

Looking at MvcServiceCollectionExtensions, I don't see an option to have both at the same time.

I saw an article that described using AddControllersAsService, but that does not exist in .NET 7.

Is there some mechanism for the ValuesController to be a registered service for DI along with the MVC controllers?

(It's not just ILogger, but other DI services as well, such as IHostApplicationLifetime).

Thanks

Minimal, Reproducible Example Steps

o Using Visual Studio 2022
o Create a new C# .NET project using the template: ASP.NET Core Web App (Model-View-Controller) named "MVCAppWithApiTest"
o Select .NET 7.0 for Framework
o Add a HomeController constructor to add Dependency Injection


       private readonly ILogger _logger;
    
       public HomeController(ILogger logger)
       {
           _logger = logger;
       }

o Build and Run the application. This verifies that the DI of ILogger is working.

o Stop the application and right-click on controllers.

o Add New Item API Controller with read/write actions

o Use the default name of ValuesController

o Build and run the application

o When the browser opens, modify the address bar to execute the get api call:

https://localhost:XXXX/api/Values

o Browser will show:

    [
        "value1",
        "value2"
    ]

o Add a ValuesController constructor to add Dependency Injection:


       public ILogger _iLogger;
    
       ValuesController(ILogger iLogger)
       {
           _iLogger = iLogger;
       }

o Build and run the application.

o When the browser opens, modify the address bar to execute the get api call:

https://localhost:XXXX/api/Values

o Exception appears:


    InvalidOperationException: A suitable constructor for type 'MVCAppWithApiTest.Controllers.ValuesController' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
    Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, out ConstructorInfo matchingConstructor, out Nullable[] matchingParameterMap)Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateFactory(Type instanceType, Type[] argumentTypes)
    Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.CreateActivator(ControllerActionDescriptor descriptor)
    Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.CreateControllerFactory(ControllerActionDescriptor descriptor)
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerCache.GetCachedResult(ControllerContext controllerContext)
    Microsoft.AspNetCore.Mvc.Routing.ControllerRequestDelegateFactory+c__DisplayClass12_0.b__0(HttpContext context)
    Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
    Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
    Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
    Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

Here is the program.cs. It is all out-of-the-box as VS2022 created it, except that I included my attempt at adding builder.Services.AddControllers();


    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews();
    
    //tried adding this but no help
    builder.Services.AddControllers();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();


Solution

  • The error happens for two reasons:

    1. Your constructor is not public. MS.DI (and most DI Containers for that matter) requires your constructors to be public
    2. ILogger is not a known registered type in ASP.NET Core (by default). The expected type is an ILogger<T>.

    In other words, your constructor must be changed to the following:

    public ILogger _iLogger;
    
    public ValuesController(ILogger<ValuesController> iLogger)
    {
        _iLogger = iLogger;
    }