Your first ASP.NET App: Dependency Injection
DEV Community

Your first ASP.NET App: Dependency Injection

Requirements

  • IDE like Visual Studio / JetBrains Rider
  • .NET >= 10
  • Base C# skill
  • CPU (manually)

What is DI

DI (Dependency Injection) in C# is a design pattern where a class receives its dependencies from the outside instead of creating them itself. In short: it replaces hardcoded new operators with passing ready-to-use objects through the constructor.

Base DI Types

Microsoft.Extensions.DependencyInjection has 3 base lifetime types:

  • Singleton - Lives for the entire duration of the application's execution. Just one instance for all. Like AppDbContext or another.
  • Scoped - Lives for every HTTP request. Every HTTP request has its own scope. Scoped lives in scope like IUserRepository, IAuthService.
  • Transient - Lives for the shortest time. It is created every time it is requested from the DI container. Like IEmailSender, IValidator, or lightweight helper services.

Let's Create Our Own DI

I wrote a simple service like this:

namespace ExampleProject;

public interface IMyService
{
    Task<string> GetHello(string name);
}

public class MyService : IMyService
{
    public async Task<string> GetHello(string name)
    {
        await Task.Delay(150);
        return $"Hello, {name}!";
    }
}

Now let's add this to our controller (MyController) method:

public async Task<IActionResult> Get(string name)

Let's replace that method with this:

[HttpGet]
public async Task<IActionResult> Get(string name)
{
    var start = DateTime.Now;
    var hello = await service.GetHello(name);
    var end = DateTime.Now;
    return Ok($"{hello} It took {end - start} to respond.");
}

So, we need to replace the class declaration to add a constructor to this:

public class MyController(IMyService service) : ControllerBase

Now let's add our Service to DI using:

using ExampleProject;
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddControllers();
builder.Services.AddScoped<IMyService, MyService>();
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.MapScalarApiReference();
}

app.MapControllers();
app.Run();

Testing

Now run, and open in browser localhost:[your port]/scalar. Press Test Request, and press Send.

And now you'll see: Hello, sabaka! It took 00:00:00.1608323 to respond.

In the next chapters we learn Databases, Clean Architecture and more.


Top Comments (2)

One thing worth adding for readers of this series: with a primary constructor the injected service silently becomes a field, so the moment someone registers a singleton that takes a scoped dependency, the scoped thing gets captured once and lives for the whole process. Nothing complains at startup - the symptom is state leaking between unrelated requests, and it's miserable to debug later. A sentence on mismatched lifetimes would save people hours.

Small nit on the measurement: DateTime.Now is a fragile way to time a handler, because a clock sync or a laptop sleep/resume can make the delta negative or absurd. Stopwatch is free and trustworthy. Otherwise a clean walkthrough, and the Scalar step makes "actually send a request" much less abstract than the usual intro.

Thanks, I will make fixes in next chapter of this series. Thanks for attention : )

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.