NeilC
NeilC

Reputation: 1390

Autofac: Resolving dependencies for ApiControllers

I'm new to Inversion of Control & Autofac. Going through the documentation, there is a lot ASP.NET Web Api concepts I do not fully understand or have experience with, making it a bit difficult to determine what is missing from my implementation.

I want to apply Autofac to an existing ASP.NET Web Api project that has several ApiControllers. These controllers all share a parent abstract class. This abstract class has a method that is responsible for returning an instance of a service. I hoped to replace this method with Autofac's dependency injection.

The parent abstract class from which every ApiController inherits, is very simple.

public abstract class BaseApiController
{
    public IMyService serviceClient { get; set; }

    public BaseApiController() {}

    public BaseApiController(IMyService serviceClient)
    {
        this.serviceClient = serviceClient;
    }
}

Every controller inherits from the above class, while some employs a default Get method, most have multiple routes. Not a single controller is specifying a constructor:

public class MyController : BaseApiController
{
    public MyController() : base() {}
    public MyController(IMyService serviceClient) : base(serviceClient) {}

    [HttpGet]
    [Route("api/foo/bar")]
    [ActionName("FooBar")]
    public string FooBar()
    {
        using (serviceClient)
        {
            return serviceClient.GetFooBar() as string;
        }
    }
}

Autofac is integrated into the Application_Start method of the applications Glabal.asax.cs, registering the ServerClientProvider which should be the provider that should be resolved to when a dependency to the IMyService is encountered:

public class Global : System.Web.HttpApplication, IContainerProviderAccessor
{
    #region AutoFac
    private static IContainerProvider _containerProvider;

    public IContainerProvider ContainerProvider => _containerProvider;
    #endregion AutoFac

    protected void Application_Start(object sender, EventArgs e)
    {

        var builder = new ContainerBuilder();

        builder.Register(x => ServiceClientProvider.GetServiceClient())
            .As<IMyService>()
            .InstancePerRequest();

        builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();

        var container = builder.Build();

        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);

        _containerProvider = new ContainerProvider(container);

    }
}

I have configured the ASP.NET application, in the web.config:

<configuration>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web" preCondition="managedHandler" />
      <add name="PropertyInjection" type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web" preCondition="managedHandler" />
    </modules>
  </system.webServer>
<configuration>

As I understand it, Autofac should automatically resolve the provider when encountering a controller with a public property that matches the registered type (Property Injection), or if a constructor exists with a parameter that matches any registered dependency.

However, I'm not getting any errors relating to my Autofac configuration. I'm getting a NullReferenceException when the MyController attempt to call the IMyService.FooBar method.

What am I missing?

Any help would be greatly appreciated.

Upvotes: 0

Views: 2558

Answers (1)

NeilC
NeilC

Reputation: 1390

After several attempts to debug the code, I realized that there existed another start up implementation in the project. OWIN is used and the start-up implementation I was trying to integrate Autofac with is instantiated but never used. So following the instructions on https://autofac.readthedocs.io/en/latest/integration/owin.html, yielded the results I was looking for.

Here is how the start-up implemetation should look at:

[assembly: OwinStartup(typeof(MyWebApp.Api.StartUp))]

namespace MyWebApp.Api
{
    public class StartUp
    {

        public void Configuration(IAppBuilder app)
        {
            // In OWIN you create your own HttpConfiguration rather than
            // re-using the GlobalConfiguration.
            HttpConfiguration httpconfig = new HttpConfiguration();

            httpconfig.Routes.MapHttpRoute(
                name: "ApiRouteWithAction",
                routeTemplate: "api/{controller}/{action}");

            httpconfig.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // Autofac SignalR integration
            var builder = new ContainerBuilder();

            // Register Web API controller in executing assembly.
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // Register SignalR hubs.
            builder.RegisterHubs(Assembly.GetExecutingAssembly());

            // Registering Deletegates
            builder.Register(x => ServiceClientProvider.GetServiceClient())
                .As<IMyService>()
                .InstancePerRequest();

            // Set the dependency resolver to be Autofac.
            var container = builder.Build();
            httpconfig.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            app.UseWebApi(httpconfig);

            // Register the Autofac middleware FIRST, then the custom middleware if any.
            app.UseAutofacMiddleware(container);
        }
    }
}

The controller & abstract class it inherited from, were tweaked, removing the parameterless constructors.

public abstract class BaseApiController
{
    public IMyService serviceClient { get; set; }

    public BaseApiController(IMyService serviceClient)
    {
        this.serviceClient = serviceClient;
    }
}

Every controller inherits from the above class, while some employs a default Get method, most have multiple routes. Not a single controller is specifying a constructor:

public class MyController : BaseApiController
{
    public MyController(IMyService serviceClient) : base(serviceClient) {}

    [HttpGet]
    [Route("api/foo/bar")]
    [ActionName("FooBar")]
    public string FooBar()
    {
        using (serviceClient)
        {
            return serviceClient.GetFooBar() as string;
        }
    }
}

Thanks for the interest, hopefully my rookie mistake will help another rookie.

Upvotes: 0

Related Questions