Start using Dependency Injection with ASP.NET Core

November 17, 2016 · 3 minute read

ASP.NET Core ships with a built-in Dependency Injection Framework.

DI frameworks give you a way to set up all your dependencies in one place. You can then use these dependencies anywhere in your web application.

The common approach is to inject them via constructors.

For example, if you create an IUserStore interface which has methods for retrieving users, you can bring this into a controller like so.

[Route("api/[controller]")]
    public class UserController : Controller
    {
        private IUserStore _userStore;

        public UserController(IUserStore userStore)
        {
            _userStore = userStore;
        }
        
        [HttpGet("[action]")]
        public IActionResult Test()
        {
            return Ok("worked");
        }             
    }  

The DI framework takes over the job of providing an instance of IUserStore when the controller’s constructor is invoked. In the case of controller constructors this usually happens per web request.

All well and good but if you try to hit http://yourapp/api/user/test you’ll see an error.

di-failed-to-activate-user-controller

The DI framework has absolutely no idea how to resolve the IUserStore dependency because we haven’t told it what to do.

Manual Configuration

You can manually configure the DI framework and tell it exactly which class to use when.

For example, you could explicitly tell the ASP.NET Core DI Framework to return an instance of InMemoryUserStore when attempting to resolve the IUserStore.

Modify the ConfigureServices method in Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddSingleton<IUserStore, InMemoryUserStore>();
}

AddSingleton instructs the Core DI Framework to instantiate one instance of InMemoryUserStore when it’s first requested, then use the same instance every time anything tries to use IUserStore.

Singleton is one of several lifetimes you can choose for your dependencies. The other common one is Transient where a new instance of the service is created every time it’s requested.

For a full list (and explanation) see the official docs.

Singleton suits us for keeping some test data in memory because it means it will be preserved across multiple requests.

Here’s an simple implementation.

public interface IUserStore
{
    void CreateUser(string name);
    IList<string> ListAll();
}

public class InMemoryUserStore : IUserStore
{
    IList<string> _users = new List<string>();

    public void CreateUser(string name)
    {
        _users.Add(name);
    }

    public IList<string> ListAll()
    {
        return _users;
    }
} 

Testing the Singleton

To prove this actually works (and that the data is held between requests) we can update our test controller and give it a spin.

[Route("api/[controller]")]
public class UserController : Controller
{
    private IUserStore _userStore;

    public UserController(IUserStore userStore)
    {
        _userStore = userStore;
    }
    
    [HttpGet("[action]")]
    public IActionResult Test()
    {
        return Ok("worked");
    }

    [HttpGet()]
    public IList<string> List()
    {
        return _userStore.ListAll();
    }
    
    [HttpPost()]
    public IActionResult Create(string name)
    {
        _userStore.CreateUser(name);
        return Ok("User created"); 
    }              
} 

Now if we use Postman to make a post to our controller we expect users to be added to the shared in-memory list inside InMemoryUserStore.

testing-user-creation

And here’s our extremely exciting list of users having used postman to create a few with different names.

user-list

In Summary

Using DI frameworks provides a clean separation of concerns where the construction and lifetime of your services are managed separately from your core business logic, freeing you up to bring your services in wherever you need to, simply by passing them into the constructor.

You don’t have to stick with Microsoft’s DI Framework. There are a number of other well-established DI frameworks which play nicely with .NET Core and the good news is, once you’ve got your controllers set up with dependencies (like our example above), switching DI Frameworks is relatively painless and can be done without changing any of your business logic or controller code.

photo credit: adafruit MCP23017 – i2c 16 input/output port expander via photopin (license)

Join the Practical ASP.NET Newsletter

Ship better Blazor apps, faster. One practical tip every Tuesday.

I respect your email privacy. Unsubscribe with one click.