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

Not

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

Önemli

Bu bilgiler, ticari olarak piyasaya sürülmeden önce önemli ölçüde değiştirilebilen bir yayın öncesi ürünle ilgilidir. Burada verilen bilgilerle ilgili olarak Microsoft açık veya zımni hiçbir garanti vermez.

Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Kirk Larkin, Steve Smith ve Brandon Dahler tarafından

ASP.NET Core, sınıflar ve bağımlılıkları arasında Denetimin TersIni (IoC) elde etmeye yönelik bir teknik olan bağımlılık ekleme (DI) yazılım tasarımı desenini destekler.

MVC denetleyicilerinde bağımlılık eklemeye özgü daha fazla bilgi için bkz . ASP.NET Core'da denetleyicilere bağımlılık ekleme.

Web uygulamaları dışındaki uygulamalarda bağımlılık ekleme kullanma hakkında bilgi için bkz . .NET'te bağımlılık ekleme.

Seçeneklerin bağımlılık eklemesi hakkında daha fazla bilgi için bkz . ASP.NET Core'da seçenekler deseni.

Bu konu, ASP.NET Core'a bağımlılık ekleme hakkında bilgi sağlar. Bağımlılık eklemeyi kullanmayla ilgili birincil belgeler .NET'te bağımlılık ekleme bölümünde yer alır.

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

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

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

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

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


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

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

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

  • öğesini farklı bir uygulamayla değiştirmek MyDependency için sınıfı değiştirilmelidir IndexModel .
  • Bağımlılıkları varsa MyDependency , bunların da sınıfı tarafından yapılandırılması IndexModel gerekir. bağlı olarak MyDependencybirden ç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.

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ı. ASP.NET Core yerleşik bir hizmet kapsayıcısı sağlar: IServiceProvider. Hizmetler genellikle uygulamanın Program.cs dosyasına kaydedilir.
  • 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 uygulamadaIMyDependency, arabirim yöntemini tanımlarWriteMessage:

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

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

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

Örnek uygulama hizmeti somut türüyle MyDependencykaydederIMyDependency. yöntemi, AddScoped hizmeti kapsamlı bir yaşam süresine ve tek bir isteğin ömrüne kaydeder. Hizmet yaşam süreleri bu konunun ilerleyen bölümlerinde açıklanmıştır.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

Örnek uygulamada hizmet IMyDependency istenir ve yöntemini çağırmak WriteMessage için kullanılır:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

DI desenini, denetleyiciyi veya Razor Sayfayı kullanarak:

  • Somut türünü MyDependencykullanmaz, yalnızca IMyDependency uyguladığı arabirimi kullanır. Bu, denetleyiciyi veya Razor Sayfayı değiştirmeden uygulamayı değiştirmeyi kolaylaştırır.
  • örneğini MyDependencyoluşturmaz, DI kapsayıcısı tarafından oluşturulur.

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

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

Güncelleştirilmiş Program.cs , yeni IMyDependency uygulamayı kaydeder:

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

MyDependency2ILogger<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 terminolojisinde bir hizmet:

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

Çerçeve güçlü bir günlük sistemi sağlar. IMyDependency Ö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, hiçbir hizmetin kaydedilmesini gerektirmeyen varsayılan günlüğün kullanılmasını gösterir:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; } = string.Empty;

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Önceki kodu kullanarak, günlüğe kaydetme çerçeve tarafından sağlandığından güncelleştirmeye Program.csgerek yoktur.

Uzantı yöntemleriyle hizmet gruplarını kaydetme

ASP.NET Core çerçevesi, 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, AddControllers uzantı yöntemi MVC denetleyicileri için gereken hizmetleri kaydeder.

Aşağıdaki kod, sayfalar şablonu tarafından Razor tek tek kullanıcı hesapları kullanılarak oluşturulur ve ve uzantı yöntemleri AddDbContextAddDefaultIdentitykullanılarak kapsayıcıya nasıl ek hizmetler ekleneceğini gösterir:

using DependencyInjectionSample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

Hizmetleri kaydeden ve seçenekleri yapılandıran aşağıdakileri göz önünde bulundurun:

using ConfigSample.Options;
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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>();

var app = builder.Build();

İlgili kayıt grupları, hizmetleri kaydetmek için bir uzantı yöntemine taşınabilir. Örneğin, yapılandırma hizmetleri aşağıdaki sınıfa eklenir:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        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 AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

Kalan hizmetler benzer bir sınıfta kayıtlıdır. Aşağıdaki kod, hizmetleri kaydetmek için yeni genişletme yöntemlerini kullanır:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddRazorPages();

var app = builder.Build();

Not: Her services.Add{GROUP_NAME} genişletme yöntemi hizmetleri ekler ve potansiyel olarak yapılandırır. Örneğin, AddControllersWithViews görünümleri olan MVC denetleyicilerinin gerektirdiği hizmetleri ekler ve AddRazorPages, Razor Sayfalarının gerektirdiği hizmetleri ekler.

Hizmet ömrü

Bkz. .NET'te bağımlılık eklemede hizmet ömrü

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 eklemenin kullanılması, kapsamı belirlenmiş hizmeti tekil gibi davranmaya zorladığı için bir çalışma zamanı özel durumu oluşturur. Yaşam süresi ve kayıt seçenekleri bölümündeki örnek yaklaşımı gösterirInvokeAsync.
  • Fabrika tabanlı ara yazılımı kullanın. Bu yaklaşım kullanılarak kaydedilen ara yazılım, kapsamı belirlenmiş hizmetlerin ara yazılımı oluşturucusuna eklenip eklenmemesini sağlayan istemci isteği (bağlantı) başına etkinleştirilir.

Daha fazla bilgi için bkz . Özel ASP.NET Core ara yazılımı yazma.

Hizmet kayıt yöntemleri

Bkz. .NET'te bağımlılık eklemede hizmet kayıt yöntemleri

Test için sahte türler kullanılırken birden çok uygulama kullanılması yaygın bir durumdır.

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 IMyDependency iki kez çağrılır. İkinci çağrısı olarak IMyDependency çözümlendiğinde öncekini AddSingleton geçersiz kılar ve birden çok hizmet aracılığıyla IEnumerable<IMyDependency>çözümlendiğinde öncekine ekler. Hizmetler, aracılığıyla IEnumerable<{SERVICE}>çözümlendiğinde kaydedildikleri sırayla görünür.

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);
    }
}

Anahtarlı hizmetler

Anahtarlı hizmetler , anahtarları kullanarak Bağımlılık Ekleme (DI) hizmetlerini kaydetmeye ve almaya yönelik bir mekanizmayı ifade eder. Hizmet, kaydetmek için (veya AddKeyedScopedAddKeyedTransient) çağrılarak AddKeyedSingleton bir anahtarla ilişkilendirilir. özniteliğine sahip anahtarı belirterek kayıtlı bir hizmete erişin [FromKeyedServices] . Aşağıdaki kodda anahtarlı hizmetlerin nasıl kullanılacağı gösterilmektedir:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddControllers();

var app = builder.Build();

app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));
app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) =>
                                                               smallCache.Get("date"));

app.MapControllers();

app.Run();

public interface ICache
{
    object Get(string key);
}
public class BigCache : ICache
{
    public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache
{
    public object Get(string key) => $"Resolving {key} from small cache.";
}

[ApiController]
[Route("/cache")]
public class CustomServicesApiController : Controller
{
    [HttpGet("big-cache")]
    public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache)
    {
        return cache.Get("data-mvc");
    }
}

public class MyHub : Hub
{
    public void Method([FromKeyedServices("small")] ICache cache)
    {
        Console.WriteLine(cache.Get("signalr"));
    }
}

Oluşturucu ekleme davranışı

Bkz. .NET'te bağımlılık eklemede oluşturucu ekleme davranışı

Entity Framework bağlamları

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:

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. Oluşturucu Operation bir GUID oluşturur ve özelliğinde OperationId son 4 karakteri depolar:

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:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseMyMiddleware();
app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Örnek uygulama, istekler içinde ve istekleri arasında nesne yaşam süreleri gösterir. IndexModel ve ara yazılım her tür türü için istekte bulunup IOperation her biri için günlüğe kaydederOperationId:

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

    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);
    }
}

benzer şekilde IndexModelara yazılım da aynı hizmetleri çözümler:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationSingleton _singletonOperation;

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

    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 static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

Kapsamı belirlenmiş ve geçici hizmetler yönteminde InvokeAsync çözümlenmelidir:

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);
}

Günlükçü çıkışı şunları gösterir:

  • Geçici nesneler her zaman farklıdır. Geçici OperationId değer, ara yazılımda IndexModel ve içinde farklıdır.
  • Kapsamı belirlenmiş nesneler belirli bir istek için aynıdır, ancak her yeni istekte farklılık gösterir.
  • Singleton nesneleri her istek için aynıdır.

Günlük çıkışını azaltmak için dosyada appsettings.Development.json "Logging:LogLevel:Microsoft:Error" değerini ayarlayın:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Uygulama başlatılırken 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 builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;

    var myDependency = services.GetRequiredService<IMyDependency>();
    myDependency.WriteMessage("Call services from main");
}

app.MapGet("/", () => "Hello World!");

app.Run();

Kapsam doğrulaması

Bkz. .NET'te bağımlılık eklemede oluşturucu ekleme davranışı

Daha fazla bilgi için bkz . Kapsam doğrulaması.

İ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.

Not

'den RequestServiceshizmetleri çözümlemek yerine oluşturucu parametreleri olarak bağımlılık 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 ekleme için hizmetler tasarlama

Bağımlılık ekleme 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.
  • Hizmetler içindeki bağımlı sınıfların doğrudan örneğini oluşturmaktan 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, bu, sınıfın çok fazla sorumlulukları olduğunu ve Tek Sorumluluk İlkesi'ni (SRP) ihlal ettiğinin bir işareti olabilir. Bazı sorumluluklarını yeni sınıflara taşıyarak sınıfını yeniden düzenlemeyi deneme. Sayfalar sayfa modeli sınıflarının Razor ve MVC denetleyici sınıflarının kullanıcı arabirimi endişelerine odaklanması gerektiğini unutmayın.

Hizmetlerin elden çıkarılması

Kapsayıcı, oluşturduğu türleri çağırır DisposeIDisposable . Kapsayıcıdan çözümlenen hizmetler hiçbir zaman geliştirici tarafından atılmamalıdır. Bir tür veya fabrika tekil olarak kayıtlıysa kapsayıcı, tekliyi otomatik olarak atılır.

Aşağıdaki örnekte, hizmetler hizmet kapsayıcısı tarafından oluşturulur ve otomatik olarak atılır: dependency-injection\samples\6.x\DIsample2\DIsample2\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;
    }
}

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;
    }
}

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

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

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

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

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
using DIsample2.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var myKey = builder.Configuration["MyKey"];
builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));

var app = builder.Build();
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    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, MyKey = MyKey from appsettings.Developement.json
Service1.Dispose

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

Aşağıdaki kodu inceleyin:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

Önceki kodda:

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

Geçici ve paylaşılan örnekler için IDisposable kılavuzu

.NET'te bağımlılık ekleme bölümünde Geçici ve paylaşılan örnek için IDisposable kılavuzuna bakın

Varsayılan hizmet kapsayıcısı değiştirme

Bkz. .NET'te bağımlılık eklemede varsayılan hizmet kapsayıcısı değiştirme

Öneriler

.NET'te bağımlılık ekleme ile ilgili Öneriler 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 :

    Yanlış:

    Yanlış kod

    Doğru:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = 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.

  • öğesine HttpContext statik erişimden 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. Daha fazla bilgi için Bkz . Orchard Core Belgeleri.

CMS'ye özgü özelliklerden herhangi biri olmadan yalnızca Orchard Core Framework kullanarak modüler ve çok kiracılı uygulamalar oluşturma örnekleri için Orchard Core örneklerine bakın.

Çerçeve tarafından sağlanan hizmetler

Program.cs Entity Framework Core ve ASP.NET Core MVC gibi platform özellikleri dahil olmak üzere uygulamanın kullandığı hizmetleri kaydeder. Başlangıçta, IServiceCollection sağlananProgram.cs, konağın nasıl yapılandırıldığına bağlı olarak çerçeve tarafından tanımlanan hizmetlere sahiptir. ASP.NET Core şablonlarına dayalı uygulamalar için çerçeve 250'den fazla hizmet kaydeder.

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

Hizmet Türü Yaşam süresi
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Geçici
IHostApplicationLifetime Tekil
IWebHostEnvironment Tekil
Microsoft.AspNetCore.Hosting.IStartup Tekil
Microsoft.AspNetCore.Hosting.IStartupFilter Geçici
Microsoft.AspNetCore.Hosting.Server.IServer Tekil
Microsoft.AspNetCore.Http.IHttpContextFactory Geçici
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.DiagnosticSource Tekil
System.Diagnostics.DiagnosticListener Tekil

Ek kaynaklar

Kirk Larkin, Steve Smith ve Brandon Dahler tarafından

ASP.NET Core, sınıflar ve bağımlılıkları arasında Denetimin TersIni (IoC) elde etmeye yönelik bir teknik olan bağımlılık ekleme (DI) yazılım tasarımı desenini destekler.

MVC denetleyicilerinde bağımlılık eklemeye özgü daha fazla bilgi için bkz . ASP.NET Core'da denetleyicilere bağımlılık ekleme.

Web uygulamaları dışındaki uygulamalarda bağımlılık ekleme kullanma hakkında bilgi için bkz . .NET'te bağımlılık ekleme.

Seçeneklerin bağımlılık eklemesi hakkında daha fazla bilgi için bkz . ASP.NET Core'da seçenekler deseni.

Bu konu, ASP.NET Core'a bağımlılık ekleme hakkında bilgi sağlar. Bağımlılık eklemeyi kullanmayla ilgili birincil belgeler .NET'te bağımlılık ekleme bölümünde yer alır.

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

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

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

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

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


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

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

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

  • öğesini farklı bir uygulamayla değiştirmek MyDependency için sınıfı değiştirilmelidir IndexModel .
  • Bağımlılıkları varsa MyDependency , bunların da sınıfı tarafından yapılandırılması IndexModel gerekir. bağlı olarak MyDependencybirden ç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.

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ı. ASP.NET Core yerleşik bir hizmet kapsayıcısı sağlar: IServiceProvider. Hizmetler genellikle uygulamanın Program.cs dosyasına kaydedilir.
  • 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 uygulamadaIMyDependency, arabirim yöntemini tanımlarWriteMessage:

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

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

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

Örnek uygulama hizmeti somut türüyle MyDependencykaydederIMyDependency. yöntemi, AddScoped hizmeti kapsamlı bir yaşam süresine ve tek bir isteğin ömrüne kaydeder. Hizmet yaşam süreleri bu konunun ilerleyen bölümlerinde açıklanmıştır.

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

Örnek uygulamada hizmet IMyDependency istenir ve yöntemini çağırmak WriteMessage için kullanılır:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

DI desenini, denetleyiciyi veya Razor Sayfayı kullanarak:

  • Somut türünü MyDependencykullanmaz, yalnızca IMyDependency uyguladığı arabirimi kullanır. Bu, denetleyiciyi veya Razor Sayfayı değiştirmeden uygulamayı değiştirmeyi kolaylaştırır.
  • örneğini MyDependencyoluşturmaz, DI kapsayıcısı tarafından oluşturulur.

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

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

Güncelleştirilmiş Program.cs , yeni IMyDependency uygulamayı kaydeder:

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

MyDependency2ILogger<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 terminolojisinde bir hizmet:

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

Çerçeve güçlü bir günlük sistemi sağlar. IMyDependency Ö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, hiçbir hizmetin kaydedilmesini gerektirmeyen varsayılan günlüğün kullanılmasını gösterir:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; } = string.Empty;

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Önceki kodu kullanarak, günlüğe kaydetme çerçeve tarafından sağlandığından güncelleştirmeye Program.csgerek yoktur.

Uzantı yöntemleriyle hizmet gruplarını kaydetme

ASP.NET Core çerçevesi, 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, AddControllers uzantı yöntemi MVC denetleyicileri için gereken hizmetleri kaydeder.

Aşağıdaki kod, sayfalar şablonu tarafından Razor tek tek kullanıcı hesapları kullanılarak oluşturulur ve ve uzantı yöntemleri AddDbContextAddDefaultIdentitykullanılarak kapsayıcıya nasıl ek hizmetler ekleneceğini gösterir:

using DependencyInjectionSample.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

var app = builder.Build();

Hizmetleri kaydeden ve seçenekleri yapılandıran aşağıdakileri göz önünde bulundurun:

using ConfigSample.Options;
using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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>();

var app = builder.Build();

İlgili kayıt grupları, hizmetleri kaydetmek için bir uzantı yöntemine taşınabilir. Örneğin, yapılandırma hizmetleri aşağıdaki sınıfa eklenir:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        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 AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

Kalan hizmetler benzer bir sınıfta kayıtlıdır. Aşağıdaki kod, hizmetleri kaydetmek için yeni genişletme yöntemlerini kullanır:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddRazorPages();

var app = builder.Build();

Not: Her services.Add{GROUP_NAME} genişletme yöntemi hizmetleri ekler ve potansiyel olarak yapılandırır. Örneğin, AddControllersWithViews görünümleri olan MVC denetleyicilerinin gerektirdiği hizmetleri ekler ve AddRazorPages, Razor Sayfalarının gerektirdiği hizmetleri ekler.

Hizmet ömrü

Bkz. .NET'te bağımlılık eklemede hizmet ömrü

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 eklemenin kullanılması, kapsamı belirlenmiş hizmeti tekil gibi davranmaya zorladığı için bir çalışma zamanı özel durumu oluşturur. Yaşam süresi ve kayıt seçenekleri bölümündeki örnek yaklaşımı gösterirInvokeAsync.
  • Fabrika tabanlı ara yazılımı kullanın. Bu yaklaşım kullanılarak kaydedilen ara yazılım, kapsamı belirlenmiş hizmetlerin ara yazılımı oluşturucusuna eklenip eklenmemesini sağlayan istemci isteği (bağlantı) başına etkinleştirilir.

Daha fazla bilgi için bkz . Özel ASP.NET Core ara yazılımı yazma.

Hizmet kayıt yöntemleri

Bkz. .NET'te bağımlılık eklemede hizmet kayıt yöntemleri

Test için sahte türler kullanılırken birden çok uygulama kullanılması yaygın bir durumdır.

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 IMyDependency iki kez çağrılır. İkinci çağrısı olarak IMyDependency çözümlendiğinde öncekini AddSingleton geçersiz kılar ve birden çok hizmet aracılığıyla IEnumerable<IMyDependency>çözümlendiğinde öncekine ekler. Hizmetler, aracılığıyla IEnumerable<{SERVICE}>çözümlendiğinde kaydedildikleri sırayla görünür.

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);
    }
}

Oluşturucu ekleme davranışı

Bkz. .NET'te bağımlılık eklemede oluşturucu ekleme davranışı

Entity Framework bağlamları

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:

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. Oluşturucu Operation bir GUID oluşturur ve özelliğinde OperationId son 4 karakteri depolar:

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:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseMyMiddleware();
app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Örnek uygulama, istekler içinde ve istekleri arasında nesne yaşam süreleri gösterir. IndexModel ve ara yazılım her tür türü için istekte bulunup IOperation her biri için günlüğe kaydederOperationId:

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

    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);
    }
}

benzer şekilde IndexModelara yazılım da aynı hizmetleri çözümler:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationSingleton _singletonOperation;

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

    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 static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

Kapsamı belirlenmiş ve geçici hizmetler yönteminde InvokeAsync çözümlenmelidir:

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);
}

Günlükçü çıkışı şunları gösterir:

  • Geçici nesneler her zaman farklıdır. Geçici OperationId değer, ara yazılımda IndexModel ve içinde farklıdır.
  • Kapsamı belirlenmiş nesneler belirli bir istek için aynıdır, ancak her yeni istekte farklılık gösterir.
  • Singleton nesneleri her istek için aynıdır.

Günlük çıkışını azaltmak için dosyada appsettings.Development.json "Logging:LogLevel:Microsoft:Error" değerini ayarlayın:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Uygulama başlatılırken 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 builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;

    var myDependency = services.GetRequiredService<IMyDependency>();
    myDependency.WriteMessage("Call services from main");
}

app.MapGet("/", () => "Hello World!");

app.Run();

Kapsam doğrulaması

Bkz. .NET'te bağımlılık eklemede oluşturucu ekleme davranışı

Daha fazla bilgi için bkz . Kapsam doğrulaması.

İ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.

Not

'den RequestServiceshizmetleri çözümlemek yerine oluşturucu parametreleri olarak bağımlılık 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 ekleme için hizmetler tasarlama

Bağımlılık ekleme 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.
  • Hizmetler içindeki bağımlı sınıfların doğrudan örneğini oluşturmaktan 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, bu, sınıfın çok fazla sorumlulukları olduğunu ve Tek Sorumluluk İlkesi'ni (SRP) ihlal ettiğinin bir işareti olabilir. Bazı sorumluluklarını yeni sınıflara taşıyarak sınıfını yeniden düzenlemeyi deneme. Sayfalar sayfa modeli sınıflarının Razor ve MVC denetleyici sınıflarının kullanıcı arabirimi endişelerine odaklanması gerektiğini unutmayın.

Hizmetlerin elden çıkarılması

Kapsayıcı, oluşturduğu türleri çağırır DisposeIDisposable . Kapsayıcıdan çözümlenen hizmetler hiçbir zaman geliştirici tarafından atılmamalıdır. Bir tür veya fabrika tekil olarak kayıtlıysa kapsayıcı, tekliyi otomatik olarak atılır.

Aşağıdaki örnekte, hizmetler hizmet kapsayıcısı tarafından oluşturulur ve otomatik olarak atılır: dependency-injection\samples\6.x\DIsample2\DIsample2\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;
    }
}

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;
    }
}

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

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

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

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

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
using DIsample2.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

var myKey = builder.Configuration["MyKey"];
builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));

var app = builder.Build();
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    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, MyKey = MyKey from appsettings.Developement.json
Service1.Dispose

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

Aşağıdaki kodu inceleyin:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

Önceki kodda:

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

Geçici ve paylaşılan örnekler için IDisposable kılavuzu

.NET'te bağımlılık ekleme bölümünde Geçici ve paylaşılan örnek için IDisposable kılavuzuna bakın

Varsayılan hizmet kapsayıcısı değiştirme

Bkz. .NET'te bağımlılık eklemede varsayılan hizmet kapsayıcısı değiştirme

Öneriler

.NET'te bağımlılık ekleme ile ilgili Öneriler 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 :

    Yanlış:

    Yanlış kod

    Doğru:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = 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.

  • öğesine HttpContext statik erişimden 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. Daha fazla bilgi için Bkz . Orchard Core Belgeleri.

CMS'ye özgü özelliklerden herhangi biri olmadan yalnızca Orchard Core Framework kullanarak modüler ve çok kiracılı uygulamalar oluşturma örnekleri için Orchard Core örneklerine bakın.

Çerçeve tarafından sağlanan hizmetler

Program.cs Entity Framework Core ve ASP.NET Core MVC gibi platform özellikleri dahil olmak üzere uygulamanın kullandığı hizmetleri kaydeder. Başlangıçta, IServiceCollection sağlananProgram.cs, konağın nasıl yapılandırıldığına bağlı olarak çerçeve tarafından tanımlanan hizmetlere sahiptir. ASP.NET Core şablonlarına dayalı uygulamalar için çerçeve 250'den fazla hizmet kaydeder.

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

Hizmet Türü Yaşam süresi
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Geçici
IHostApplicationLifetime Tekil
IWebHostEnvironment Tekil
Microsoft.AspNetCore.Hosting.IStartup Tekil
Microsoft.AspNetCore.Hosting.IStartupFilter Geçici
Microsoft.AspNetCore.Hosting.Server.IServer Tekil
Microsoft.AspNetCore.Http.IHttpContextFactory Geçici
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.DiagnosticSource Tekil
System.Diagnostics.DiagnosticListener Tekil

Ek kaynaklar

Kirk Larkin, Steve Smith, Scott Addie ve Brandon Dahler tarafından

ASP.NET Core, sınıflar ve bağımlılıkları arasında Denetimin TersIni (IoC) elde etmeye yönelik bir teknik olan bağımlılık ekleme (DI) yazılım tasarımı desenini destekler.

MVC denetleyicilerinde bağımlılık eklemeye özgü daha fazla bilgi için bkz . ASP.NET Core'da denetleyicilere bağımlılık ekleme.

Web uygulamaları dışındaki uygulamalarda bağımlılık ekleme kullanma hakkında bilgi için bkz . .NET'te bağımlılık ekleme.

Seçeneklerin bağımlılık eklemesi hakkında daha fazla bilgi için bkz . ASP.NET Core'da seçenekler deseni.

Bu konu, ASP.NET Core'a bağımlılık ekleme hakkında bilgi sağlar. Bağımlılık eklemeyi kullanmayla ilgili birincil belgeler .NET'te bağımlılık ekleme bölümünde yer alır.

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

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

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

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

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

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

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

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

  • öğesini farklı bir uygulamayla değiştirmek MyDependency için sınıfı değiştirilmelidir IndexModel .
  • Bağımlılıkları varsa MyDependency , bunların da sınıfı tarafından yapılandırılması IndexModel gerekir. bağlı olarak MyDependencybirden ç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 MyDependency 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ı. ASP.NET Core yerleşik bir hizmet kapsayıcısı sağlar: IServiceProvider. Hizmetler genellikle uygulamanın Startup.ConfigureServices yöntemine kaydedilir.
  • 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 uygulamadaIMyDependency, arabirim yöntemini tanımlarWriteMessage:

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

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

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

Örnek uygulama hizmeti somut türüyle MyDependencykaydederIMyDependency. yöntemi, AddScoped hizmeti kapsamlı bir yaşam süresine ve tek bir isteğin ömrüne kaydeder. Hizmet yaşam süreleri bu konunun ilerleyen bölümlerinde açıklanmıştır.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();

    services.AddRazorPages();
}

Örnek uygulamada hizmet IMyDependency istenir ve yöntemini çağırmak WriteMessage için kullanılır:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

DI desenini kullanarak denetleyici:

  • Somut türünü MyDependencykullanmaz, yalnızca IMyDependency uyguladığı arabirimi kullanır. Bu, denetleyiciyi değiştirmeden denetleyicinin kullandığı uygulamayı değiştirmeyi kolaylaştırır.
  • örneğini MyDependencyoluşturmaz, DI kapsayıcısı tarafından oluşturulur.

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

public class MyDependency2 : IMyDependency
{
    private readonly ILogger<MyDependency2> _logger;

    public MyDependency2(ILogger<MyDependency2> logger)
    {
        _logger = logger;
    }

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

Güncelleştirilmiş ConfigureServices yöntem yeni IMyDependency uygulamayı kaydeder:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency2>();

    services.AddRazorPages();
}

MyDependency2ILogger<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 terminolojisinde bir hizmet:

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

Çerçeve güçlü bir günlük sistemi sağlar. IMyDependency Ö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, içinde herhangi bir hizmetin kaydedilmesini ConfigureServicesgerektirmeyen varsayılan günlüğün kullanılmasını gösterir:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; }

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Önceki kodu kullanarak, günlüğe kaydetme çerçeve tarafından sağlandığından güncelleştirmeye ConfigureServicesgerek yoktur.

Başlangıç'a eklenen hizmetler

Hizmetler oluşturucuya Startup ve yöntemine Startup.Configure eklenebilir.

Genel Ana Bilgisayar (IHostBuilder) kullanılırken oluşturucuya Startup yalnızca aşağıdaki hizmetler eklenebilir:

DI kapsayıcısına kayıtlı herhangi bir hizmet yöntemine Startup.Configure eklenebilir:

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

Daha fazla bilgi için bkz . ASP.NET Core'da uygulama başlatma ve Başlangıç'ta Access yapılandırması.

Uzantı yöntemleriyle hizmet gruplarını kaydetme

ASP.NET Core çerçevesi, 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, AddControllers uzantı yöntemi MVC denetleyicileri için gereken hizmetleri kaydeder.

Aşağıdaki kod, sayfalar şablonu tarafından Razor tek tek kullanıcı hesapları kullanılarak oluşturulur ve ve uzantı yöntemleri AddDbContextAddDefaultIdentitykullanılarak kapsayıcıya nasıl ek hizmetler ekleneceğini gösterir:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

Hizmetleri kaydeden ve seçenekleri yapılandıran aşağıdaki ConfigureServices yöntemini göz önünde bulundurun:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
        Configuration.GetSection(PositionOptions.Position));
    services.Configure<ColorOptions>(
        Configuration.GetSection(ColorOptions.Color));

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

    services.AddRazorPages();
}

İlgili kayıt grupları, hizmetleri kaydetmek için bir uzantı yöntemine taşınabilir. Örneğin, yapılandırma hizmetleri aşağıdaki sınıfa eklenir:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        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 AddMyDependencyGroup(
             this IServiceCollection services)
        {
            services.AddScoped<IMyDependency, MyDependency>();
            services.AddScoped<IMyDependency2, MyDependency2>();

            return services;
        }
    }
}

Kalan hizmetler benzer bir sınıfta kayıtlıdır. Aşağıdaki ConfigureServices yöntemi, hizmetleri kaydetmek için yeni genişletme yöntemlerini kullanır:

public void ConfigureServices(IServiceCollection services)
{
    services.AddConfig(Configuration)
            .AddMyDependencyGroup();

    services.AddRazorPages();
}

Not: Her services.Add{GROUP_NAME} genişletme yöntemi hizmetleri ekler ve potansiyel olarak yapılandırır. Örneğin, AddControllersWithViews görünümleri olan MVC denetleyicilerinin gerektirdiği hizmetleri ekler ve AddRazorPages, Razor Sayfalarının gerektirdiği hizmetleri ekler. Uygulamaların, Microsoft.Extensions.DependencyInjection ad alanında genişletme yöntemleri oluşturma adlandırma kuralına uymasını öneriyoruz. Microsoft.Extensions.DependencyInjection ad alanında genişletme yöntemleri oluşturma:

  • Hizmet kaydı gruplarını kapsar.
  • Hizmete uygun IntelliSense erişimi sağlar.

Hizmet ömrü

Bkz. .NET'te bağımlılık eklemede hizmet ömrü

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 eklemenin kullanılması, kapsamı belirlenmiş hizmeti tekil gibi davranmaya zorladığı için bir çalışma zamanı özel durumu oluşturur. Yaşam süresi ve kayıt seçenekleri bölümündeki örnek yaklaşımı gösterirInvokeAsync.
  • Fabrika tabanlı ara yazılımı kullanın. Bu yaklaşım kullanılarak kaydedilen ara yazılım, istemci isteği (bağlantı) başına etkinleştirilir ve bu da kapsamı belirlenmiş hizmetlerin ara yazılımının InvokeAsync yöntemine eklemesine olanak tanır.

Daha fazla bilgi için bkz . Özel ASP.NET Core ara yazılımı yazma.

Hizmet kayıt yöntemleri

Bkz. .NET'te bağımlılık eklemede hizmet kayıt yöntemleri

Test için sahte türler kullanılırken birden çok uygulama kullanılması yaygın bir durumdır.

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 IMyDependency iki kez çağrılır. İkinci çağrısı olarak IMyDependency çözümlendiğinde öncekini AddSingleton geçersiz kılar ve birden çok hizmet aracılığıyla IEnumerable<IMyDependency>çözümlendiğinde öncekine ekler. Hizmetler, aracılığıyla IEnumerable<{SERVICE}>çözümlendiğinde kaydedildikleri sırayla görünür.

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);
    }
}

Oluşturucu ekleme davranışı

Bkz. .NET'te bağımlılık eklemede oluşturucu ekleme davranışı

Entity Framework bağlamları

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:

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. Oluşturucu Operation bir GUID oluşturur ve özelliğinde OperationId son 4 karakteri depolar:

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

    public string OperationId { get; }
}

yöntemi, Startup.ConfigureServices adlandırılmış yaşam sürelerine Operation göre sınıfın birden çok kaydını oluşturur:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();

    services.AddRazorPages();
}

Örnek uygulama, istekler içinde ve istekleri arasında nesne yaşam süreleri gösterir. IndexModel ve ara yazılım her tür türü için istekte bulunup IOperation her biri için günlüğe kaydederOperationId:

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

    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);
    }
}

benzer şekilde IndexModelara yazılım da aynı hizmetleri çözümler:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationTransient transientOperation,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _singletonOperation = singletonOperation;
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context,
        IOperationScoped scopedOperation)
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

Kapsamı belirlenmiş hizmetler yönteminde InvokeAsync çözümlenmelidir:

public async Task InvokeAsync(HttpContext context,
    IOperationScoped scopedOperation)
{
    _logger.LogInformation("Transient: " + _transientOperation.OperationId);
    _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);
    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);

    await _next(context);
}

Günlükçü çıkışı şunları gösterir:

  • Geçici nesneler her zaman farklıdır. Geçici OperationId değer, ara yazılımda IndexModel ve içinde farklıdır.
  • Kapsamı belirlenmiş nesneler belirli bir istek için aynıdır, ancak her yeni istekte farklılık gösterir.
  • Singleton nesneleri her istek için aynıdır.

Günlük çıkışını azaltmak için dosyada appsettings.Development.json "Logging:LogLevel:Microsoft:Error" değerini ayarlayın:

{
  "MyKey": "MyKey from appsettings.Developement.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "System": "Debug",
      "Microsoft": "Error"
    }
  }
}

Hizmetleri ana bilgisayardan arama

IServiceScope Uygulamanın kapsamındaki kapsamlı bir hizmeti çözümlemek için IServiceScopeFactory.CreateScope ile bir oluşturun. Bu yaklaşım, başlatma görevlerini çalıştırmak için başlangıçta kapsamlı bir hizmete erişmek için kullanışlıdır.

Aşağıdaki örnekte kapsamındaki IMyDependency hizmete erişme ve içinde yöntemini Program.Mainçağırma gösterilmektedirWriteMessage:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var myDependency = services.GetRequiredService<IMyDependency>();
                myDependency.WriteMessage("Call services from main");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Kapsam doğrulaması

Bkz. .NET'te bağımlılık eklemede oluşturucu ekleme davranışı

Daha fazla bilgi için bkz . Kapsam doğrulaması.

İ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.

Not

'den RequestServiceshizmetleri çözümlemek yerine oluşturucu parametreleri olarak bağımlılık 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 ekleme için hizmetler tasarlama

Bağımlılık ekleme 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.
  • Hizmetler içindeki bağımlı sınıfların doğrudan örneğini oluşturmaktan 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, bu, sınıfın çok fazla sorumlulukları olduğunu ve Tek Sorumluluk İlkesi'ni (SRP) ihlal ettiğinin bir işareti olabilir. Bazı sorumluluklarını yeni sınıflara taşıyarak sınıfını yeniden düzenlemeyi deneme. Sayfalar sayfa modeli sınıflarının Razor ve MVC denetleyici sınıflarının kullanıcı arabirimi endişelerine odaklanması gerektiğini unutmayın.

Hizmetlerin elden çıkarılması

Kapsayıcı, oluşturduğu türleri çağırır DisposeIDisposable . Kapsayıcıdan çözümlenen hizmetler hiçbir zaman geliştirici tarafından atılmamalıdır. Bir tür veya fabrika tekil olarak kayıtlıysa kapsayıcı, tekliyi otomatik olarak atılır.

Aşağıdaki örnekte, hizmetler hizmet kapsayıcısı tarafından oluşturulur ve otomatik olarak atılır:

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;
    }
}

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;
    }
}

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

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

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

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

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    
    var myKey = Configuration["MyKey"];
    services.AddSingleton<IService3>(sp => new Service3(myKey));

    services.AddRazorPages();
}
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    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, MyKey = My Key from config
Service1.Dispose

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

Aşağıdaki kodu inceleyin:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(new Service1());
    services.AddSingleton(new Service2());

    services.AddRazorPages();
}

Önceki kodda:

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

Geçici ve paylaşılan örnekler için IDisposable kılavuzu

.NET'te bağımlılık ekleme bölümünde Geçici ve paylaşılan örnek için IDisposable kılavuzuna bakın

Varsayılan hizmet kapsayıcısı değiştirme

Bkz. .NET'te bağımlılık eklemede varsayılan hizmet kapsayıcısı değiştirme

Öneriler

.NET'te bağımlılık ekleme ile ilgili Öneriler 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 :

    Yanlış:

    Yanlış kod

    Doğru:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = 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.

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

  • içinde ConfigureServicesçağrısı yapmaktan BuildServiceProvider kaçının. Arama BuildServiceProvider genellikle geliştirici içindeki ConfigureServicesbir hizmeti çözümlemek istediğinde gerçekleşir. Örneğin, öğesinin yapılandırmadan yüklendiği LoginPath durumu göz önünde bulundurun. Aşağıdaki yaklaşımdan kaçının:

    BuildServiceProvider hatalı kod çağrısı

    Yukarıdaki resimde altında yeşil dalgalı çizgi services.BuildServiceProvider seçildiğinde aşağıdaki ASP0000 uyarısı gösterilir:

    Uygulama kodundan 'BuildServiceProvider' çağrısının ASP0000, tekil hizmetlerin ek bir kopyasının oluşturulmasına neden olur. Bağımlılık ekleme hizmetlerini 'Yapılandır' parametresi olarak ekleme gibi alternatifleri göz önünde bulundurun.

    Çağrısı BuildServiceProvider , parçalanmış tekiller oluşturabilen ve birden çok kapsayıcıda nesne graflarına başvurulara neden olabilen ikinci bir kapsayıcı oluşturur.

    Elde LoginPath etmenin doğru bir yolu, seçenekler deseninin DI için yerleşik desteğini kullanmaktır:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();
    
        services.AddOptions<CookieAuthenticationOptions>(
                            CookieAuthenticationDefaults.AuthenticationScheme)
            .Configure<IMyService>((options, myService) =>
            {
                options.LoginPath = myService.GetLoginPath();
            });
    
        services.AddRazorPages();
    }
    
  • Atılabilir geçici hizmetler, kapsayıcı tarafından imha edilmek üzere yakalanır. Bu, en üst düzey kapsayıcıdan çözümlenirse bellek sızıntısına dönüşebilir.

  • Uygulamanın kapsamı belirlenmiş hizmetleri yakalayan tekillere sahip olmadığından emin olmak için kapsam doğrulamasını etkinleştirin. Daha fazla bilgi için bkz . Kapsam doğrulaması.

Tüm öneri kümelerinde olduğu gibi, bir öneriyi yoksaymanın gerekli olduğu durumlarla karşılaşabilirsiniz. Özel durumlar nadirdir, çoğunlukla çerçevenin kendi içinde özel durumlardır.

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. Daha fazla bilgi için Bkz . Orchard Core Belgeleri.

CMS'ye özgü özelliklerden herhangi biri olmadan yalnızca Orchard Core Framework kullanarak modüler ve çok kiracılı uygulamalar oluşturma örnekleri için Orchard Core örneklerine bakın.

Çerçeve tarafından sağlanan hizmetler

yöntemi, Startup.ConfigureServices Entity Framework Core ve ASP.NET Core MVC gibi platform özellikleri dahil olmak üzere uygulamanın kullandığı hizmetleri kaydeder. Başlangıçta, IServiceCollection sağlananConfigureServices, konağın nasıl yapılandırıldığına bağlı olarak çerçeve tarafından tanımlanan hizmetlere sahiptir. ASP.NET Core şablonlarına dayalı uygulamalar için çerçeve 250'den fazla hizmet kaydeder.

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

Hizmet Türü Yaşam süresi
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Geçici
IHostApplicationLifetime Tekil
IWebHostEnvironment Tekil
Microsoft.AspNetCore.Hosting.IStartup Tekil
Microsoft.AspNetCore.Hosting.IStartupFilter Geçici
Microsoft.AspNetCore.Hosting.Server.IServer Tekil
Microsoft.AspNetCore.Http.IHttpContextFactory Geçici
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.DiagnosticSource Tekil
System.Diagnostics.DiagnosticListener Tekil

Ek kaynaklar