ASP.NET Web API (.NET Framework) · Building APIs

Dependency Injection

Web API 2 has a hook for dependency injection — IDependencyResolver — but no container of its own. Plug in Autofac (or Unity, Ninject, Simple Injector) and controllers can receive their services through the constructor.

Intermediate 6 min Lesson 10 of 36Covers ASP.NET Web API 2.2 on .NET Framework 4.8.
0/36 in ASP.NET Web API
On this page · 6 min · Intermediate

Learning objectives#

  • Explain why Web API 2 needs a third-party container
  • Register controllers and services with Autofac
  • Choose InstancePerRequest, SingleInstance and InstancePerDependency
  • Fix “parameterless public constructor” errors

Explanation#

By default Web API creates controllers with their parameterless constructor. If a controller asks for IProductService, you get the error “Make sure that the controller has a parameterless public constructor”.

A DI container fixes this. Install the Autofac.WebApi2 NuGet package, register types, build the container and set config.DependencyResolver.

builder.RegisterApiControllers(Assembly.GetExecutingAssembly()) registers every controller. builder.RegisterType<ProductService>().As<IProductService>() maps an interface to a class.

Lifetimes: InstancePerRequest — one per HTTP request (DbContext, repositories, services). SingleInstance — one for the whole app (configuration, caches, clocks). InstancePerDependency — a new one every time it is injected.

Configure the container once at startup (Global.asax or WebApiConfig). ASP.NET Core later made this built in with builder.Services.

Syntax#

C# Illustrative
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<ProductService>().As<IProductService>().InstancePerRequest();
config.DependencyResolver = new AutofacWebApiDependencyResolver(builder.Build());

Code example: Autofac in a Web API 2 project#

C#AutofacConfig.cs Illustrative
using System;
using System.Reflection;
using System.Web;
using System.Web.Http;
using Autofac;
using Autofac.Integration.WebApi;


public interface IClock
{
    DateTime UtcNow { get; }
}


public class SystemClock : IClock
{
    public DateTime UtcNow => DateTime.UtcNow;
}


public interface IGreetingService
{
    string Greet(string name);
}


public class GreetingService : IGreetingService
{
    private readonly IClock _clock;
    public GreetingService(IClock clock) { _clock = clock; }


    public string Greet(string name)
    {
        return (_clock.UtcNow.Hour < 12 ? "Good morning, " : "Hello, ") + name;
    }
}


// App_Start/AutofacConfig.cs
public static class AutofacConfig
{
    public static void Register(HttpConfiguration config)
    {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());


        builder.RegisterType<SystemClock>().As<IClock>().SingleInstance();
        builder.RegisterType<GreetingService>().As<IGreetingService>().InstancePerRequest();


        config.DependencyResolver = new AutofacWebApiDependencyResolver(builder.Build());
    }
}


// Global.asax.cs
public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(config =>
        {
            config.MapHttpAttributeRoutes();
            AutofacConfig.Register(config);
        });
    }
}


// The controller just asks for what it needs
public class GreetingsController : ApiController
{
    private readonly IGreetingService _greetings;


    public GreetingsController(IGreetingService greetings)
    {
        _greetings = greetings;
    }


    [HttpGet, Route("api/greetings/{name}")]
    public IHttpActionResult Get(string name)
    {
        return Ok(_greetings.Greet(name));
    }
}
Expected output
GET /api/greetings/Asha   (at 09:00 UTC)  → 200 "Good morning, Asha"
GET /api/greetings/Asha   (at 15:00 UTC)  → 200 "Hello, Asha"

Without AutofacConfig.Register:
→ 500 "An error occurred when trying to create a controller of type 'GreetingsController'.
       Make sure that the controller has a parameterless public constructor."

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. GreetingService depends on IClock, and the controller depends on IGreetingService — nobody uses new for services.
  2. RegisterApiControllers lets Autofac create every controller.
  3. SystemClock is a singleton: one shared, stateless instance.
  4. GreetingService is created once per HTTP request and disposed at the end of it.
  5. Setting config.DependencyResolver tells Web API to ask Autofac for controllers and their dependencies.

Real-world use case#

A Web API 2 service registers ShopDbContext, repositories and services InstancePerRequest, so every request gets one DbContext shared by all its repositories — and unit tests can pass mocks into controller constructors.

Common mistakes#

DbContext as SingleInstance

A single DbContext shared by all requests is not thread-safe and grows forever. Use InstancePerRequest.

Forgetting RegisterApiControllers

Autofac cannot build controllers it does not know about, so you still get the parameterless constructor error.

Resolving services manually everywhere

Calling the container from inside classes (service locator) hides dependencies. Use constructor injection.

Best practices#

  • Configure the container once at startup
  • Inject interfaces through constructors
  • Use InstancePerRequest for anything holding a DbContext
  • Keep singletons stateless or thread-safe

Remember this#

Web API 2 has the DI hook (IDependencyResolver), not the container.

Autofac: RegisterApiControllers + RegisterType<T>().As<I>().

Per request for DbContext; single instance for stateless helpers.

Practice exercise#

Your task: Register an ITaxCalculator (implemented by GstCalculator, 18%) as SingleInstance, and write a TaxController that receives it and returns the tax for GET api/tax/{amount}.

Need a hint?
builder.RegisterType<GstCalculator>().As<ITaxCalculator>().SingleInstance();
Show solution
C# Illustrative
using System.Web.Http;
using Autofac;


public interface ITaxCalculator { decimal TaxFor(decimal amount); }
public class GstCalculator : ITaxCalculator { public decimal TaxFor(decimal amount) => amount * 0.18m; }


public static class TaxRegistrations
{
    public static void Add(ContainerBuilder builder)
    {
        builder.RegisterType<GstCalculator>().As<ITaxCalculator>().SingleInstance();
    }
}


public class TaxController : ApiController
{
    private readonly ITaxCalculator _tax;
    public TaxController(ITaxCalculator tax) { _tax = tax; }


    [HttpGet, Route("api/tax/{amount:decimal}")]
    public IHttpActionResult Get(decimal amount) => Ok(_tax.TaxFor(amount));   // GET api/tax/100 → 18.00
}

Mini quiz#

Dependency Injection quizMultiple choice Question 1 of 3

Which property tells Web API 2 to use a DI container?

Score so far: 0/0

Want more? Practise all ASP.NET Web API MCQs · ASP.NET Web API interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.