ASP.NET Core'da bağımlılık ekleme

Note

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 10 sürümüne bakın.

Warning

ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 10 sürümüne bakın.

Kirk Larkin, Steve Smith ve Brandon Dahler tarafından

ASP.NET Core, sınıflar ve onların bağımlılıkları arasında Denetimin Tersini (IoC) sağlamak için bir teknik olarak bağımlılık ekleme (DI) yazılım tasarımı desenini destekler.

Bu makalede, ASP.NET Core web uygulamalarında DI hakkında bilgi sağlanır. Web uygulamaları dışındaki uygulamalar da dahil olmak üzere tüm uygulama türlerinde DI hakkında bilgi için bkz. .NET'te bağımlılık ekleme.

Bu makaledeki yönergeleri tamamlayan veya bunların yerine geçen yönergeler aşağıdaki makalelerde bulunur:

Bu makaledeki kod örnekleri Blazor temel alır. Sayfa örneklerini görmek Razor için bu makalenin 7.0 sürümüne bakın.

Örnek kodu görüntüleme veya indirme (indirme)

Bu makaledeki örnek kodu gösterim amacıyla yerel Blazor Web App bir kodda kullanırken etkileşimli işleme modunu benimseyin.

Bağımlılık enjeksiyonuna genel bakış

Bağımlılık, başka bir nesnenin bağımlı olduğu bir nesnedir. Aşağıdaki MyDependency sınıfı bir WriteMessage yöntemle göz önünde bulundurun:

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

Bir sınıf, MyDependency sınıfının bir örneğini oluşturabilir ve WriteMessage yöntemini çağırabilir. Aşağıdaki örnekte MyDependency sınıfı bir Razor bileşeninin bağımlılığıdır.

Pages/DependencyExample1.razor:

@page "/dependency-example-1"

<button @onclick="WriteMessage">Write message</button>

@code {
    private readonly MyDependency dependency = new MyDependency();

    private void WriteMessage() =>
        dependency.WriteMessage("DependencyExample1.WriteMessage called");
}

Bir sınıf, WriteMessage yöntemini çağırmak için MyDependency sınıfının bir örneğini oluşturabilir. Aşağıdaki örnekte, MyDependency sınıfı bir sayfa sınıfının bağımlılığıdır IndexModel .

Pages/Index.cshtml.cs:

public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

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

Tüketen sınıf oluşturur ve doğrudan MyDependency sınıfına bağlıdır. Önceki örnekte olduğu gibi doğrudan bağımlılık almak sorunludur ve aşağıdaki nedenlerden dolayı kaçınılmalıdır:

  • Farklı bir uygulamayla MyDependency öğesini değiştirmek için, tüketen sınıfın değiştirilmesi gerekir.
  • Bağımlılıkları varsa MyDependency , bunlar da kullanan sınıf tarafından yapılandırılmalıdır. bağlı olarak MyDependencybirden çok sınıfa sahip büyük bir projede yapılandırma kodu uygulamanın etrafına dağılır.
  • Uygulamanın birim testi yapmak zor.

DI bu sorunları şu şekilde giderir:

  • Bağımlılık uygulamasını soyutlama amacıyla bir arabirim veya temel sınıf kullanımı.
  • Di kapsayıcısı olarak da adlandırılan bir hizmet kapsayıcısındabağımlılığın kaydı. ASP.NET Core yerleşik bir hizmet kapsayıcısı sağlar: IServiceProvider. Hizmetler genellikle uygulamanın Program dosyasına (.NET 6 veya üzeri) veya uygulamanın Startup dosyasına (.NET 5 veya öncesi) kaydedilir.
  • Hizmetin kullanıldığı sınıflara enjekte edilmesi. Çerçeve, bağımlılıkların örneklerini oluşturur ve artık gerekli olmadığında bunları atar.

Aşağıdaki örnekte, IMyDependency arabirim yöntem imzasını WriteMessage tanımlar.

Interfaces/IMyDependency.cs:

public interface IMyDependency
{
    void WriteMessage(string message);
}

Yukarıdaki arabirim, aşağıdaki somut tür MyDependency tarafından uygulanır.

Services/MyDependency.cs:

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

Uygulama, IMyDependency hizmetini, hizmetlerin genellikle Program dosyasında (.NET 6 veya üzeri) veya Startup.ConfigureServices yönteminde (.NET 5 veya daha önceki) hizmet kapsayıcısına eklendiği belirli tür MyDependency ile kaydeder. AddScoped yöntemi, hizmeti, .NET 8 veya üzerindeki bir devre ömrüyle ya da bir MVC veya Pages uygulamasındaki tek bir isteğin süreli yaşam döngüsüyle kaydeder. Hizmet yaşam süreleri bu makalenin devamında açıklanmıştır.

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

Aşağıdaki Razor bileşeninde gösterildiği gibi, IMyDependency hizmeti istenir ve WriteMessage yöntemini çağırmak için kullanılır.

Pages/DependencyExample2.razor:

@page "/dependency-example-2"
@inject IMyDependency Dependency

<button @onclick="WriteMessage">Write message</button>

@code {
    private void WriteMessage() =>
        Dependency.WriteMessage("DependencyExample2.WriteMessage called");
}

IMyDependency hizmeti istenir ve WriteMessage yöntemini çağırmak için kullanılır, aşağıdaki sayfa modeli sınıfının gösterdiği gibi.

Pages/Index.cshtml.cs:

public class IndexModel(IMyDependency dependency) : PageModel
{
    public void OnGet()
    {
        dependency.WriteMessage("IndexModel.OnGet called");
    }
}

DI desenini kullanarak, bağımlılığı tüketen sınıf:

  • Somut tür MyDependency kullanılmaz, yalnızca uyguladığı IMyDependency arabirimi kullanılır. Bu, tüketiciyi değiştirmeden uygulamayı değiştirmeyi kolaylaştırır.
  • Doğrudan bir MyDependency örneği oluşturmaz veya bertaraf etmez. Bağımlılık, hizmet kapsayıcısı tarafından oluşturulur ve yok edilir.

Arabirim IMyDependency uygulaması, aşağıdaki örnekte bağımlılık olarak eklenen yerleşik günlük API'sini kullanarak geliştirilebilir.

Services/MyDependency.cs:

public class MyDependency(ILogger<MyDependency> logger) : IMyDependency
{
    public void WriteMessage(string message)
    {
        logger.LogInformation($"MyDependency.WriteMessage: {message}");
    }
}

MyDependency ILogger<TCategoryName>, çerçeve tarafından sağlanan bir hizmete bağlıdır.

DI'yi zincirleme bir şekilde kullanmak yaygın bir durum. İ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ı, ILogger<TCategoryName> (genel) açık türlerden yararlanarak, her (genel) oluşturulmuş türü kaydettirme gereksinimini ortadan kaldırır.

DI terminolojisinde bir hizmet:

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

IMyDependency Önceki örneklerde yer alan uygulamalar, günlüğe kaydetme uygulamak için değil, genel DI ilkelerini göstermek için yazılmıştır. Önceki örneklerde gösterildiği gibi çoğu uygulamanın günlükçü oluşturması gerekmez. Aşağıdaki kod, çerçevenin özel bir hizmetin () kaydedilmesini gerektirmeyen IMyDependency kullanılmasını doğrudan gösterir.

Pages/LoggingExample.razor:

@page "/logging-example"
@inject ILogger<LoggingExample> Logger

<button @onclick="WriteMessage">Write message</button>

@code {
    private void WriteMessage() => 
        Logger.LogInformation("LoggingExample.WriteMessage called");
}

Pages/IndexModel.cshtml.cs:

public class IndexModel(ILogger<IndexModel> logger) : PageModel
{ 
    public void OnGet()
    {
        logger.LogInformation("IndexModel.OnGet called");
    }
}

Startup içine enjekte edilen hizmetler

Hizmetler Startup oluşturucusuna ve Startup.Configure yöntemine enjekte edilebilir.

ASP.NET Core 3.0 veya sonraki sürümlerin Genel Ana Bilgisayarı (IHostBuilder), geçici bir "kök" hizmet sağlayıcısının uygulamanın ana hizmet kapsayıcısını başlatmak ve yapılandırmak için ana bilgisayar için temel hizmetleri kullanması sonrasında uygulamanın yaşam döngüsü boyunca tek bir hizmet kapsayıcısı kullanır. Ana bilgisayar başlatma işlemine dahil olmayan özel hizmetler ve çerçeve hizmetleri de dahil olmak üzere hizmetlerin çoğu, Startup yapıcısı çağrıldığında hizmet kapsayıcısında yapılandırılmamış ya da mevcut değildir. Genel Host kullanılırken oluşturucuya yalnızca aşağıdaki hizmetler enjekte edilebilir:

Genel Konak, sınıf oluşturucusunda Startup kullanılabilir hizmetleri kısıtlayarak, bir hizmeti oluşturulmadan veya kullanılabilir duruma gelmeden önce kullanmaya çalışmanızı ve geçici hizmet kapsayıcısında oluşturulan tek bir hizmetin son hizmet kapsayıcısında oluşturulan hizmetten farklı olabileceği özel veya çerçeve tekli hizmetlerinin birden çok örneğini oluşturmanızı engeller.

Hizmet kapsayıcısına kayıtlı herhangi bir hizmet, Startup.Configure yöntemine enjekte edilebilir. Aşağıdaki örnekte, bir ILogger<TCategoryName> eklenir:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    ...
}

Daha fazla bilgi için bkz. ASP.NET Core'da uygulama başlatma ve Startup sınıfında erişim yapılandırması.

Hizmet kayıt yöntemleri

Hizmet kayıtları hakkında genel yönergeler için bkz. Hizmet kaydı.

Test için mock türleri oluşturulurken birden fazla uygulamanın kullanılması yaygın bir durumdur. Daha fazla bilgi için bkz . ASP.NET Core'da tümleştirme testleri.

Bir hizmeti yalnızca uygulama türüyle kaydetmek, hizmeti aynı uygulama ve hizmet türüyle kaydetmeye eşdeğerdir:

builder.Services.AddSingleton<MyDependency>();
services.AddSingleton<MyDependency>();

Hizmet kayıt yöntemleri, 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 IMyDependency iki kez çağrılır. İkinci kez AddSingleton çözümlendiğinde önceki çağrıyı IMyDependency geçersiz kılar ve birden çok hizmet IEnumerable<IMyDependency> aracılığıyla çözümlendiğinde önceki çağrıya ekler.

builder.Services.AddSingleton<IMyDependency, MyDependency>();
builder.Services.AddSingleton<IMyDependency, DifferentDependency>();

public class MyService
{
    public MyService(IMyDependency myDependency, 
       IEnumerable<IMyDependency> myDependencies)
    {
        Trace.Assert(myDependency is DifferentDependency);

        var dependencyArray = myDependencies.ToArray();
        Trace.Assert(dependencyArray[0] is MyDependency);
        Trace.Assert(dependencyArray[1] is DifferentDependency);
    }
}
services.AddSingleton<IMyDependency, MyDependency>();
services.AddSingleton<IMyDependency, DifferentDependency>();

public class MyService
{
    public MyService(IMyDependency myDependency, 
       IEnumerable<IMyDependency> myDependencies)
    {
        Trace.Assert(myDependency is DifferentDependency);

        var dependencyArray = myDependencies.ToArray();
        Trace.Assert(dependencyArray[0] is MyDependency);
        Trace.Assert(dependencyArray[1] is DifferentDependency);
    }
}

Uzantı yöntemleriyle hizmet gruplarını kaydetme

ASP.NET Core çerçevesinde bir grup ilgili hizmeti kaydetme kuralı, bir çerçeve özelliğinin gerektirdiği tüm hizmetleri kaydetmek için, {GROUP NAME} yer tutucusu açıklayıcı bir grup adı olan tek bir Add{GROUP NAME} uzantı yönteminin kullanılmasını içerir. Örneğin, AddRazorComponents uzantı yöntemi, Razor bileşenlerin sunucu tarafında işlenmesi için gereken hizmetleri kaydeder.

Seçenekleri yapılandıran ve hizmetleri kaydeden aşağıdaki örneği göz önünde bulundurun:

builder.Services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));
builder.Services.Configure<ColorOptions>(
    builder.Configuration.GetSection(ColorOptions.Color));

builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<IMyDependency2, MyDependency2>();
services.Configure<PositionOptions>(
    builder.Configuration.GetSection(PositionOptions.Position));
services.Configure<ColorOptions>(
    builder.Configuration.GetSection(ColorOptions.Color));

services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyDependency2, MyDependency2>();

İlgili kayıt grupları, hizmetleri kaydetmek için bir uzantı yöntemine taşınabilir. Aşağıdaki örnekte:

  • uzantı yöntemi, AddConfig yapılandırma verilerini kesin olarak türü belirlenmiş C# sınıflarına bağlar ve sınıfları hizmet kapsayıcısına kaydeder.
  • AddDependencyGroup uzantı yöntemi ek sınıf (hizmet) bağımlılıkları ekler.
namespace Microsoft.Extensions.DependencyInjection;

public static class ConfigServiceCollectionExtensions
{
    public static IServiceCollection AddConfig(
        this IServiceCollection services, IConfiguration config)
    {
        services.Configure<PositionOptions>(
            config.GetSection(PositionOptions.Position));
        services.Configure<ColorOptions>(
            config.GetSection(ColorOptions.Color));

        return services;
    }

    public static IServiceCollection AddDependencyGroup(
        this IServiceCollection services)
    {
        services.AddScoped<IMyDependency, MyDependency>();
        services.AddScoped<IMyDependency2, MyDependency2>();

        return services;
    }
}

Aşağıdaki kod, hizmetleri kaydetmek için önceki AddConfig ve AddDependencyGroup uzantı yöntemlerini çağırır:

builder.Services
    .AddConfig(builder.Configuration)
    .AddDependencyGroup();
services
    .AddConfig(builder.Configuration)
    .AddDependencyGroup();

Uygulamalar için Microsoft.Extensions.DependencyInjection ad alanında uzantı yöntemleri oluşturma adlandırma kuralına uymalarını öneririz.

  • Servis kayıt gruplarını kapsar.
  • Hizmete uygun IntelliSense erişimi sağlar.

Hizmet ömrü

Hizmet ömrü hakkında genel yönergeler için bkz. Hizmet ömrü. Uygulamalar için geçerli olan ek hizmet ömrü kılavuzu için Blazor bkz. ASP.NET Core Blazor bağımlılık ekleme.

Ara yazılımda kapsamlı hizmetleri kullanmak için aşağıdaki yaklaşımlardan birini kullanın:

  • Hizmeti ara yazılımının Invoke veya InvokeAsync yöntemine ekleyin. Oluşturucu enjeksiyonu kullanılması, kapsamı belirlenmiş hizmeti tekil gibi davranmaya zorladığı için bir çalışma zamanı özel durumu fırlatır. Yaşam süresi ve kayıt seçenekleri bölümündeki örnek yaklaşımını göstermektedir.
  • Fabrika tabanlı ara yazılımı kullanın. Bu yaklaşım kullanılarak kaydedilen ara yazılım, her istemci isteği (bağlantı) başına etkinleştirilir ve bu, kapsamı belirlenmiş hizmetlerin ara yazılımın yapıcısına enjekte edilmesini sağlar.

Daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın:

Anahtarlı hizmetler

Anahtarlı hizmetler anahtarları kullanarak hizmetleri kaydeder ve alır. Bir hizmet, hizmet kaydı için aşağıdaki uzantı yöntemlerinden herhangi birini çağırarak bir anahtarla ilişkilendirilir:

Aşağıdaki örnekte aşağıdaki API ile anahtarlı hizmetler gösterilmektedir:

  • IStringCache , yöntem imzası olan Get hizmet arabirimidir.
  • StringCache1 ve StringCache2 için IStringCachesomut hizmet uygulamalarıdır.

Interfaces/IStringCache.cs:

public interface IStringCache
{
    string Get(int key);
}

Services/StringCache1.cs:

public class StringCache1 : IStringCache
{
    public string Get(int key) => $"Resolving {key} from StringCache1.";
}

Services/StringCache2.cs:

public class StringCache2 : IStringCache
{
    public string Get(int key) => $"Resolving {key} from StringCache2.";
}

özniteliğine sahip anahtarı belirterek kayıtlı bir hizmete erişin[FromKeyedServices].

Anahtarlı hizmet ve dosyadaki Program minimum API uç noktaları örneği:

builder.Services.AddKeyedSingleton<IStringCache, StringCache1>("cache1");
builder.Services.AddKeyedSingleton<IStringCache, StringCache2>("cache2");

...

app.MapGet("/cache1", ([FromKeyedServices("cache1")] IStringCache stringCache1) => 
    stringCache1.Get(1));
app.MapGet("/cache2", ([FromKeyedServices("cache2")] IStringCache stringCache2) =>
    stringCache2.Get(2));

Razor bileşeninde (Pages/KeyedServicesExample.razor), [Inject] özelliğini kullanan örnek kullanım. InjectAttribute.Key Hizmetin eklenecek anahtarını belirtmek için özelliğini kullanın:

@page "/keyed-services-example"

@Cache?.Get(3)

@code {
    [Inject(Key = "cache1")]
    public IStringCache? Cache { get; set; }
}

Anahtarlı hizmetleri Razor bileşenlerle kullanma hakkında daha fazla bilgi için bkz. ASP.NET Core Blazor bağımlılık enjeksiyonu.

Birincil oluşturucu eklemeli bir SignalR hub'da (Hubs/MyHub1.cs) örnek kullanım:

using Microsoft.AspNetCore.SignalR;

public class MyHub1([FromKeyedServices("cache2")] IStringCache cache) : Hub
{
    public void Method()
    {
        Console.WriteLine(cache.Get(4));
    }
}

Yöntem ekleme ile hub'da SignalR (Hubs/MyHub2.cs) kullanım örneği:

using Microsoft.AspNetCore.SignalR;

public class MyHub2 : Hub
{
    public void Method([FromKeyedServices("cache2")] IStringCache cache)
    {
        Console.WriteLine(cache.Get(5));
    }
}

Orta katman, hem orta katmanın oluşturucusunda hem de Invoke/InvokeAsync yönteminde anahtarlı hizmetleri destekler.

internal class MyMiddleware
{
    private readonly RequestDelegate _next;

    public MyMiddleware(RequestDelegate next,
        [FromKeyedServices("cache1")] IStringCache cache)
    {
        _next = next;

        Console.WriteLine(cache.Get(6));
    }

    public Task Invoke(HttpContext context,
        [FromKeyedServices("cache2")] IStringCache cache)
        {
            Console.WriteLine(cache.Get(7));

            return _next(context);
        } 
}

Dosyanın Program (.NET 6 veya üzeri) veya yönteminin Startup.Configure (.NET 5 veya öncesi) uygulama işleme hattında:

app.UseMiddleware<MyMiddleware>();

Ara yazılım oluşturma hakkında daha fazla bilgi için Özel ASP.NET Core ara yazılımı yazma bölümüne bkz.

Yapıcı enjeksiyon davranışı

Oluşturucu ekleme davranışı hakkında daha fazla bilgi için aşağıdaki kaynaklara bakın:

Entity Framework bağlamları

Sunucu tarafı uygulamalarda yönergeler EF Core için bkz. EF Core.

Varsayılan olarak, Web uygulaması veritabanı işlemlerinin kapsamı normalde istemci isteğine göre belirlenmiş olduğundan, Entity Framework bağlamları kapsamlı yaşam süresi kullanılarak hizmet kapsayıcısına eklenir. Farklı bir yaşam süresi kullanmak için, bir AddDbContext aşırı yükleme kullanarak yaşam süresi belirtin. Belirli bir yaşam süresine sahip hizmetler, hizmetin ömründen daha kısa olan bir veritabanı bağlamı kullanmamalıdır.

Yaşam süresi ve kayıt seçenekleri

Hizmet ömrü ile kayıt seçenekleri arasındaki farkı göstermek için, bir görevi tanımlayıcısı OperationIdolan bir işlem olarak temsil eden aşağıdaki arabirimleri göz önünde bulundurun. Bir işlemin hizmetinin kullanım ömrünün aşağıdaki arabirimler için nasıl yapılandırıldığına bağlı olarak, kapsayıcı bir sınıf tarafından istendiğinde hizmetin aynı veya farklı örneklerini sağlar.

IOperation.cs:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

Aşağıdaki Operation sınıf, önceki tüm arabirimleri uygular. Operation Oluşturucu bir GUID oluşturur ve özelliğinde OperationId son dört karakteri depolar.

Operation.cs:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
    public Operation()
    {
        OperationId = Guid.NewGuid().ToString()[^4..];
    }

    public string OperationId { get; }
}

Aşağıdaki kod, adlandırılmış yaşam sürelerine göre sınıfın Operation birden çok kaydını oluşturur.

Hizmetlerin kaydedildiği yer:

builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();

Aşağıdaki örnekte, istekler içinde ve istekler arasında nesne yaşam süreleri gösterilmektedir. Bileşen Operation ve ara yazılım her türden istekte bulunur ve her bir IOperation türü için OperationId günlüğe kaydeder.

Pages/OperationExample.razor:

@page "/operation-example"
@inject IOperationTransient TransientOperation
@inject IOperationScoped ScopedOperation
@inject IOperationSingleton SingletonOperation

<ul>
    <li>Transient: @TransientOperation.OperationId</li>
    <li>Scoped: @ScopedOperation.OperationId</li>
    <li>Singleton: @SingletonOperation.OperationId</li>
</ul>

Aşağıdaki örnekte, istekler içinde ve istekler arasında nesne yaşam süreleri gösterilmektedir. IndexModel ve ara yazılım, her IOperation türü için istekte bulunur ve her biri için OperationId kaydederler.

IndexModel.cshtml.cs:

public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationScoped _scopedOperation;
    private readonly IOperationSingleton _singletonOperation;

    public IndexModel(ILogger<IndexModel> logger,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void OnGet()
    {
        _logger.LogInformation($"Transient: {_transientOperation.OperationId}");
        _logger.LogInformation($"Scoped: {_scopedOperation.OperationId}");
        _logger.LogInformation($"Singleton: {_singletonOperation.OperationId}");
    }
}

Ara yazılım da aynı hizmetleri çözebilir ve kullanabilir. Kapsamlı ve geçici hizmetler InvokeAsync yönteminde/tanımlanmalıdır.

MyMiddleware.cs:

public class MyMiddleware(ILogger<IndexModel> logger,
    IOperationSingleton singletonOperation)
{
    public async Task InvokeAsync(HttpContext context,
        IOperationTransient transientOperation, IOperationScoped scopedOperation)
    {
        logger.LogInformation($"Transient: {transientOperation.OperationId}");
        logger.LogInformation($"Scoped: {scopedOperation.OperationId}");
        logger.LogInformation($"Singleton: {singletonOperation.OperationId}");

        await _next(context);
    }
}
public class MyMiddleware
{
    private readonly ILogger<IndexModel> _logger;
    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(ILogger<IndexModel> logger,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _singletonOperation = singletonOperation;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationTransient transientOperation, IOperationScoped scopedOperation)
    {
        _logger.LogInformation($"Transient: {transientOperation.OperationId}");
        _logger.LogInformation($"Scoped: {scopedOperation.OperationId}");
        _logger.LogInformation($"Singleton: {_singletonOperation.OperationId}");

        await _next(context);
    }
}

Dosyanın Program (.NET 6 veya üzeri) veya yönteminin Startup.Configure (.NET 5 veya öncesi) uygulama işleme hattında:

app.UseMiddleware<MyMiddleware>();

Ara yazılım oluşturma hakkında daha fazla bilgi için Özel ASP.NET Core ara yazılımı yazma bölümüne bkz.

Yukarıdaki örneklerin çıkışında şunlar gösterilir:

  • Geçici nesneler her zaman farklıdır. Geçici OperationId değeri, Razor bileşeni ve ara yazılım için farklıdır.
  • Kapsamı belirlenmiş nesneler belirli bir istek için aynıdır, ancak yeni Blazor devreler arasında farklılık gösterir.
  • Singleton nesneler her istek veya Blazor döngü için aynıdır.
  • Geçici nesneler her zaman farklıdır. OperationId Geçici değer, sayfa ve ara yazılım için farklıdır.
  • Kapsamı belirlenmiş nesneler belirli bir istek için aynıdır, ancak yeni istekler arasında farklılık gösterir.
  • Singleton nesneleri her istek için aynıdır.

Uygulama başlangıcında bir hizmeti çözme

Aşağıdaki kod, uygulama başlatıldığında sınırlı bir süre için kapsamlı bir hizmetin nasıl çözümleneceğini gösterir:

var app = builder.Build();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;
    var dependency = services.GetRequiredService<IMyDependency>();
    dependency.WriteMessage("Call services from main");
}

Kapsam doğrulaması

Kapsam doğrulaması hakkında yönergeler için aşağıdaki kaynaklara bakın:

İstek Hizmetleri

ASP.NET Core isteğindeki hizmetler ve bağımlılıkları aracılığıyla HttpContext.RequestServiceskullanıma sunulur.

Çerçeve, istek başına bir kapsam oluşturur ve RequestServices kapsamı belirlenmiş hizmet sağlayıcısını kullanıma sunar. tüm kapsamlı hizmetler, istek etkin olduğu sürece geçerlidir.

Note

RequestServices'den hizmetleri çözümlemek yerine bağımlılıkları oluşturucu parametreleri olarak istemeyi tercih edin. Oluşturucu parametreleri olarak bağımlılık istemek, test etmek daha kolay olan sınıfları verir.

Bağımlılık enjeksiyonu için hizmetler tasarlama

DI için hizmetler tasarlarken:

  • Durum bilgisi olan statik sınıflardan ve üyelerden kaçının. Bunun yerine tekil hizmetleri kullanacak uygulamalar tasarlayarak genel durum oluşturmaktan kaçının.
  • Hizmetlerdeki bağımlı sınıfları doğrudan örneklemeye çalışmaktan kaçının. Doğrudan örnekleme, kodu belirli bir uygulamayla eşler.
  • Hizmetleri küçük, iyi faktörlü ve kolayca test edilmiş hale getirin.

Bir sınıfın çok fazla eklenmiş bağımlılığı varsa, sınıfın çok fazla sorumlulukları olduğunu veTek Sorumluluk İlkesi'ni (SRP) ihlal ettiğinin bir işareti olabilir. Bazı sorumluluklarını yeni sınıflara taşıyarak sınıfı tekrar düzenlemeye çalış. Sayfa modeli sınıflarının Razor ve MVC denetleyici sınıflarının, kullanıcı arabirimi ile ilgili konulara odaklanması gerektiğini unutmayın.

Hizmetlerin sonlandırılması

Kapsayıcı, oluşturduğu türler için Dispose çağrısını yapar IDisposable. Kapalı birimden çözümlenen hizmetler geliştirici tarafından asla elden çıkarılmamalıdır. Bir tür veya fabrika tekil olarak kayıtlıysa, kapsayıcı tekliyi otomatik olarak kaldırır.

Aşağıdaki örnekte, hizmetler hizmet kapsayıcısı tarafından oluşturulur ve otomatik olarak bertaraf edilir.

Services/Service1.cs:

public class Service1 : IDisposable
{
    private bool _disposed;

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

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        Console.WriteLine("Service1.Dispose");
        _disposed = true;

        GC.SuppressFinalize(this);
    }
}

Services/Service2.cs:

public class Service2 : IDisposable
{
    private bool _disposed;

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

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        Console.WriteLine("Service2.Dispose");
        _disposed = true;

        GC.SuppressFinalize(this);
    }
}

Services/Service3.cs:

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

public class Service3(string myKey) : IService3, IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, Key = {myKey}");
    }

    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        Console.WriteLine("Service3.Dispose");
        _disposed = true;

        GC.SuppressFinalize(this);
    }
}

appsettings.Development.json'da:

"Key": "Value from appsettings.Development.json"

Hizmetlerin uygulama tarafından kaydedildiği yer:

builder.Services.AddScoped<Service1>();
builder.Services.AddSingleton<Service2>();

var key = builder.Configuration["Key"] ?? string.Empty;
builder.Services.AddSingleton<IService3>(sp => new Service3(key));
services.AddScoped<Service1>();
services.AddSingleton<Service2>();

var myKey = builder.Configuration["Key"] ?? string.Empty;
services.AddSingleton<IService3>(sp => new Service3(myKey));

Pages/DisposalExample.razor:

@page "/disposal-example"
@inject Service1 Service1
@inject Service2 Service2
@inject IService3 Service3

@code {
    protected override void OnInitialized()
    {
        Service1.Write("DisposalExample.OnInitialized");
        Service2.Write("DisposalExample.OnInitialized");
        Service3.Write("DisposalExample.OnInitialized");
    }
}

Hata ayıklama konsolu, Dizin sayfasının her yenilenmesinden sonra aşağıdaki çıkışı gösterir:

Service1: DisposalExample.OnInitialized
Service2: DisposalExample.OnInitialized
Service3: DisposalExample.OnInitialized, Key = Value from appsettings.Development.json
Service1.Dispose

Service1 öğesinin atılmasıyla ilgili girişi görmek için, DisposalExample bileşeninden uzaklaşarak atılmasını tetikleyin.

Pages/Index.cshtml.cs:

public class IndexModel(
    Service1 service1, Service2 service2, IService3 service3) 
    : PageModel
{
    public void OnGet()
    {
        service1.Write("IndexModel.OnGet");
        service2.Write("IndexModel.OnGet");
        service3.Write("IndexModel.OnGet");
    }
}

Hata ayıklama konsolu, Dizin sayfasının her yenilenmesinden sonra aşağıdaki çıkışı gösterir:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, Key = Value from appsettings.Development.json
Service1.Dispose

Hizmet kapsayıcısı tarafından oluşturulmayan hizmetler

Aşağıdaki kodu inceleyin:

builder.Services.AddSingleton(new Service1());
builder.Services.AddSingleton(new Service2());
services.AddSingleton(new Service1());
services.AddSingleton(new Service2());

Önceki kod için:

  • Hizmet örnekleri hizmet kapsayıcısı tarafından oluşturulmaz.
  • Çerçeve, hizmetleri otomatik olarak sonlandırmıyor.
  • Geliştirici, hizmetleri yok etme sorumluluğundadır.

IDisposable geçici ve paylaşılan örnekler için kılavuz

Daha fazla bilgi için bkz. .NET'te bağımlılık ekleme: Geçici ve paylaşılan örnek için IDisposable kılavuzu.

Varsayılan hizmet kapsayıcısının değiştirilmesi

Daha fazla bilgi için bkz. .NET'te bağımlılık ekleme: Varsayılan hizmet kapsayıcısı değiştirme.

Recommendations

Daha fazla bilgi için Bağımlılık ekleme yönergeleri: Öneriler bölümüne bakın.

Hizmet bulucu düzenini kullanmaktan kaçının. Örneğin, bunun yerine DI'yi kullanabileceğiniz bir hizmet örneğini almak için çağırmayın GetService :

Incorrect:

Yanlış kod

Correct:

public class MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
{
    public void MyMethod()
    {
        var option = optionsMonitor.CurrentValue.Option;

        ...
    }
}

Kaçınılması gereken başka bir hizmet bulucu varyasyonu, çalışma zamanında bağımlılıkları çözümleyen bir fabrika eklemektir. Bu uygulamaların her ikisi de Inversion of Control stratejilerini bir araya getirebilir.

HttpContext öğesine statik olarak erişmekten kaçının (örneğin, IHttpContextAccessor.HttpContext).

DI, statik/genel nesne erişim desenlerine bir alternatiftir . Statik nesne erişimiyle karıştırırsanız DI'nin avantajlarını fark edemeyebilirsiniz.

Orchard Core , ASP.NET Core üzerinde modüler, çok kiracılı uygulamalar oluşturmaya yönelik bir uygulama çerçevesidir. Bkz. Orchard Core Belgeleri için daha fazla bilgi.

CMS'ye özgü özellikleri olmadan yalnızca Orchard Core Framework kullanarak modüler ve çok kiracılı uygulamalar oluşturma örnekleri için bkz . Orchard Core örnekleri.

Çerçeve tarafından sağlanan hizmetler

Dosya Program (.NET 6 veya üzeri) veya Startup dosyası (.NET 5 veya öncesi), uygulamanın kullandığı hizmetleri kaydeder; bunlar, Entity Framework Core gibi platform özellikleri ve Razor'deki bileşenleri Blazor (.NET 8 veya üzeri) destekleyen hizmetleri içerir. Başlangıçta, barındırıcı nasıl yapılandırıldıysa çerçeve tarafından tanımlanan hizmetlere IServiceCollection sahiptir. ASP.NET Core şablonlarına dayalı uygulamalar için çerçeve 250'den fazla hizmet kaydeder.

Aşağıdaki tabloda çerçeve kayıtlı hizmetlerin küçük bir örneği açıklanmaktadır:

Hizmet türü Lifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Transient
IHostApplicationLifetime Singleton
IWebHostEnvironment Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Transient
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Http.IHttpContextFactory Transient
Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> Transient
Microsoft.Extensions.Options.IOptions<TOptions> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton

Ek kaynaklar