Share via


Självstudie: Använda beroendeinmatning i .NET

Den här självstudien visar hur du använder beroendeinmatning (DI) i .NET. Med Microsoft-tillägg hanteras DI genom att lägga till tjänster och konfigurera dem i en IServiceCollection. Gränssnittet IHost exponerar instansen IServiceProvider , som fungerar som en container för alla registrerade tjänster.

I den här självstudien lär du dig att:

  • Skapa en .NET-konsolapp som använder beroendeinmatning
  • Skapa och konfigurera en allmän värd
  • Skriva flera gränssnitt och motsvarande implementeringar
  • Använda tjänstens livslängd och omfång för DI

Förutsättningar

  • .NET Core 3.1 SDK eller senare.
  • Kunskaper om att skapa nya .NET-program och installera NuGet-paket.

Skapa ett nytt konsolprogram

Skapa ett nytt .NET-konsolprogram med namnet ConsoleDI med hjälp av antingen det nya dotnet-kommandot eller en ny IDE-projektguide.Example Lägg till NuGet-paketet Microsoft.Extensions.Hosting i projektet.

Den nya konsolappens projektfil bör likna följande:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>
    <RootNamespace>ConsoleDI.Example</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
  </ItemGroup>

</Project>

Viktigt!

I det här exemplet krävs NuGet-paketet Microsoft.Extensions.Hosting för att skapa och köra appen. Vissa metapaket kan innehålla Microsoft.Extensions.Hosting paketet, i vilket fall en explicit paketreferens inte krävs.

Lägga till gränssnitt

I den här exempelappen får du lära dig hur beroendeinmatning hanterar tjänstens livslängd. Du skapar flera gränssnitt som representerar olika tjänstlivslängder. Lägg till följande gränssnitt i projektrotkatalogen:

IReportServiceLifetime.cs

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IReportServiceLifetime
{
    Guid Id { get; }

    ServiceLifetime Lifetime { get; }
}

Gränssnittet IReportServiceLifetime definierar:

  • En Guid Id egenskap som representerar den unika identifieraren för tjänsten.
  • En ServiceLifetime egenskap som representerar tjänstens livslängd.

JagExampleTransientService.cs

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IExampleTransientService : IReportServiceLifetime
{
    ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}

JagExampleScopedService.cs

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IExampleScopedService : IReportServiceLifetime
{
    ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}

JagExampleSingletonService.cs

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IExampleSingletonService : IReportServiceLifetime
{
    ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}

Alla undergränssnitt IReportServiceLifetime för att uttryckligen implementera IReportServiceLifetime.Lifetime med ett standardvärde. Implementerar IReportServiceLifetime.Lifetime till exempel IExampleTransientService uttryckligen med ServiceLifetime.Transient värdet.

Lägga till standardimplementeringar

Exempelimplementeringarna initierar alla sin Id egenskap med resultatet av Guid.NewGuid(). Lägg till följande standardimplementeringsklasser för de olika tjänsterna i projektrotkatalogen:

ExampleTransientService.cs

namespace ConsoleDI.Example;

internal sealed class ExampleTransientService : IExampleTransientService
{
    Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

ExampleScopedService.cs

namespace ConsoleDI.Example;

internal sealed class ExampleScopedService : IExampleScopedService
{
    Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

ExampleSingletonService.cs

namespace ConsoleDI.Example;

internal sealed class ExampleSingletonService : IExampleSingletonService
{
    Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

Varje implementering definieras som internal sealed och implementerar motsvarande gränssnitt. Implementerar IExampleSingletonServicetill exempel ExampleSingletonService .

Lägga till en tjänst som kräver DI

Lägg till följande reporterklass för tjänstlivslängd, som fungerar som en tjänst i konsolappen:

ServiceLifetimeReporter.cs

namespace ConsoleDI.Example;

internal sealed class ServiceLifetimeReporter(
    IExampleTransientService transientService,
    IExampleScopedService scopedService,
    IExampleSingletonService singletonService)
{
    public void ReportServiceLifetimeDetails(string lifetimeDetails)
    {
        Console.WriteLine(lifetimeDetails);

        LogService(transientService, "Always different");
        LogService(scopedService, "Changes only with lifetime");
        LogService(singletonService, "Always the same");
    }

    private static void LogService<T>(T service, string message)
        where T : IReportServiceLifetime =>
        Console.WriteLine(
            $"    {typeof(T).Name}: {service.Id} ({message})");
}

Definierar ServiceLifetimeReporter en konstruktor som kräver vart och ett av de ovan nämnda tjänstgränssnitten, dvs. IExampleTransientService, IExampleScopedServiceoch IExampleSingletonService. Objektet exponerar en enda metod som gör att konsumenten kan rapportera om tjänsten med en viss lifetimeDetails parameter. När den ReportServiceLifetimeDetails anropas loggar metoden varje tjänsts unika identifierare med meddelandet om tjänstlivslängd. Loggmeddelandena hjälper till att visualisera tjänstens livslängd.

Registrera tjänster för DI

Uppdatera Program.cs med följande kod:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddTransient<IExampleTransientService, ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();

using IHost host = builder.Build();

ExemplifyServiceLifetime(host.Services, "Lifetime 1");
ExemplifyServiceLifetime(host.Services, "Lifetime 2");

await host.RunAsync();

static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
    using IServiceScope serviceScope = hostProvider.CreateScope();
    IServiceProvider provider = serviceScope.ServiceProvider;
    ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
    logger.ReportServiceLifetimeDetails(
        $"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");

    Console.WriteLine("...");

    logger = provider.GetRequiredService<ServiceLifetimeReporter>();
    logger.ReportServiceLifetimeDetails(
        $"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");

    Console.WriteLine();
}

Varje services.Add{LIFETIME}<{SERVICE}> tilläggsmetod lägger till (och kan konfigurera) tjänster. Vi rekommenderar att appar följer den här konventionen. Placera inte tilläggsmetoder i Microsoft.Extensions.DependencyInjection namnområdet om du inte skapar ett officiellt Microsoft-paket. Tilläggsmetoder som definieras i Microsoft.Extensions.DependencyInjection namnområdet:

  • Visas i IntelliSense utan ytterligare using block.
  • Minska antalet obligatoriska using instruktioner i de Program eller Startup klasser där dessa tilläggsmetoder vanligtvis anropas.

Appen:

Slutsats

I den här exempelappen skapade du flera gränssnitt och motsvarande implementeringar. Var och en av dessa tjänster identifieras unikt och paras ihop med en ServiceLifetime. Exempelappen visar hur du registrerar tjänstimplementeringar mot ett gränssnitt och hur du registrerar rena klasser utan stödgränssnitt. Exempelappen visar sedan hur beroenden som definieras som konstruktorparametrar löses vid körning.

När du kör appen visas utdata som liknar följande:

// Sample output:
// Lifetime 1: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: d08a27fa-87d2-4a06-98d7-2773af886125 (Always different)
//     IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 1: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: b43d68fb-2c7b-4a9b-8f02-fc507c164326 (Always different)
//     IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// 
// Lifetime 2: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: f3856b59-ab3f-4bbd-876f-7bab0013d392 (Always different)
//     IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 2: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
//     IExampleTransientService: a8015c6a-08cd-4799-9ec3-2f2af9cbbfd2 (Always different)
//     IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
//     IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)

Från apputdata kan du se följande:

  • Transient tjänster är alltid olika, skapas en ny instans med varje hämtning av tjänsten.
  • Scoped tjänster ändras endast med ett nytt omfång, men är samma instans inom ett omfång.
  • Singleton tjänster är alltid desamma, skapas en ny instans bara en gång.

Se även