Implementing Dependency Injection with AutoFac in .NET Console Applications

In software architecture, dependencies arise when one class requires collaboration from another to function. This relationship often leads to coupling. Consider a standard three-tier structure consisting of a User Interface, Business Logic, and Data Access layer. Without proper abstraction, the business logic layer directly instantiates data access objects, creating a tight coupling where changes in data retrieval logic necessitate changes in business logic.

Interface-Driven Design

To mitigate tight coupling, systems should depend on abstractions rather then concrete implementations. This is achieved by defining interfaces for data access layers. For instance, instead of relying on a specific UserDataStore class, the business logic depends on an IUserDataStore interface.

This approach offers two primary benefits: reduced coupling and clear separation of responsibilities. Development teams can work in parallel; the business logic team defines the interface requirements, while the data team implements the underlying storage mechanisms. If the storage implementation chenges—for example, moving from static data to a SQL database—only the concrete implementation class needs modification, provided it adheres to the interface contract.

Understanding IoC and DI

Inversion of Control (IoC) is a design principle where the flow of control is inverted. Instead of the caller creating dependencies, the control is handed over to a framework or container. Dependency Injection (DI) is a specific pattern used to achieve IoC.

In a DI setup, external components (dependencies) are provided to a class rather than the class creating them internally. This is typically managed by a container that holds registrations of services and their implementations. When a service is requested, the container resolves the dependencies and injects them, often via constructors.

While IoC is a broad concept, DI is the practical mechanism. Confusing the two is common, but technically, DI is a subset of IoC strategies.

AutoFac Container Overview

AutoFac is a popular Inversion of Control container for .NET. It manages the lifecycle of objects and resolves dependencies automatically. It supports various registration modes, including type mapping, instance sharing, and assembly scanning.

Console Application Implementation

The following example demonstrates setting up dependency injection in a .NET Framework console application using AutoFac. The goal is to retrieve a user profile name without manually instantiating the data access layer within the business logic.

Project Structure

The solution is divided into distinct layers:

  • Model: Contains data structures.
  • Repositroy: Handles data access logic.
  • Service: Contains business logic.
  • Infrastructure: Manages the DI container.
  • ConsoleApp: The entry point.

Model Layer

Defines the data structure used across layers.

namespace Demo.DependencyInjection.Models
{
    public class UserProfile
    {
        public long UserId { get; set; }
        public string DisplayName { get; set; }
        public int Score { get; set; }
    }
}

Repository Layer

Defines the interface and concrete implementation for data access.

using Demo.DependencyInjection.Models;

namespace Demo.DependencyInjection.Repository.Contracts
{
    public interface IUserDataStore
    {
        string FetchUserName(long userId);
    }
}

namespace Demo.DependencyInjection.Repository.Implementation
{
    public class UserDataStore : IUserDataStore
    {
        public string FetchUserName(long userId)
        {
            // Simulating data retrieval
            return "Demo User";
        }
    }
}

Service Layer

Implements business logic depending on the repository interface via constructor injection.

using Demo.DependencyInjection.Repository.Contracts;

namespace Demo.DependencyInjection.Service.Contracts
{
    public interface IAccountManager
    {
        string GetProfileName(long userId);
    }
}

namespace Demo.DependencyInjection.Service.Implementation
{
    public class AccountManager : IAccountManager
    {
        private readonly IUserDataStore _dataAccess;

        public AccountManager(IUserDataStore dataAccess)
        {
            _dataAccess = dataAccess;
        }

        public string GetProfileName(long userId)
        {
            return _dataAccess.FetchUserName(userId);
        }
    }
}

Infrastructure Layer

Configures the AutoFac container and registers services.

using Autofac;
using Demo.DependencyInjection.Repository.Contracts;
using Demo.DependencyInjection.Repository.Implementation;
using Demo.DependencyInjection.Service.Contracts;
using Demo.DependencyInjection.Service.Implementation;

namespace Demo.DependencyInjection.Infrastructure
{
    public static class DependencyRegistry
    {
        public static IContainer Container { get; private set; }

        public static void Bootstrap()
        {
            var builder = new ContainerBuilder();
            RegisterServices(builder);
            Container = builder.Build();
        }

        private static void RegisterServices(ContainerBuilder builder)
        {
            builder.RegisterType<UserDataStore>()
                   .As<IUserDataStore>()
                   .InstancePerDependency();

            builder.RegisterType<AccountManager>()
                   .As<IAccountManager>()
                   .InstancePerDependency();
        }
    }
}

Entry Point

The console application initializes the container and resolves services.

using System;
using Demo.DependencyInjection.Infrastructure;
using Demo.DependencyInjection.Service.Contracts;

namespace Demo.DependencyInjection.ConsoleApp
{
    class Startup
    {
        static void Main(string[] args)
        {
            DependencyRegistry.Bootstrap();
            DisplayUserInfo(1001);
            Console.ReadKey();
        }

        private static void DisplayUserInfo(long id)
        {
            var manager = DependencyRegistry.Container.Resolve<IAccountManager>();
            var name = manager.GetProfileName(id);
            Console.WriteLine($"User: {name}");
        }
    }
}

Execution Flow

  1. Initialization: Bootstrap creates the container and registers mappings between interfaces and concrete classes.
  2. Resolution: When Resolve<IAccountManager> is called, AutoFac identifies AccountManager as the implementation.
  3. Injection: During AccountManager instantiation, AutoFac detects the IUserDataStore constructor parameter. It recursively resolves UserDataStore and injects it.
  4. Execution: The business logic executes using the injected dependency.

Practical Considerations

While console applications demonstrate the core mechanics, production environments like ASP.NET MVC or WebAPI utilize different patterns to avoid manual resolution.

Batch Registration

Manually registering every type is inefficient. In larger systems, assembly scanning is preferred. AutoFac can scan assemblies for types matching specific naming conventions (e.g., ending in "Service" or "Repository") and register them automatically against their interfaces.

Constructor Injection in Controllers

In web frameworks, manual resolution in methods is unnecessary. Frameworks integrate with DI containers to inject dependencies directly into controller constructors. This keeps controllers clean and testable.

public class UserController : Controller
{
    private readonly IAccountManager _manager;

    public UserController(IAccountManager manager)
    {
        _manager = manager;
    }

    public string GetInfo(long id)
    {
        return _manager.GetProfileName(id);
    }
}

Design Complexity

Dependency Injection is a pattern, not a silver bullet. Its utility depends on the project scale. For small scripts, direct instantiation may suffice. However, for maintainable enterprise applications, especially within the .NET Core ecosystem where DI is built-in, understanding these patterns is essential for decoupling components and facilitating unit testing.

Tags: dependency-injection autofac dotnet inversion-of-control software-architecture

Posted on Mon, 11 May 2026 00:06:12 +0000 by smitthhyy