Dependency Injection: How to Start with Autofac the Easy Way

Dependency Injection (DI) is a design pattern that helps us develop loosely coupled, maintainable, and testable code. DI is a form of Inversion of Control (IoC), where the control of creating objects is transferred from the class itself to a container or framework. It allows us to inject dependencies, i.e., the objects that a class needs to function, into the class, either through the constructor, a property, or a method. There's a powerful framework in dotnet that helps us with this called Autofac and we'll dive into Autofac dependency injection in this article!

Personally, some of the core software designs & architectural patterns that I leverage focus a lot on these concepts. This includes things like plugin architectures and how I leverage facades in my code bases! As we proceed, we'll delve deeper into how DI can be achieved in C# and dotnet, particularly with the Autofac framework, and how it can benefit software development, especially for those new to the field.


What is Dependency Injection?

If you're still wondering "What is dependency injection?", then we'll dive in deeper in this section. Dependency Injection is a subset of a broader principle known as Inversion of Control (IoC). In IoC, the responsibility of creating and managing objects shifts from the class itself to an external container or framework. This approach allows for the injection of dependencies, which are the objects a class requires to operate, directly into the class. These dependencies can be provided through various means, such as the constructor, a property, or a method.

Central to the concept of DI is the Dependency Inversion Principle (DIP), a core tenet of the SOLID design principles in object-oriented programming. The DIP emphasizes that high-level modules shouldn't rely on low-level modules. Instead, both should depend on abstractions. This means that classes shouldn't be tied to specific implementations. They should interact with abstractions, like interfaces. The concrete implementations of these abstractions are then provided to the classes at runtime. This approach fosters a design where components are more interchangeable and the overall system is more modular.

While Dependency Injection (DI), Dependency Inversion Principle (DIP), and Inversion of Control (IoC) are terms often used interchangeably, they each have distinct roles in the software design landscape.

  • IoC is a broad principle where the control of creating and managing objects is shifted away from the class itself to an external entity, allowing for a more modular and flexible system.
  • DI is a specific technique to implement IoC, where dependencies (the objects a class needs to operate) are "injected" into the class from the outside, rather than being created within the class. This makes the system more adaptable and easier to test.
  • On the other hand, DIP is a design principle that advises that high-level modules (the core functionality) should not depend on low-level modules (the details), but both should rely on abstractions. This ensures that our main functionalities are not tightly bound to specific details, making the system more resilient to changes.

In essence, while IoC describes a shift in control, DI provides a method to achieve it, and DIP offers a design guideline to ensure the system remains decoupled and maintainable. So don't be nervous if you're confused hearing these terms thrown around... They are all very related!

Dependency Injection with Autofac & Autofac Examples

Answering "What is Autofac?": Dependency Injection in dotnet

Autofac is a popular and powerful Inversion of Control (IoC) container for .NET. At its core, an IoC container is a tool that facilitates the automatic creation, wiring, and management of objects, allowing developers to focus on the broader logic of the application without getting bogged down in the minutiae of object instantiation. Autofac excels in this domain, providing a robust and flexible framework for registering components, resolving dependencies, and managing object lifetimes. It's not the only one that exists, but it's my favorite and the one we'll focus on today.

In the realm of Dependency Injection (DI), Autofac generally serves as the bridge between the abstracted interfaces and their concrete implementations. When a class requires a specific service or component to function, rather than directly creating or referencing that component, the class simply declares its need. Autofac then takes on the responsibility of providing the class with the appropriate implementation at runtime. This decouples the class from any specific implementation, promoting a design that's modular, testable, and maintainable. It does this so well that sometimes it feels almost like magic.

What sets Autofac apart from other IoC containers is its extensive feature set and adaptability. It supports a wide range of DI patterns and integrates seamlessly with various .NET frameworks, including ASP.NET Core, making it a go-to choice for many developers. By leveraging Autofac, developers can harness the full power of Dependency Injection, ensuring that their applications are not only well-architected but also primed for scalability and future enhancements. Of course, many concepts we touch on will be applicable to other DI & IoC frameworks as well.

Getting Started With Autofac For Dependency Injection

Setting up Autofac in a .NET project is straightforward. First, you need to install the Autofac NuGet package. You can do this through the NuGet package manager in Visual Studio or by running the following command in the Package Manager Console:

Install-Package Autofac

Once Autofac is installed, you can create an instance of the ContainerBuilder class, which is used to register components and then build an IContainer which is used to resolve instances of components.


Registering Dependencies: Types, Instances, and Delegates

Autofac provides a flexible framework for registering dependencies, allowing developers to choose the method that best fits their specific needs. Let's explore three common registration methods: registering a type, an instance, and using a delegate.

Registering a Type

The RegisterType method is the most direct way to inform Autofac how to create an instance of a type when it's required.

var builder = new ContainerBuilder();
builder.RegisterType<SomeService>().As<IService>();
var container = builder.Build();

In this approach, SomeService is registered as an implementation of the IService interface. Every time you resolve IService, Autofac will create a new instance of SomeService for you, unless otherwise specified.

Registering an Instance

Sometimes, you might have a pre-existing instance of an object that you want to use throughout your application. In such cases, the RegisterInstance method comes in handy.

var builder = new ContainerBuilder();
var instance = new SomeService();
builder.RegisterInstance(instance).As<IService>();
var container = builder.Build();

Here, the same instance of SomeService will be used whenever IService is resolved. This is particularly useful when you want to maintain state or share a single instance across different parts of your application.

Registering with a Delegate

For scenarios where you need more control over the object creation process, you can register a delegate.

var builder = new ContainerBuilder();
builder.Register(c => new SomeService("parameterValue")).As<IService>();
var container = builder.Build();

By using a delegate, you can provide additional parameters, make decisions based on the context, or even implement more complex object creation logic. It offers a higher degree of flexibility compared to the straightforward type registration. I often use this if I need to specify particular logic about what I am resolving before passing it into the constructor of the object being registered.

Comparing and Contrasting

  • Type Registration: This is the most common method, ideal for when you want Autofac to handle the lifecycle and creation of the service. It's simple and effective for stateless services.
  • Instance Registration: Perfect for when you have a specific instance you want to reuse. It ensures that the same instance is provided wherever the service is resolved, making it suitable for stateful services or shared resources.
  • Delegate Registration: Offers the most flexibility. It's especially useful when object creation involves more than just invoking a constructor, or when you want to make decisions during the instantiation process.

In essence, the method you choose depends on the specific requirements of your application and the nature of the service being registered. This video shows more advanced usages you can play with:


Lifetime Scopes in Autofac

Autofac provides different lifetime scopes for services, which control how the lifetime of the service is managed. Understanding these scopes is crucial for managing memory and performance. Here are the main lifetime scopes:

  • InstancePerDependency: This is the default lifetime scope. A new instance of the service will be created each time it is requested.
  • SingleInstance: This scope ensures that only a single instance of the service will be created, and this instance will be shared across all requests.
  • InstancePerLifetimeScope: This scope creates one instance of the service per nested lifetime scope. This is useful when you want to share instances within a specific context, such as a web request.

Here's an Autofac example of how to register services with different lifetime scopes:

var builder = new ContainerBuilder();
builder.RegisterType<MyTransientService>().As<IMyService>().InstancePerDependency();
builder.RegisterType<MySingletonService>().As<IMyService>().SingleInstance();
builder.RegisterType<MyScopedService>().As<IMyService>().InstancePerLifetimeScope();
var container = builder.Build();

In this Autofac example, MyTransientService is registered as transient (a new instance per dependency), MySingletonService is registered as a singleton (a single shared instance), and MyScopedService is registered as scoped (one instance per lifetime scope).


Constructor Injection with Autofac

Constructor injection is one of the most common ways to achieve dependency injection. With Autofac, the process is streamlined and intuitive. When a class declares its dependencies through its constructor, Autofac automatically identifies and provides the required dependencies when creating an instance of that class. Let's delve into how this works.

Consider you have an interface ILogger and its implementation ConsoleLogger:

public interface ILogger
{
    void Log(string message);
}

public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

Now, let's say you have a UserService class that requires an ILogger to log messages:

public class UserService
{
    private readonly ILogger _logger;

    public UserService(ILogger logger)
    {
        _logger = logger;
    }

    public void CreateUser(string username)
    {
        // Some logic to create a user
        _logger.Log($"User {username} created successfully.");
    }
}

In the above UserService class, the dependency on ILogger is declared through its constructor.

To make Autofac automatically resolve this dependency, you'll first register the types with the container:

var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<UserService>();
var container = builder.Build();

Now, when you request an instance of UserService from the container, Autofac will automatically provide an instance of ConsoleLogger to its constructor:

using var scope = container.BeginLifetimeScope();
var userService = scope.Resolve<UserService>();
userService.CreateUser("JohnDoe");

In the above code, when UserService is resolved, Autofac sees that it requires an ILogger in its constructor. Since ConsoleLogger is registered as the implementation of ILogger, Autofac creates an instance of ConsoleLogger and passes it to the UserService constructor. This is the magic of constructor injection with Autofac!

In essence, Autofac takes away the manual labor of wiring up dependencies. By simply declaring what you need in the constructor and ensuring everything is registered in the container, Autofac handles the rest, making your code cleaner and more maintainable. Here's a more advanced Autofac example covered in this video:


Wrapping Up Autofac & Dependency Injection

Congratulations! You've just taken your first steps into the world of Dependency Injection with Autofac. Remember, the key to effective DI is to depend on abstractions, not concrete implementations. Use the appropriate lifetime scope for your services, and leverage the advanced features of Autofac to write clean, maintainable, and testable code.

There are so many more features to uncover in Autofac, so there's no way I could write a single article on it that would cover all of them. We're simply scratching the surface here, but hopefully, it proves as a way to get you started with considering DI, IoC, and using Autofac in your projects!

For more in-depth tutorials and guides on C# and .NET development, check out the other posts on Dev Leader and the Dev Leader YouTube channel. If you haven't already, subscribe to my newsletter to keep up to date with my content releases! Happy coding!

Dependency Injection with Autofac: Not as Difficult as You Think

Looking to get started using dependency injection with Autofac in your projects? Here's a quick primer on what it is and how to get going for your next project.

Using Autofac in C# - 3 Simple Tips for Beginners

Learn how to use Autofac in C# for dependency injection! Check out these three code examples to help you get started with dependency injection in C#.

Autofac ContainerBuilder in ASP.NET Core - What You Need To Know (Part 2)

Learn how to use Autofac ContainerBuilder in ASP.NET Core to wire up dependency injection. I'll explore what we can and cannot do with this approach!

An error has occurred. This application may no longer respond until reloaded. Reload x