Simplifying Dependency Injection

Programming .NET C#

Dependency Injection (DI) is a cornerstone of modern .NET applications, ensuring loose coupling, testability, and maintainability. However, traditional DI configurations can quickly become verbose and boilerplate-heavy. Developers often find themselves manually registering every service, leading to cluttered Program.cs files and a tedious onboarding experience. Campsis.AutoInject comes to the rescue!

Campsis.AutoInject is a lightweight yet powerful automatic DI registration library that eliminates boilerplate code, making .NET DI effortless and intuitive. Here we explore the why, how, and real-world benefits of using Campsis.AutoInject  in your next project.

The Problem with Manual DI

Let's look at a standard .NET application with multiple services:

services.AddScoped<IUserService, UserService>();
services.AddSingleton<ICacheService, MemoryCacheService>();
services.AddTransient<ILogger, Logger>();

For every new service, you have to:

  • Remember to register it in Program.cs.
  • Choose the correct lifecycle (Scoped, Singleton, Transient).
  • Handle service variations (e.g., multiple implementations).

This process is not only redundant but also error-prone — if you forget to register a service, runtime errors will be the consequence.

Campsis.AutoInject  automates this process by scanning assemblies for annotated classes and registering them accordingly.

How It Works

Simply annotate your services with SingletonAttribute, ScopedAttribute, or TransientAttribute like this:

[Singleton(typeof(IUserService))]
public class UserService : IUserService { }

[Scoped(typeof(ICacheService))]
public class MemoryCacheService : ICacheService { }

[Transient(typeof(ILogger))]
public class Logger : ILogger { }

Then, in Program.cs, just call

builder.Services.UseAutoInjection();

Now Campsis.AutoInject scans your assembly, finds the annotated services, and registers them automatically for you.

Injecting Multiple Storage Implementations

Example:

[Singleton(typeof(IStorageBroker), WithKey : "SQL")]
public class SqlStorageBroker : IStorageBroker { }

[Singleton(typeof(IStorageBroker), WithKey : "MONGO")]
public class MongoStorageBroker : IStorageBroker { }

Then inject the appropriate implementation using FromKeyedServices:

[Singleton(typeof(IStudentService))]
public class StudentService(
    [FromKeyedServices("SQL")] IStorageBroker sqlStorageBroker,
    [FromKeyedServices("MONGO")] IStorageBroker mongoStorageBroker) : IStudentService
{
  public ValueTask<string> InsertStudentIntoMongoAsync(object student) =>
        mongoStorageBroker.InsertStudentAsync(student);

  public ValueTask<string> InsertStudentIntoSQLAsync(object student) =>
        sqlStorageBroker.InsertStudentAsync(student);
}

This allows seamless switching between different implementations at runtime.

Install the library via NuGet package manager or via CLI:

dotnet add package Campsis.AutoInject