DI - Dependency Injection

Learn about DI and why this simple concept is useful.

What Is Dependency Injection?

Dependency Injection is the concept of passing dependencies to objects or other frameworks.

When a user wants to swap a class or dependency you can pass a different dependency in place of the old dependency.

How Does Dependency Injection Work?

Here is the main class where it functions.

public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

This separate class depends on the MyDependency to function.

public class IndexModel : PageModel
{
    private readonly MyDependency _dependency;

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet");
    }
}

Dependency injection plays its place when you decide to replace the dependency and test it with another dependency.

Replacing the _dependency.WriteMessage with a different method, to test a more optimal way of going about with the code is dependency injection.

Dependency injection also allows you to prepare all the required classes to function a code.

With a simple connection to the script you need you can change the WriteMessage method above.

These swappable classes use things called services.

What Are Services?

The swappable classes that dependency injection uses are called services. Services are well-defined classes that serve a specific purpose. Any service registered with dependency injection can usually be resolved in Program.cs and app.Services.

Services also offer service scopes.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

What Are Service lifetimes?

There are three types of service lifetimes:

  1. Scoped
  2. Transient
  3. Singleton

Scoped

Service scope is a type of method that allows two different page managers to share the same dependencies.

Page managers allow you to manage and monitor functions of a page

Usually, this is not possible, but service scopes allow this by creating an overhanging method.

Another unique aspect of service scopes is that it allows for a ServiceKey to provide a default implementation.

The ServiceKey is a lookup key that is used when calling ServiceScope.consume() to fetch a dependency.

For web applications, scoped lifetimes are created once per user request.

Service Scope Constructors

Below constructs a new scope for a service scope class.

protected constructor(parent: ServiceScope | undefined);

Transient Lifetimes

Transient lifetime services are created each time they are requested from the service container. This lifetime works best for lightweight, stateless services.

In apps that process requests, transient services are disposed of at the end of the request.

Singleton

Singletons are a type of service that usually are created either the first time they are requested or when providing an implementation instance directly to the container.

Every subsequent request of the service implementation from the dependency injection container uses the same instance. If the app requires singleton behavior, allow the service container to manage the service’s lifetime. Do not implement the singleton design pattern and provide code to dispose of the singleton. Services should never be disposed of by code that resolved the service from the container. If a type of factory is registered as a singleton, the container disposes of the singleton automatically.

How do you make a Service?

Tutorial

  1. In Visual Studios, go to File > New > Project and select “Window Service”

  2. Right-click screen > Add installer t

  3. Right-click screen > View code (F7)

  4. Start programming your service

  5. Right-click project in Solution > Rebuild

  6. Run code in command prompt:

  7. cd C:\Windows\Microsoft.NET\Framework\v4.0.30319

Common rules for creating services:

Avoid stateful, static classes, and members. Avoid creating global state by designing apps to use singleton services instead.

A state is a behavorial pattern that is stored.

When multiple methods are required to share state, people tend to use global variables/objects to store such state.

Avoid direct instantiation of dependent classes within services. Direct instantiation couples the code to a particular implementation.

Make services small, well-factored, and easily testable.

How are services registered?

The ‘Add’ extension methods are used to register all of the services required by a framework feature.

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddConfig(builder.Configuration)
    .AddMyDependencyGroup();

builder.Services.AddRazorPages();

var app = builder.Build();

Helpful Resources and References

ServiceScope class ASP.NET Core Web Host Dependency injection in ASP.NET Core Introduction to services and dependency injection Service Scope Method Details More Method Details Link Transient lifetime methods

Last modified March 13, 2022: Added the entire seeder code (f8c545e)