ASP.NET Core'da bağımlılık ekleme
Uyarı
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 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 MyDependency
WriteMessage
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ştirilmelidirIndexModel
. - Bağımlılıkları varsa
MyDependency
, bunların da sınıfı tarafından yapılandırılmasıIndexModel
gerekir. bağlı olarakMyDependency
birden ç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 MyDependency
kaydederIMyDependency
. 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ü
MyDependency
kullanmaz, yalnızcaIMyDependency
uyguladığı arabirimi kullanır. Bu, denetleyiciyi veya Razor Sayfayı değiştirmeden uygulamayı değiştirmeyi kolaylaştırır. - örneğini
MyDependency
oluş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();
MyDependency2
ILogger<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.cs
gerek 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 AddDbContext AddDefaultIdentitykullanı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
veyaInvokeAsync
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 AddKeyedScoped
AddKeyedTransient
) ç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ı OperationId
olan 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 IndexModel
ara 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ımdaIndexModel
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 RequestServices
hizmetleri çö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 Dispose IDisposable . 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
Bkz. .NET'te Bağımlılık ekleme önerileri
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ış:
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.
DI'de çok kiracılılık için önerilen desenler
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.
Ç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:
Ek kaynaklar
- ASP.NET Core'daki görünümlere bağımlılık ekleme
- ASP.NET Core'daki denetleyicilere bağımlılık ekleme
- ASP.NET Core'da gereksinim işleyicilerine bağımlılık ekleme
- ASP.NET Core Blazor bağımlılık ekleme
- DI uygulaması geliştirme için NDC Konferans Desenleri
- ASP.NET Core'da uygulama başlatma
- ASP.NET Core'da fabrika tabanlı ara yazılım etkinleştirmesi
- .NET'te bağımlılık eklemeyle ilgili temel bilgileri anlama
- Bağımlılık ekleme yönergeleri
- Öğretici: .NET'te bağımlılık ekleme kullanma
- .NET bağımlılık ekleme
- ASP.NET ÇEKIRDEK BAĞıMLıLıK EKLEME: ISERVICECOLLECTION NEDIR?
- IDisposables'ı ASP.NET Core'da atmanın dört yolu
- Bağımlılık Ekleme ile ASP.NET Core'da Temiz Kod Yazma (MSDN)
- Açık Bağımlılıklar İlkesi
- Denetim Kapsayıcılarının Ters Çevrilmesi ve Bağımlılık Ekleme Deseni (Martin Fowler)
- ASP.NET Core DI'de birden çok arabirime sahip bir hizmeti kaydetme
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 MyDependency
WriteMessage
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ştirilmelidirIndexModel
. - Bağımlılıkları varsa
MyDependency
, bunların da sınıfı tarafından yapılandırılmasıIndexModel
gerekir. bağlı olarakMyDependency
birden ç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 MyDependency
kaydederIMyDependency
. 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ü
MyDependency
kullanmaz, yalnızcaIMyDependency
uyguladığı arabirimi kullanır. Bu, denetleyiciyi veya Razor Sayfayı değiştirmeden uygulamayı değiştirmeyi kolaylaştırır. - örneğini
MyDependency
oluş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();
MyDependency2
ILogger<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.cs
gerek 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 AddDbContext AddDefaultIdentitykullanı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
veyaInvokeAsync
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ı OperationId
olan 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 IndexModel
ara 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ımdaIndexModel
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 RequestServices
hizmetleri çö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 Dispose IDisposable . 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
Bkz. .NET'te Bağımlılık ekleme önerileri
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ış:
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.
DI'de çok kiracılılık için önerilen desenler
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.
Ç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:
Ek kaynaklar
- ASP.NET Core'daki görünümlere bağımlılık ekleme
- ASP.NET Core'daki denetleyicilere bağımlılık ekleme
- ASP.NET Core'da gereksinim işleyicilerine bağımlılık ekleme
- ASP.NET Core Blazor bağımlılık ekleme
- DI uygulaması geliştirme için NDC Konferans Desenleri
- ASP.NET Core'da uygulama başlatma
- ASP.NET Core'da fabrika tabanlı ara yazılım etkinleştirmesi
- IDisposables'ı ASP.NET Core'da atmanın dört yolu
- Bağımlılık Ekleme ile ASP.NET Core'da Temiz Kod Yazma (MSDN)
- Açık Bağımlılıklar İlkesi
- Denetim Kapsayıcılarının Ters Çevrilmesi ve Bağımlılık Ekleme Deseni (Martin Fowler)
- ASP.NET Core DI'de birden çok arabirime sahip bir hizmeti kaydetme
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 MyDependency
WriteMessage
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ştirilmelidirIndexModel
. - Bağımlılıkları varsa
MyDependency
, bunların da sınıfı tarafından yapılandırılmasıIndexModel
gerekir. bağlı olarakMyDependency
birden ç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 MyDependency
kaydederIMyDependency
. 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ü
MyDependency
kullanmaz, yalnızcaIMyDependency
uyguladığı arabirimi kullanır. Bu, denetleyiciyi değiştirmeden denetleyicinin kullandığı uygulamayı değiştirmeyi kolaylaştırır. - örneğini
MyDependency
oluş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();
}
MyDependency2
ILogger<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 ConfigureServices
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; }
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 ConfigureServices
gerek 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 AddDbContext AddDefaultIdentitykullanı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
veyaInvokeAsync
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ı OperationId
olan 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 IndexModel
ara 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ımdaIndexModel
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 RequestServices
hizmetleri çö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 Dispose IDisposable . 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
Bkz. .NET'te Bağımlılık ekleme önerileri
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ış:
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. AramaBuildServiceProvider
genellikle geliştirici içindekiConfigureServices
bir hizmeti çözümlemek istediğinde gerçekleşir. Örneğin, öğesinin yapılandırmadan yüklendiğiLoginPath
durumu göz önünde bulundurun. Aşağıdaki yaklaşımdan kaçının: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.
DI'de çok kiracılılık için önerilen desenler
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.
Ç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:
Ek kaynaklar
- ASP.NET Core'daki görünümlere bağımlılık ekleme
- ASP.NET Core'daki denetleyicilere bağımlılık ekleme
- ASP.NET Core'da gereksinim işleyicilerine bağımlılık ekleme
- ASP.NET Core Blazor bağımlılık ekleme
- DI uygulaması geliştirme için NDC Konferans Desenleri
- ASP.NET Core'da uygulama başlatma
- ASP.NET Core'da fabrika tabanlı ara yazılım etkinleştirmesi
- IDisposables'ı ASP.NET Core'da atmanın dört yolu
- Bağımlılık Ekleme ile ASP.NET Core'da Temiz Kod Yazma (MSDN)
- Açık Bağımlılıklar İlkesi
- Denetim Kapsayıcılarının Ters Çevrilmesi ve Bağımlılık Ekleme Deseni (Martin Fowler)
- ASP.NET Core DI'de birden çok arabirime sahip bir hizmeti kaydetme
ASP.NET Core