Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
I den här artikeln skapar du en .NET-konsolapp som manuellt skapar en ServiceCollection och motsvarande ServiceProvider. Du lär dig hur du registrerar tjänster och hanterar dem med hjälp av beroendeinjektion (DI). Den här artikeln använder NuGet-paketet Microsoft.Extensions.DependencyInjection för att demonstrera grunderna i DI i .NET.
Anteckning
Den här artikeln drar inte nytta av funktionerna för generisk värd. En mer omfattande guide finns i Använda beroendeinmatning i .NET.
Kom igång
Kom igång genom att skapa ett nytt .NET-konsolprogram med namnet DI.Basics. Några av de vanligaste metoderna för att skapa ett konsolprojekt refereras i följande lista:
- Visual Studio: Arkiv > Nytt > Projekt-menyn.
- Visual Studio Code och menyalternativet för C# Dev Kit-tillägget: Solution Explorer .
- 
              .NET CLI: dotnet new consolekommando i terminalen.
Du måste lägga till paketreferensen till Microsoft.Extensions.DependencyInjection i projektfilen. Oavsett metod bör du se till att projektet liknar följande XML för FILEN DI.Basics.csproj :
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
  </ItemGroup>
</Project>
Grunderna för beroendeinjektion
Beroendeinmatning är ett designmönster som gör att du kan ta bort hårdkodade beroenden och göra programmet mer underhållsbart och testbart. DI är en teknik för att uppnå inversion av kontroll (IoC) mellan klasser och deras beroenden.
Abstraktionerna för DI i .NET definieras i NuGet-paketet Microsoft.Extensions.DependencyInjection.Abstractions :
- IServiceCollection: Definierar ett kontrakt för en samling tjänstbeskrivningar.
- IServiceProvider: Definierar en mekanism för att hämta ett tjänstobjekt.
- ServiceDescriptor: Beskriver en tjänst med dess tjänsttyp, implementering och livslängd.
I .NET hanteras DI genom att lägga till tjänster och konfigurera dem i en IServiceCollection. När tjänsterna har registrerats skapas en IServiceProvider instans genom att metoden anropas BuildServiceProvider . Fungerar IServiceProvider som en container för alla registrerade tjänster och används för att lösa tjänster.
Skapa exempeltjänster
Alla tjänster skapas inte lika. Vissa tjänster kräver en ny instans varje gång tjänstcontainern hämtar dem (transient), medan andra ska delas över begäranden (scoped) eller under hela appens livslängd (singleton). Mer information om tjänstlivslängder finns i Tjänstlivslängder.
På samma sätt exponerar vissa tjänster bara en konkret typ, medan andra uttrycks som ett kontrakt mellan ett gränssnitt och en implementeringstyp. Du skapar flera varianter av tjänster för att demonstrera dessa begrepp.
Skapa en ny C#-fil med namnet IConsole.cs och lägg till följande kod:
public interface IConsole
{
    void WriteLine(string message);
}
Den här filen definierar ett IConsole gränssnitt som exponerar en enda metod, WriteLine. Skapa sedan en ny C#-fil med namnet DefaultConsole.cs och lägg till följande kod:
internal sealed class DefaultConsole : IConsole
{
    public bool IsEnabled { get; set; } = true;
    void IConsole.WriteLine(string message)
    {
        if (IsEnabled is false)
        {
            return;
        }
        Console.WriteLine(message);
    }
}
Föregående kod representerar standardimplementeringen av IConsole gränssnittet. Metoden WriteLine skriver villkorligt till konsolen baserat på IsEnabled egenskapen .
Dricks
Namngivning av en implementering är ett val som utvecklingsteamet bör komma överens om. Prefixet Default är en vanlig konvention som anger en standardimplementering av ett gränssnitt, men det krävs inte .
Skapa sedan en IGreetingService.cs fil och lägg till följande C#-kod:
public interface IGreetingService
{
    string Greet(string name);
}
Lägg sedan till en ny C#-fil med namnet DefaultGreetingService.cs och lägg till följande kod:
internal sealed class DefaultGreetingService(
    IConsole console) : IGreetingService
{
    public string Greet(string name)
    {
        var greeting = $"Hello, {name}!";
        console.WriteLine(greeting);
        return greeting;
    }
}
Föregående kod representerar standardimplementeringen av IGreetingService gränssnittet. Tjänstimplementeringen kräver en IConsole som primär konstruktorparameter. Metoden Greet :
- Skapar en greetinggivetname.
- Anropar WriteLine-metoden påIConsole-instansen.
- Returnerar greetingtill anroparen.
Den sista tjänsten som ska skapas är FarewellService.cs-filen, lägg till följande C#-kod innan du fortsätter:
public class FarewellService(IConsole console)
{
    public string SayGoodbye(string name)
    {
        var farewell = $"Goodbye, {name}!";
        console.WriteLine(farewell);
        return farewell;
    }
}
Representerar FarewellService en konkret typ, inte ett gränssnitt. Den bör deklareras så public att den blir tillgänglig för konsumenterna. Till skillnad från andra tjänstimplementeringstyper som deklarerades som internal och sealedvisar den här koden att inte alla tjänster behöver vara gränssnitt. Det visar också att tjänstimplementeringar kan vara sealed för att förhindra arv och internal för att begränsa åtkomsten till programdelen.
Uppdatera Program-klassen
Öppna filen Program.cs och ersätt den befintliga koden med följande C#-kod:
using Microsoft.Extensions.DependencyInjection;
// 1. Create the service collection.
var services = new ServiceCollection();
// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    });
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();
// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();
// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();
// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");
Föregående uppdaterade kod visar instruktioner:
- Skapa en ny ServiceCollectioninstans.
- Registrera och konfigurera tjänster i ServiceCollection:- Den IConsoleanvänder implementeringsfabrikens överlagring och returnerar enDefaultConsole-typ medIsEnabledinställd påtrue.
- 
              IGreetingServiceLäggs till med en motsvarande implementeringstyp avDefaultGreetingServicetypen.
- 
              FarewellServiceLäggs till som en betongtyp.
 
- Den 
- Bygg ServiceProviderfrånServiceCollection.
- Lös tjänsterna IGreetingServiceochFarewellService.
- Använd de lösta tjänsterna för att hälsa och säga adjö till en person med namnet David.
Om du uppdaterar IsEnabled-egenskapen för DefaultConsole till false, utelämnar Greet-metoderna och SayGoodbye-metoden skrivningen till de resulterande meddelandena till konsolen. En ändring som den här hjälper till att visa att IConsole tjänsten matas in i IGreetingService tjänsterna och FarewellService som ett beroende som påverkar apparnas beteende.
Alla dessa tjänster registreras som singletons, men för det här exemplet fungerar de identiskt om de registrerades som tillfälliga eller begränsade tjänster.
Viktigt!
I det här invecklade exemplet spelar tjänstlivslängden ingen roll, men i ett verkligt program bör du noga överväga livslängden för varje tjänst.
Kör exempelappen
Om du vill köra exempelappen trycker du antingen på F5 i Visual Studio, Visual Studio Code eller kör dotnet run kommandot i terminalen. När appen är klar bör du se följande utdata:
Hello, David!
Goodbye, David!
Tjänstbeskrivningar
De vanligaste API:erna för att lägga till tjänster i ServiceCollection är generiska tilläggsmetoder namngivna efter livslängd, till exempel:
- AddSingleton<TService>
- AddTransient<TService>
- AddScoped<TService>
Dessa metoder är bekvämlighetsmetoder som skapar en ServiceDescriptor instans och lägger till den i ServiceCollection. 
              ServiceDescriptor är en enkel klass som beskriver en tjänst med dess tjänsttyp, implementeringstyp och livslängd. Den kan också beskriva implementeringsfabriker och instanser.
För var och en av de tjänster som du registrerade i ServiceCollection, kan du istället anropa Add-metoden med en ServiceDescriptor-instans direkt. Föreställ dig följande exempel:
services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IConsole),
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    },
    lifetime: ServiceLifetime.Singleton));
Föregående kod motsvarar hur IConsole tjänsten registrerades i ServiceCollection. Metoden Add används för att lägga till en ServiceDescriptor instans som beskriver IConsole tjänsten. Den statiska metoden ServiceDescriptor.Describe delegerar till olika ServiceDescriptor konstruktorer. Överväg motsvarande kod för IGreetingService tjänsten:
services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IGreetingService),
    implementationType: typeof(DefaultGreetingService),
    lifetime: ServiceLifetime.Singleton));
Föregående kod beskriver tjänsten IGreetingService med dess tjänsttyp, implementeringstyp och livslängd. Tänk slutligen på motsvarande kod för FarewellService tjänsten:
services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(FarewellService),
    implementationType: typeof(FarewellService),
    lifetime: ServiceLifetime.Singleton));
Föregående kod beskriver den konkreta FarewellService typen som både tjänst- och implementeringstyper. Tjänsten är registrerad som en tjänst av typen singleton.