1

I have a solution that has WebAPI, BL and DAL layers in separate projects interfaces included, the projects are targeting .Net 4.8, I am trying to implement Dependency Injection for the first time.

I haven't used any external IoC packages StructureMap, Autofac, etc and really the only injection I'm doing is between the layers.

I have been following this previous stackoverflow question

However, I have become stuck.

This is my startup.cs:

using TA.Services.WebAPI.Providers;

using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;

using System.Linq;
using System.Web.Mvc;
using TA.Services.BL.BusinessLogic;
using TA.Services.BL.Interface;

[assembly: OwinStartup(typeof(TA.Services.WebAPI.Startup))]
namespace TA.Services.WebAPI
{
  public class Startup
  {
    public void Configuration(IAppBuilder app)
    {
      var services = new ServiceCollection();
      ConfigureServices(services);
      var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());

      HttpConfiguration config = new HttpConfiguration();

      config.DependencyResolver = resolver;

      ConfigureOAuth(app);

      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
      app.UseWebApi(config);

    }

    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
          .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
          .Where(t => typeof(IController).IsAssignableFrom(t)
                      || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
    }

    public void ConfigureOAuth(IAppBuilder app)
    {
      OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
      {
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        Provider = new SimpleAuthorizationServerProvider()
      };

      // Token Generation
      app.UseOAuthAuthorizationServer(OAuthServerOptions);
      app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    }
  }
  public class DefaultDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
  {
    protected IServiceProvider serviceProvider;

    public DefaultDependencyResolver(IServiceProvider serviceProvider)
    {
      this.serviceProvider = serviceProvider;
    }

    public IDependencyScope BeginScope()
    {
      return new DefaultDependencyResolver(this.serviceProvider.CreateScope().ServiceProvider);
    }

    public void Dispose()
    {
    }

    public object GetService(Type serviceType)
    {
      return this.serviceProvider.GetService(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
      return this.serviceProvider.GetServices(serviceType);
    }
  }

  public static class ServiceProviderExtensions
  {
    public static IServiceCollection AddControllersAsServices(this IServiceCollection services,
        IEnumerable<Type> controllerTypes)
    {
      foreach (var type in controllerTypes)
      {
        services.AddTransient(type);
      }

      return services;
    }
  }
}

I have only altered one of my simple controllers to inject it's appropriate BL interface and tested via the front-end, but it gets a 'Make sure that the controller has a parameterless public constructor'

So other than startup and the controller I haven't touched anything else.

Questions:

1: Can anyone see if there is anything missing from what I've done?

2: I can see through debug, that the controllers I have are populating the services via ConfigureServices(). Do I need to do anything extra to link them up with their dependencies?

[UPDATE 1 of 2]: Controller: Basically just changed out the constructor so it has a parameter.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TA.Services.BL.BusinessLogic;
using TA.Services.BL.Interface;
using TA.Services.DTO.DTO;
using TA.Services.WebAPI.Providers;

using Syncfusion.EJ2.Base;
using Newtonsoft.Json;
using TA.Services.WebAPI.Helpers;

namespace TA.Services.WebAPI.Controllers
{
  [WebApiConfig.CamelCaseControllerConfig]

  public class AuditsController : ApiController
  {
    private readonly IAuditsBL _auditsBL;
    
    /*
    public AuditsController()
    {
      _auditsBL = new AuditsBL();
    }
    */
    public AuditsController(IAuditsBL auditsBL)
    {
      _auditsBL = auditsBL;
    }
    ...
  }
}

[UPDATE 2 of 2]:

Made a change to ConfigureServices adding singleton to the services

public void ConfigureServices(IServiceCollection services)
{
  services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
      .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
      .Where(t => typeof(IController).IsAssignableFrom(t)
                  || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
  //Added this
  services.AddSingleton<IAuditsBL, AuditsBL>();
}

When I run the app I'm getting services populated like: enter image description here

and when I run the front-end and navigate to where the AuditController gets called I get the following in devtools: enter image description here

Hank
  • 2,456
  • 3
  • 35
  • 83
  • What changes did you made to controller? Can you share that change? What dependencies that controller have? have you registered that dependency in the DI? – Chetan Jun 14 '22 at 04:58
  • Hi @Chetan, thanks, got an hour or so before online again, but short answer is I just altered the constructor to have a parameter of BL interface – Hank Jun 14 '22 at 05:55
  • Can you share relevant code of controller? Are the BL interfaces registered with their types in dependency map? At least I can not see that in your code, correct me if I am missing something. No hurry... – Chetan Jun 14 '22 at 06:06
  • Please add the exception's full stack trace. The exception might be coming from the MVC stack instead of the Web API stack. – Steven Jun 14 '22 at 07:16
  • You are likely missing a call to `System.Web.Mvc.DependencyResolver.SetResolver(resolver);`. – Steven Jun 14 '22 at 07:33
  • Hi guys, back on, will start preparing answers to your questions. Steven isn't SetResolver just for MVC not WebAPI? WebAPI does it like this: `config.DependencyResolver = resolver;` – Hank Jun 14 '22 at 08:18
  • @Chetan, in regard to the registering BL interfaces, no I haven't done any dependency map. How would I do that and where? In the startup as well? – Hank Jun 14 '22 at 08:24
  • Made some some updates in the question, off to sleep now – Hank Jun 14 '22 at 11:13

1 Answers1

2
  1. Set MVC resolver to your Configuration method. I dont know why it is needed in web api controller.
DependencyResolver.SetResolver(resolver);
  1. Configure route after set resolver:
config.Routes.MapHttpRoute(
       name: "MyDefaultApi",
       routeTemplate: "api/{controller}/{id}",
       defaults: new { id = RouteParameter.Optional }
);
  1. ConfigureServices add singleton to the services (you are add this already)
services.AddSingleton<IAuditsBL, AuditsBL>();

Finally configuration method look like this:

public void Configuration(IAppBuilder app)
        {
            var services = new ServiceCollection();
            ConfigureServices(services);
            var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());
            // Set MVC Resolver
            DependencyResolver.SetResolver(resolver);

            // MVC Route
            RouteTable.Routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });

            // Set WebAPI Resolver and register
            HttpConfiguration config = new HttpConfiguration();
            config.DependencyResolver = resolver;

            // WebApi Route
            config.Routes.MapHttpRoute(
                name: "MyDefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
           );

            app.UseWebApi(config);
        }
 public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
              .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
              .Where(t => typeof(IController).IsAssignableFrom(t)
                          || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
            services.AddSingleton<IMyInterface, MyClass>();
        }

git: Link

MD. RAKIB HASAN
  • 3,670
  • 4
  • 22
  • 35