.NET bağımlılık ekleme

.NET, sınıflar ve bağımlılıkları arasında Denetimin Ters Çevrilmesi (IoC) elde etmek için kullanılan bir teknik olan bağımlılık ekleme (DI) yazılım tasarımı desenini destekler. .NET'te bağımlılık ekleme, yapılandırma, günlüğe kaydetme ve seçenekler deseninin yanı sıra çerçevenin yerleşik bir parçasıdır.

Bağımlılık, başka bir nesnenin bağımlı olduğu bir nesnedir. Aşağıdaki MessageWriter sınıfı diğer sınıfların bağımlı olduğu bir Write yöntemle inceleyin:

public class MessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
    }
}

Bir sınıf, yöntemini kullanmak için sınıfının bir örneğini MessageWriterWrite oluşturabilir. Aşağıdaki örnekte sınıfı, MessageWriter sınıfının bir bağımlılığıdır Worker :

public class Worker : BackgroundService
{
    private readonly MessageWriter _messageWriter = new();

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
            await Task.Delay(1_000, stoppingToken);
        }
    }
}

sınıfı oluşturur ve doğrudan sınıfına MessageWriter bağlıdır. Önceki örnekte olduğu gibi sabit kodlanmış bağımlılıklar sorunludur ve aşağıdaki nedenlerle kaçınılmalıdır:

  • öğesini farklı bir uygulamayla değiştirmek MessageWriter için sınıfı değiştirilmelidir Worker .
  • Bağımlılıkları varsa MessageWriter , bunların da sınıfı tarafından yapılandırılması Worker gerekir. bağlı olarak MessageWriterbirden çok sınıfa sahip büyük bir projede yapılandırma kodu uygulamaya dağıtılır.
  • Bu uygulamanın birim testi zor. Uygulama, bu yaklaşımla mümkün olmayan bir sahte veya saplama MessageWriter sınıfı kullanmalıdır.

Bağımlılık ekleme aşağıdakiler aracılığıyla bu sorunları giderir:

  • Bağımlılık uygulamasını soyutlama amacıyla bir arabirim veya temel sınıf kullanımı.
  • Hizmet kapsayıcısında bağımlılığın kaydı. .NET, yerleşik bir hizmet kapsayıcısı sağlar. IServiceProvider Hizmetler genellikle uygulamanın başlangıcında kaydedilir ve öğesine IServiceCollectioneklenir. Tüm hizmetler eklendikten sonra, hizmet kapsayıcısını oluşturmak için kullanırsınız BuildServiceProvider .
  • Hizmetin kullanıldığı sınıfın oluşturucusuna eklenmesi. Çerçeve, bağımlılığın bir örneğini oluşturma ve artık gerekli olmadığında bunu yok etme sorumluluğunu üstlenir.

Örnek olarak, IMessageWriter arabirimi yöntemini tanımlar Write :

namespace DependencyInjection.Example;

public interface IMessageWriter
{
    void Write(string message);
}

Bu arabirim somut bir tür tarafından uygulanır: MessageWriter

namespace DependencyInjection.Example;

public class MessageWriter : IMessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
    }
}

Örnek kod, hizmeti somut türüyle MessageWriterkaydederIMessageWriter. yöntemi, AddSingleton hizmeti uygulamanın ömrü olan tek bir yaşam süresiyle kaydeder. Hizmet yaşam süreleri bu makalenin devamında açıklanmıştır.

using DependencyInjection.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();

using IHost host = builder.Build();

host.Run();

Yukarıdaki kodda örnek uygulama:

  • Bir konak uygulama oluşturucu örneği oluşturur.

  • Aşağıdakileri kaydederek hizmetleri yapılandırıyor:

    • Barındırılan Worker hizmet olarak. Daha fazla bilgi için bkz . .NET'te Çalışan Hizmetleri.
    • Sınıfının IMessageWriter karşılık gelen uygulamasına MessageWriter sahip tekil bir hizmet olarak arabirim.
  • Konağı oluşturur ve çalıştırır.

Konak, bağımlılık ekleme hizmet sağlayıcısını içerir. Ayrıca, öğesini otomatik olarak başlatmak Worker ve ilgili IMessageWriter uygulamayı bağımsız değişken olarak sağlamak için gereken diğer tüm ilgili hizmetleri içerir.

namespace DependencyInjection.Example;

public sealed class Worker(IMessageWriter messageWriter) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
            await Task.Delay(1_000, stoppingToken);
        }
    }
}

ÇALıŞAN hizmeti, DI desenini kullanarak:

  • Somut türünü MessageWriterkullanmaz, yalnızca IMessageWriter bunu uygulayan arabirimi kullanır. Bu, çalışan hizmetini değiştirmeden çalışan hizmetinin kullandığı uygulamayı değiştirmeyi kolaylaştırır.
  • örneğini MessageWriteroluşturmaz. Örnek, DI kapsayıcısı tarafından oluşturulur.

Yerleşik günlük API'sini IMessageWriter kullanarak arabirimin uygulanması geliştirilebilir:

namespace DependencyInjection.Example;

public class LoggingMessageWriter(
    ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
    public void Write(string message) =>
        logger.LogInformation("Info: {Msg}", message);
}

Güncelleştirilmiş AddSingleton yöntem yeni IMessageWriter uygulamayı kaydeder:

builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();

HostApplicationBuilder (builder) türü NuGet paketinin Microsoft.Extensions.Hosting bir parçasıdır.

LoggingMessageWriterILogger<TCategoryName>oluşturucuda istediği öğesine bağlıdır. ILogger<TCategoryName> çerçeve tarafından sağlanan bir hizmettir.

Bağımlılık ekleme özelliğini zincirleme bir şekilde kullanmak olağan dışı değildir. İstenen her bağımlılık da kendi bağımlılıklarını istemektedir. Kapsayıcı, grafikteki bağımlılıkları çözer ve tam olarak çözümlenen hizmeti döndürür. Çözülmesi gereken ortak bağımlılık kümesi genellikle bağımlılık ağacı, bağımlılık grafı veya nesne grafı olarak adlandırılır.

Kapsayıcı , (genel) açık türlerden yararlanarak çözümlenerek ILogger<TCategoryName> her (genel) yapı türüne kaydolma gereksinimini ortadan kaldırır.

Bağımlılık ekleme terminolojisi ile bir hizmet:

  • Genellikle hizmet gibi diğer nesnelere hizmet sağlayan bir nesnedir IMessageWriter .
  • Bir web hizmetiyle ilgili değildir, ancak hizmet bir web hizmeti kullanabilir.

Çerçeve güçlü bir günlük sistemi sağlar. IMessageWriter Önceki örneklerde gösterilen uygulamalar, günlüğe kaydetmeyi değil, temel DI'yi göstermek için yazılmıştır. Çoğu uygulamanın günlükçü yazması gerekmez. Aşağıdaki kod, yalnızca barındırılan hizmet AddHostedServiceolarak kaydedilmesini gerektiren Worker varsayılan günlüğün kullanılmasını gösterir:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger) =>
        _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1_000, stoppingToken);
        }
    }
}

Önceki kodu kullanarak, Program.cs güncelleştirmeye gerek yoktur, çünkü günlüğe kaydetme çerçeve tarafından sağlanır.

Birden çok oluşturucu bulma kuralı

Bir tür birden fazla oluşturucu tanımladığında, hizmet sağlayıcısının hangi oluşturucunun kullanılacağını belirleme mantığı vardır. Türlerin DI çözümlenebilir olduğu en çok parametreye sahip oluşturucu seçilir. Aşağıdaki C# örnek hizmetini göz önünde bulundurun:

public class ExampleService
{
    public ExampleService()
    {
    }

    public ExampleService(ILogger<ExampleService> logger)
    {
        // omitted for brevity
    }

    public ExampleService(FooService fooService, BarService barService)
    {
        // omitted for brevity
    }
}

Yukarıdaki kodda, günlük kaydının eklendiğini ve hizmet sağlayıcısından çözülebilir olduğunu, ancak ve BarService türlerinin FooService çözümlenmediğini varsayalım. parametresine ILogger<ExampleService> sahip oluşturucu örneği çözümlemek ExampleService için kullanılır. Daha fazla parametre FooService tanımlayan bir oluşturucu olsa da ve BarService türleri DI çözümlenebilir değildir.

Oluşturucular keşfedilirken belirsizlik varsa, bir özel durum oluşturulur. Aşağıdaki C# örnek hizmetini göz önünde bulundurun:

public class ExampleService
{
    public ExampleService()
    {
    }

    public ExampleService(ILogger<ExampleService> logger)
    {
        // omitted for brevity
    }

    public ExampleService(IOptions<ExampleOptions> options)
    {
        // omitted for brevity
    }
}

Uyarı

ExampleService Belirsiz DI çözümlenebilir tür parametrelerine sahip kod bir özel durum oluşturur. Bunu yapma, "belirsiz DI çözümlenebilir türler" ile ne anlama geldiğini göstermek için tasarlanmıştır.

Yukarıdaki örnekte üç oluşturucu vardır. İlk oluşturucu parametresizdir ve hizmet sağlayıcısından hizmet gerektirmez. Hem günlüğe kaydetme hem de seçeneklerin DI kapsayıcısına eklendiğini ve DI çözümlenebilir hizmetler olduğunu varsayalım. DI kapsayıcısı türü çözümlemeye ExampleService çalıştığında, iki oluşturucu belirsiz olduğundan bir özel durum oluşturur.

Bunun yerine her iki DI çözümlenebilir türü de kabul eden bir oluşturucu tanımlayarak belirsizlikten kaçınabilirsiniz:

public class ExampleService
{
    public ExampleService()
    {
    }

    public ExampleService(
        ILogger<ExampleService> logger,
        IOptions<ExampleOptions> options)
    {
        // omitted for brevity
    }
}

Uzantı yöntemleriyle hizmet gruplarını kaydetme

Microsoft Uzantıları, bir grup ilgili hizmeti kaydetmek için bir kural kullanır. Kural, bir çerçeve özelliğinin gerektirdiği tüm hizmetleri kaydetmek için tek Add{GROUP_NAME} bir uzantı yöntemi kullanmaktır. Örneğin, AddOptions uzantı yöntemi seçenekleri kullanmak için gereken tüm hizmetleri kaydeder.

Çerçeve tarafından sağlanan hizmetler

Kullanılabilir konak veya uygulama oluşturucu desenlerinden herhangi birini kullanırken varsayılanlar uygulanır ve hizmetler çerçeve tarafından kaydedilir. En popüler konak ve uygulama oluşturucu desenlerinden bazılarını göz önünde bulundurun:

Bu API'lerden herhangi birinden oluşturucu oluşturduktan sonra, IServiceCollection konağın nasıl yapılandırıldığına bağlı olarak çerçeve tarafından tanımlanan hizmetlere sahiptir. .NET şablonlarını temel alan uygulamalar için çerçeve yüzlerce hizmeti kaydedebilir.

Aşağıdaki tabloda bu çerçeveye kayıtlı hizmetlerin küçük bir örneği listelenmektedir:

Hizmet Türü Yaşam süresi
Microsoft.Extensions.DependencyInjection.IServiceScopeFactory Tekil
IHostApplicationLifetime Tekil
Microsoft.Extensions.Logging.ILogger<TCategoryName> Tekil
Microsoft.Extensions.Logging.ILoggerFactory Tekil
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Tekil
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Geçici
Microsoft.Extensions.Options.IOptions<TOptions> Tekil
System.Diagnostics.DiagnosticListener Tekil
System.Diagnostics.DiagnosticSource Tekil

Hizmet ömrü

Hizmetler aşağıdaki yaşam süresinden birine kaydedilebilir:

Aşağıdaki bölümlerde önceki yaşam ömürlerinin her biri açıklanmaktadır. Kayıtlı her hizmet için uygun bir yaşam süresi seçin.

Geçici

Geçici yaşam süresi hizmetleri, hizmet kapsayıcısından her istendiklerinde oluşturulur. Bir hizmeti geçici olarak kaydetmek için çağrısı yapınAddTransient.

İstekleri işleyen uygulamalarda, geçici hizmetler isteğin sonunda atılır. Hizmetler her seferinde çözümlenip oluşturulurken bu yaşam süresi istek başına ayırmalara neden olur. Daha fazla bilgi için bkz . Bağımlılık Ekleme Yönergeleri: Geçici ve paylaşılan örnekler için IDisposable kılavuzu.

Kapsamlı

Web uygulamaları için kapsamı belirlenmiş bir yaşam süresi, hizmetlerin istemci isteği (bağlantı) başına bir kez oluşturulduğunu gösterir. Kapsamı belirlenmiş hizmetleri ile AddScopedkaydedin.

İstekleri işleyen uygulamalarda, kapsamı belirlenmiş hizmetler isteğin sonunda atılır.

Entity Framework Core kullanılırken uzantı yöntemi türleri AddDbContext varsayılan olarak kapsamlı bir yaşam süresiyle kaydeder DbContext .

Not

Kapsamı belirlenmiş bir hizmeti tek bir hizmetten çözümlemeyin ve bunu dolaylı olarak, örneğin geçici bir hizmet aracılığıyla yapmamaya dikkat edin. Sonraki istekler işlenirken hizmetin yanlış duruma sahip olması neden olabilir. Şu şekilde yapılır:

  • Kapsamlı veya geçici bir hizmetten tekil bir hizmeti çözümleme.
  • Başka bir kapsamlı veya geçici hizmetten kapsamı belirlenmiş bir hizmeti çözümleme.

Varsayılan olarak, geliştirme ortamında, daha uzun bir ömrü olan başka bir hizmetten bir hizmeti çözümlemek bir özel durum oluşturur. Daha fazla bilgi için bkz . Kapsam doğrulaması.

Tekil

Tek kullanımlık hizmetler şunlardan biri oluşturulur:

  • İlk kez isteniyorlar.
  • Geliştirici tarafından kapsayıcıya doğrudan bir uygulama örneği sağlanırken. Bu yaklaşıma nadiren ihtiyaç duyulduğu görülür.

Bağımlılık ekleme kapsayıcısından hizmet uygulamasının sonraki her isteği aynı örneği kullanır. Uygulama tekil davranış gerektiriyorsa, hizmet kapsayıcısının hizmetin ömrünü yönetmesine izin verin. Tekil tasarım desenini uygulamayın ve tekilden kurtulmak için kod sağlayın. Hizmetler hiçbir zaman hizmeti kapsayıcıdan çözümleyen kodla atılmamalıdır. Bir tür veya fabrika tekil olarak kayıtlıysa kapsayıcı, tekliyi otomatik olarak atılır.

ile AddSingletontekil hizmetleri kaydedin. Tekli hizmetler iş parçacığı açısından güvenli olmalıdır ve genellikle durum bilgisi olmayan hizmetlerde kullanılır.

İstekleri işleyen uygulamalarda, tekli hizmetler uygulama kapatıldığında ServiceProvider atılır. Uygulama kapatana kadar bellek serbest bırakılmadığından, tek bir hizmetle bellek kullanımını göz önünde bulundurun.

Hizmet kayıt yöntemleri

Çerçeve, belirli senaryolarda yararlı olan hizmet kaydı uzantısı yöntemleri sağlar:

Metot Otomatik
nesne
Elden çıkarma
Birden çok
uygulamalar
Geçiş args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()

Örnek:

services.AddSingleton<IMyDep, MyDep>();
Yes Evet Hayır
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})

Örnekler:

services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep(99));
Yes Evet Yes
Add{LIFETIME}<{IMPLEMENTATION}>()

Örnek:

services.AddSingleton<MyDep>();
Yes Hayı Hayır
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})

Örnekler:

services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep(99));
Hayır Evet Yes
AddSingleton(new {IMPLEMENTATION})

Örnekler:

services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));
Hayır Hayı Evet

Tür elden çıkarma hakkında daha fazla bilgi için Hizmetlerin elden çıkarılması bölümüne bakın.

Bir hizmeti yalnızca uygulama türüyle kaydetmek, bu hizmeti aynı uygulama ve hizmet türüyle kaydetmekle eşdeğerdir. Bu nedenle, bir hizmetin birden çok uygulaması, açık bir hizmet türü almayan yöntemler kullanılarak kaydedilemez. Bu yöntemler bir hizmetin birden çok örneğini kaydedebilir, ancak hepsi aynı uygulama türüne sahip olur.

Yukarıdaki hizmet kayıt yöntemlerinden herhangi biri, aynı hizmet türündeki birden çok hizmet örneğini kaydetmek için kullanılabilir. Aşağıdaki örnekte, AddSingleton hizmet türü olarak ile IMessageWriter iki kez çağrılır. İkinci çağrısı olarak IMessageWriter çözümlendiğinde öncekini AddSingleton geçersiz kılar ve birden çok hizmet aracılığıyla IEnumerable<IMessageWriter>çözümlendiğinde öncekine ekler. Hizmetler, aracılığıyla IEnumerable<{SERVICE}>çözümlendiğinde kaydedildikleri sırayla görünür.

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

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
builder.Services.AddSingleton<ExampleService>();

using IHost host = builder.Build();

_ = host.Services.GetService<ExampleService>();

await host.RunAsync();

Yukarıdaki örnek kaynak kodu, öğesinin IMessageWriteriki uygulamasını kaydeder.

using System.Diagnostics;

namespace ConsoleDI.IEnumerableExample;

public sealed class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is LoggingMessageWriter);

        var dependencyArray = messageWriters.ToArray();
        Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
        Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
    }
}

iki ExampleService oluşturucu parametresi tanımlar: tek IMessageWriterbir ve bir IEnumerable<IMessageWriter>. Tek IMessageWriter , kaydedilen son uygulamadır, ancak IEnumerable<IMessageWriter> tüm kayıtlı uygulamaları temsil eder.

Çerçeve ayrıca, yalnızca henüz kayıtlı bir uygulama yoksa hizmeti kaydeden uzantı yöntemleri sağlar TryAdd{LIFETIME} .

Aşağıdaki örnekte, çağrısı AddSingleton için IMessageWriterbir uygulama olarak kaydederConsoleMessageWriter. zaten kayıtlı bir uygulamaya sahip olduğundan IMessageWriter çağrısının TryAddSingleton hiçbir etkisi yoktur:

services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();

TryAddSingleton zaten eklendiğinden ve "deneyin" başarısız olacağı için öğesinin hiçbir etkisi yoktur. aşağıdakini ExampleService onaylar:

public class ExampleService
{
    public ExampleService(
        IMessageWriter messageWriter,
        IEnumerable<IMessageWriter> messageWriters)
    {
        Trace.Assert(messageWriter is ConsoleMessageWriter);
        Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
    }
}

Daha fazla bilgi için bkz.

TryAddEnumerable(ServiceDescriptor) yöntemleri, hizmeti yalnızca aynı türde bir uygulama yoksa kaydeder. Birden çok hizmet aracılığıyla IEnumerable<{SERVICE}>çözümlenir. Hizmetleri kaydederken, aynı türlerden biri henüz eklenmediyse bir örnek ekleyin. Kitaplık yazarları, bir uygulamanın birden çok kopyasını kapsayıcıya kaydetmekten kaçınmak için kullanır TryAddEnumerable .

Aşağıdaki örnekte, için bir uygulama IMessageWriter1olarak kaydetmeye TryAddEnumerable yönelik ilk çağrı.MessageWriter İkinci çağrı için IMessageWriter2kaydolrMessageWriter. Üçüncü çağrının hiçbir etkisi yoktur çünkü IMessageWriter1 zaten kayıtlı bir uygulaması vardır MessageWriter:

public interface IMessageWriter1 { }
public interface IMessageWriter2 { }

public class MessageWriter : IMessageWriter1, IMessageWriter2
{
}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());

Hizmet kaydı, aynı türdeki birden çok uygulamayı kaydetme dışında genellikle sipariş bağımsızdır.

IServiceCollection bir nesne koleksiyonudur ServiceDescriptor . Aşağıdaki örnekte, oluşturup ekleyerek bir hizmetin nasıl kaydedildiği gösterilmektedir ServiceDescriptor:

string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMessageWriter),
    _ => new DefaultMessageWriter(secretKey),
    ServiceLifetime.Transient);

services.Add(descriptor);

Yerleşik Add{LIFETIME} yöntemler aynı yaklaşımı kullanır. Örneğin, addScoped kaynak koduna bakın.

Oluşturucu ekleme davranışı

Hizmetler şu şekilde çözümlenebilir:

Oluşturucular bağımlılık ekleme tarafından sağlanmayan bağımsız değişkenleri kabul edebilir, ancak bağımsız değişkenlerin varsayılan değerler ataması gerekir.

Hizmetler veya ActivatorUtilitiestarafından IServiceProvider çözümlendiğinde, oluşturucu ekleme için genel bir oluşturucu gerekir.

Hizmetler tarafından ActivatorUtilitiesçözümlendiğinde, oluşturucu ekleme için yalnızca bir geçerli oluşturucu olması gerekir. Oluşturucu aşırı yüklemeleri desteklenir, ancak bağımsız değişkenleri bağımlılık ekleme ile yerine getirilebilen yalnızca bir aşırı yükleme olabilir.

Kapsam doğrulaması

Uygulama ortamda çalıştırıldığında Development ve konağı derlemek için CreateApplicationBuilder'ı çağırdığında, varsayılan hizmet sağlayıcısı aşağıdakileri doğrulamak için denetimler gerçekleştirir:

  • Kapsamı belirlenmiş hizmetler kök hizmet sağlayıcısından çözümlenmez.
  • Kapsamı belirlenmiş hizmetler tekil hizmetlere eklenmez.

Kök hizmet sağlayıcısı çağrıldığında BuildServiceProvider oluşturulur. Kök hizmet sağlayıcısının ömrü, sağlayıcı uygulamayla başladığında uygulamanın kullanım ömrüne karşılık gelir ve uygulama kapatıldığında atılır.

Kapsamı belirlenmiş hizmetler, bunları oluşturan kapsayıcı tarafından atılır. Kök kapsayıcıda kapsamlı bir hizmet oluşturulursa, hizmetin kullanım ömrü etkin bir şekilde singleton olarak yükseltilir çünkü yalnızca uygulama kapatıldığında kök kapsayıcı tarafından atılır. Hizmet kapsamlarını doğrulamak çağrıldığında BuildServiceProvider bu durumları yakalar.

Kapsam senaryoları

IServiceScopeFactory her zaman tekil olarak kaydedilir, ancak IServiceProvider öğesini içeren sınıfın ömrüne göre değişiklik gösterebilir. Örneğin, bir kapsamdaki hizmetleri çözümlerseniz ve bu hizmetlerden herhangi biri bir IServiceProvideralırsa, kapsamlı bir örnek olur.

gibi uygulamaları IHostedServiceBackgroundService içinde kapsam belirleme hizmetleri elde etmek için, oluşturucu ekleme yoluyla hizmet bağımlılıklarını eklemeyin. Bunun yerine ekleyin IServiceScopeFactory, bir kapsam oluşturun ve ardından uygun hizmet ömrünü kullanmak için kapsamdaki bağımlılıkları çözün.

namespace WorkerScope.Example;

public sealed class Worker(
    ILogger<Worker> logger,
    IServiceScopeFactory serviceScopeFactory)
    : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using (IServiceScope scope = serviceScopeFactory.CreateScope())
            {
                try
                {
                    logger.LogInformation(
                        "Starting scoped work, provider hash: {hash}.",
                        scope.ServiceProvider.GetHashCode());

                    var store = scope.ServiceProvider.GetRequiredService<IObjectStore>();
                    var next = await store.GetNextAsync();
                    logger.LogInformation("{next}", next);

                    var processor = scope.ServiceProvider.GetRequiredService<IObjectProcessor>();
                    await processor.ProcessAsync(next);
                    logger.LogInformation("Processing {name}.", next.Name);

                    var relay = scope.ServiceProvider.GetRequiredService<IObjectRelay>();
                    await relay.RelayAsync(next);
                    logger.LogInformation("Processed results have been relayed.");

                    var marked = await store.MarkAsync(next);
                    logger.LogInformation("Marked as processed: {next}", marked);
                }
                finally
                {
                    logger.LogInformation(
                        "Finished scoped work, provider hash: {hash}.{nl}",
                        scope.ServiceProvider.GetHashCode(), Environment.NewLine);
                }
            }
        }
    }
}

Yukarıdaki kodda, uygulama çalışırken arka plan hizmeti:

  • öğesine bağlıdır IServiceScopeFactory.
  • Ek hizmetleri çözümlemek için bir IServiceScope oluşturur.
  • Tüketim için kapsamı belirlenmiş hizmetleri çözümler.
  • Nesneleri işleme ve sonra bunları geçirme üzerinde çalışır ve son olarak işlenmiş olarak işaretler.

Örnek kaynak kodundan, uygulamalarının IHostedService kapsamlı hizmet ömründen nasıl yararlanabileceğini görebilirsiniz.

Anahtarlı hizmetler

.NET 8'den başlayarak, bir anahtara dayalı hizmet kayıtları ve aramalar için destek sağlanır; başka bir deyişle, farklı bir anahtara birden çok hizmet kaydetmek ve arama için bu anahtarı kullanmak mümkündür.

Örneğin, arabiriminin IMessageWriterfarklı uygulamalarına sahip olduğunuz durumu göz önünde bulundurun: MemoryMessageWriter ve QueueMessageWriter.

Bir anahtarı parametre olarak destekleyen hizmet kayıt yöntemlerinin (daha önce görüldüğü) aşırı yüklemesini kullanarak bu hizmetleri kaydedebilirsiniz:

services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");

key ile sınırlı stringdeğildir, türü doğru şekilde uyguladığı Equalssürece istediğiniz herhangi biri object olabilir.

kullanan IMessageWritersınıfının oluşturucusunda, çözümlenmesi gereken hizmetin anahtarını belirtmek için öğesini eklersiniz FromKeyedServicesAttribute :

public class ExampleService
{
    public ExampleService(
        [FromKeyedServices("queue")] IMessageWriter writer)
    {
        // Omitted for brevity...
    }
}

Ayrıca bkz.