Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
.NET, sınıflar ve bağımlılıkları arasında Denetimin Ters Çevrilmesi (IoC) elde etmek için kullanılan bir teknik olan bağımlılık ekleme (DI) yazılım tasarımı desenini destekler. .NET'te bağımlılık ekleme, yapılandırma, günlüğe kaydetme ve seçenekler deseninin yanı sıra çerçevenin yerleşik bir parçasıdır.
Bağımlılık, başka bir nesnenin bağımlı olduğu bir nesnedir. Aşağıdaki MessageWriter sınıf, diğer sınıfların bağımlı olabileceği bir Write yönteme sahiptir:
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Bir sınıf, MessageWriter sınıfının bir örneğini oluşturup Write yöntemini kullanabilir. Aşağıdaki örnekte, MessageWriter sınıfı, sınıfının bir Worker.
public class Worker : BackgroundService
{
private readonly MessageWriter _messageWriter = new();
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
Bu durumda, Worker sınıfı oluşturur ve doğrudan MessageWriter sınıfına bağlıdır. Bunun gibi sabit kodlanmış bağımlılıklar sorunludur ve aşağıdaki nedenlerle kaçınılmalıdır:
- Farklı bir uygulamayı
MessageWriterile değiştirmek içinWorkersınıfını değiştirmeniz gerekir. - Bağımlılıkları varsa
MessageWriter, sınıfı daWorkerbunları yapılandırmalıdır. bağlı olarakMessageWriterbirden ç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
MessageWritersınıfı kullanmalıdır.
Kavram
Bağımlılık ekleme, sabit kodlanmış bağımlılık sorunlarını şu şekilde giderir:
Bağımlılık uygulamasını soyutlama amacıyla bir arabirim veya temel sınıf kullanımı.
Hizmet kapsayıcısında bağımlılığın kaydı.
.NET, yerleşik bir hizmet kapsayıcısı sağlar. IServiceProvider Hizmetler genellikle uygulamanın başlangıcında kaydedilir ve öğesine IServiceCollectioneklenir. Tüm hizmetler eklendikten sonra hizmet kapsayıcısını oluşturmak için kullanın BuildServiceProvider .
Hizmetin kullanıldığı sınıfın oluşturucusuna eklenmesi.
Çerçeve, bağımlılığın bir örneğini oluşturma ve artık gerekli olmadığında bunu yok etme sorumluluğunu üstlenir.
Tavsiye
Bağımlılık enjeksiyonu terminolojisinde, bir hizmet genellikle IMessageWriter gibi diğer nesnelere hizmet sağlayan bir nesnedir. Hizmet, bir web hizmeti kullanabileceği halde bir web hizmetiyle ilgili değildir.
Örneğin, IMessageWriter arabirimi Write yöntemini tanımlar. Bu arabirim, daha önce gösterilen somut bir türle MessageWriteruygulanır. Aşağıdaki örnek kod, IMessageWriter hizmetini MessageWriter somut türüyle kayıt eder.
AddSingleton yöntemi, hizmeti singleton ömrüyle kaydeder; bu da uygulama kapatılana kadar atılmadığı anlamına gelir.
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();
using IHost host = builder.Build();
host.Run();
// <SnippetMW>
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
// </SnippetMW>
// <SnippetIMW>
public interface IMessageWriter
{
void Write(string message);
}
// </SnippetIMW>
// <SnippetWorker>
public sealed class Worker(IMessageWriter messageWriter) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
// </SnippetWorker>
Yukarıdaki kod örneğinde vurgulanan satırlar:
- Bir ana bilgisayar uygulama oluşturucu örneği oluşturun.
hizmetini barındırılan bir hizmet olarak ve sınıfının ilgili uygulamasıyla arabirimini tekil bir hizmet olarak kaydederek hizmetleri yapılandırın. - Sunucuyu oluşturun ve çalıştırın.
Konak, bağımlılık ekleme hizmet sağlayıcısını içerir. Ayrıca, öğesini otomatik olarak başlatmak Worker ve ilgili IMessageWriter uygulamayı bağımsız değişken olarak sağlamak için gereken diğer tüm ilgili hizmetleri içerir.
Çalışan hizmeti, DI desenini kullanarak somut türü MessageWriter yerine, yalnızca uyguladığı IMessageWriter arabirimini kullanır. Bu tasarım, çalışan hizmetini değiştirmeden çalışan hizmetinin kullandığı uygulamayı değiştirmeyi kolaylaştırır. Çalışan hizmeti ayrıca örneğiniMessageWriteroluşturmaz. DI kapsayıcısı nesneyi oluşturur.
Şimdi, MessageWriter çerçeve tarafından sağlanan günlük hizmetini kullanan bir türle değiştirmek istediğinizi hayal edin. Constructor'da isteyerek LoggingMessageWriter bağımlılığı olan bir ILogger<TCategoryName> sınıfı oluşturun.
public class LoggingMessageWriter(
ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
public void Write(string message) =>
logger.LogInformation("Info: {Msg}", message);
}
MessageWriter öğesinden LoggingMessageWriter öğesine geçmek için AddSingleton çağrısını, bu yeni IMessageWriter uygulamasını kaydedecek şekilde güncelleştirmeniz yeterlidir.
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
Tavsiye
Kapsayıcı, ILogger<TCategoryName> yararlanarak çözümlenir ve bu da her (genel) yapı türü kaydetme gereksinimini ortadan kaldırır.
Oluşturucu ekleme davranışı
Hizmetler IServiceProvider (yerleşik hizmet kapsayıcısı) veya ActivatorUtilities kullanılarak çözümlenebilir.
ActivatorUtilities kapsayıcıda kayıtlı olmayan nesneler oluşturur ve bazı çerçeve özellikleriyle birlikte kullanılır.
Oluşturucular bağımlılık ekleme tarafından sağlanmayan bağımsız değişkenleri kabul edebilir, ancak bağımsız değişkenlerin varsayılan değerler ataması gerekir.
IServiceProvider veya ActivatorUtilities hizmetleri çözdüğünde, oluşturucu enjeksiyonu için genel bir oluşturucu gerekir.
Hizmetleri çözümlerken ActivatorUtilities, oluşturucu enjeksiyonu yalnızca bir geçerli oluşturucu bulunmasını gerektirir. Oluşturucu aşırı yüklemeleri desteklenir, ancak bağımsız değişkenleri bağımlılık ekleme ile yerine getirilebilen yalnızca bir aşırı yükleme olabilir.
Oluşturucu seçim kuralları
Bir tür birden fazla oluşturucu tanımladığında, hizmet sağlayıcısının hangi oluşturucunun kullanılacağını belirleme mantığı vardır. Türlerin DI çözümlenebilir olduğu en çok parametreye sahip oluşturucu seçilir. Aşağıdaki örnek hizmeti göz önünde bulundurun:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// ...
}
public ExampleService(ServiceA serviceA, ServiceB serviceB)
{
// ...
}
}
Önceki kodda, günlük kaydının eklendiğini ve hizmet sağlayıcısından çözümlenebilir olduğunu, ancak ServiceA ve ServiceB türlerinin çözümlenmediğini varsayalım.
ILogger<ExampleService> parametresine sahip oluşturucu, ExampleService örneğini çözümler. Daha fazla parametre tanımlayan bir oluşturucu olmasına rağmen, ServiceA ve ServiceB türleri DI tarafından çözümlenebilir değildir.
Oluşturucular keşfedilirken belirsizlik varsa, bir özel durum oluşturulur. Aşağıdaki C# örnek hizmetini göz önünde bulundurun:
Uyarı
Belirsiz DI çözümlenebilir tür parametrelerine sahip bu ExampleService kod bir özel durum oluşturur. Bunu yapmayın, çünkü "belirsiz DI çözümlenebilir türler" ile neyin kastedildiğini göstermek için tasarlanmıştır.
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// ...
}
public ExampleService(IOptions<ExampleOptions> options)
{
// ...
}
}
Yukarıdaki örnekte üç oluşturucu vardır. İlk oluşturucu parametresizdir ve hizmet sağlayıcısından hizmet gerektirmez. Hem günlüğe kaydetme hem de seçeneklerin DI kapsayıcısına eklendiğini ve DI çözümlenebilir hizmetler olduğunu varsayalım. DI kapsayıcısı ExampleService türünü çözümlemeye çalıştığında, iki oluşturucu belirsiz olduğundan bir hata fırlatır.
Bunun yerine her iki DI çözümlenebilir türü de kabul eden bir oluşturucu tanımlayarak belirsizlikten kaçının:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(
ILogger<ExampleService> logger,
IOptions<ExampleOptions> options)
{
// ...
}
}
Kapsam doğrulaması
Kapsamı belirlenmiş hizmetler , bunları oluşturan kapsayıcı tarafından atılır. Belirli bir kapsamda tanımlanan bir hizmet kök kapsayıcıda oluşturulursa, hizmetin kullanım ömrü etkin bir şekilde singleton'a yükseltilmiş olur, çünkü yalnızca uygulama kapatıldığında kök kapsayıcı tarafından atılır. Hizmet kapsamlarını doğrulamak çağrıldığında BuildServiceProvider bu durumları yakalar.
Bir uygulama geliştirme ortamında çalıştığında ve ana bilgisayarı derlemek için CreateApplicationBuilder'ı çağırdığında, varsayılan hizmet sağlayıcısı aşağıdakileri doğrulamak için kontroller gerçekleştirir:
- Kapsamı belirlenmiş hizmetler kök hizmet sağlayıcısından çözümlenmez.
- Kapsamı belirlenmiş hizmetler tekil hizmetlere eklenmez.
Kapsam senaryoları
IServiceScopeFactory her zaman tekil olarak kaydedilir, ancak IServiceProvider öğesini içeren sınıfın ömrüne göre değişiklik gösterebilir. Örneğin, kapsamdan hizmetleri çözümlüyorsanız ve bu hizmetlerden herhangi biri bir IServiceProvider alırsa, bu kapsam dâhilinde bir örnektir.
gibi uygulamaları IHostedServiceBackgroundServiceiçinde kapsam belirleme hizmetleri elde etmek için, oluşturucu ekleme yoluyla hizmet bağımlılıklarını eklemeyin. Bunun yerine ekleyin IServiceScopeFactory, bir kapsam oluşturun ve ardından uygun hizmet ömrünü kullanmak için kapsamdaki bağımlılıkları çözün.
namespace WorkerScope.Example;
public sealed class Worker(
ILogger<Worker> logger,
IServiceScopeFactory serviceScopeFactory)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
try
{
logger.LogInformation(
"Starting scoped work, provider hash: {hash}.",
scope.ServiceProvider.GetHashCode());
var store = scope.ServiceProvider.GetRequiredService<IObjectStore>();
var next = await store.GetNextAsync();
logger.LogInformation("{next}", next);
var processor = scope.ServiceProvider.GetRequiredService<IObjectProcessor>();
await processor.ProcessAsync(next);
logger.LogInformation("Processing {name}.", next.Name);
var relay = scope.ServiceProvider.GetRequiredService<IObjectRelay>();
await relay.RelayAsync(next);
logger.LogInformation("Processed results have been relayed.");
var marked = await store.MarkAsync(next);
logger.LogInformation("Marked as processed: {next}", marked);
}
finally
{
logger.LogInformation(
"Finished scoped work, provider hash: {hash}.{nl}",
scope.ServiceProvider.GetHashCode(), Environment.NewLine);
}
}
}
}
}
Yukarıdaki kodda, uygulama çalışırken arka plan hizmeti:
- öğesine bağlıdır IServiceScopeFactory.
- Diğer hizmetleri çözümlemek için bir IServiceScope oluşturur.
- Tüketim için kapsamı belirlenmiş hizmetleri çözümler.
- Nesneleri işleme ve sonra bunları geçirme üzerinde çalışır ve son olarak işlenmiş olarak işaretler.
Örnek kaynak kodundan, uygulamalarının IHostedService kapsamlı hizmet ömründen nasıl yararlanabileceğini görebilirsiniz.
Anahtarlı hizmetler
Hizmetleri kaydedebilir ve bir anahtara göre aramalar yapabilirsiniz. Başka bir deyişle, farklı anahtarlara sahip birden çok hizmeti kaydetmek ve arama için bu anahtarı kullanmak mümkündür.
Örneğin, arabiriminin IMessageWriterfarklı uygulamalarına sahip olduğunuz durumu göz önünde bulundurun: MemoryMessageWriter ve QueueMessageWriter.
Bir anahtarı parametre olarak destekleyen hizmet kayıt yöntemlerinin (daha önce görüldüğü) aşırı yüklemesini kullanarak bu hizmetleri kaydedebilirsiniz:
services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");
key ile sınırlı stringdeğildir.
key doğru şekilde uygulandığı sürece, object, istediğiniz herhangi bir Equals olabilir.
kullanan IMessageWritersınıfının oluşturucusunda, çözümlenmesi gereken hizmetin anahtarını belirtmek için öğesini eklersiniz FromKeyedServicesAttribute :
public class ExampleService
{
public ExampleService(
[FromKeyedServices("queue")] IMessageWriter writer)
{
// Omitted for brevity...
}
}
KeyedService.AnyKey özelliği
özelliği, KeyedService.AnyKey anahtarlı hizmetlerle çalışmak için özel bir anahtar sağlar.
KeyedService.AnyKey kullanarak, herhangi bir anahtarla eşleşen bir yedek olarak bir hizmeti kaydedebilirsiniz. Bu, açık kaydı olmayan herhangi bir anahtar için varsayılan bir uygulama sağlamak istediğinizde kullanışlıdır.
var services = new ServiceCollection();
// Register a fallback cache for any key.
services.AddKeyedSingleton<ICache>(KeyedService.AnyKey, (sp, key) =>
{
// Create a cache instance based on the key.
return new DefaultCache(key?.ToString() ?? "unknown");
});
// Register a specific cache for the "premium" key.
services.AddKeyedSingleton<ICache>("premium", new PremiumCache());
var provider = services.BuildServiceProvider();
// Requesting with "premium" key returns PremiumCache.
var premiumCache = provider.GetKeyedService<ICache>("premium");
Console.WriteLine($"Premium key: {premiumCache}");
// Requesting with any other key uses the AnyKey fallback.
var basicCache = provider.GetKeyedService<ICache>("basic");
Console.WriteLine($"Basic key: {basicCache}");
var standardCache = provider.GetKeyedService<ICache>("standard");
Console.WriteLine($"Standard key: {standardCache}");
Yukarıdaki örnekte:
- Anahtarla
ICacheistekte bulunmak,"premium"PremiumCacheörneğini döndürür. -
ICachebaşka bir anahtarla (gibi"basic"veya"standard") istekte bulunmak, geri dönüşDefaultCachekullanarak yeni birAnyKeyoluşturur.
Önemli
.NET 10'dan başlayarak, GetKeyedService() ile çağrı yapmak, KeyedService.AnyKey sorgu anahtarı değil, bir kayıt geri dönüşü olarak amaçlandığı için, InvalidOperationException bir AnyKey oluşturur. Daha fazla bilgi için bkz . AnyKey ile GetKeyedService() ve GetKeyedServices() sorunlarını düzeltme.
Ayrıca bakınız
- Hızlı Başlangıç: Bağımlılık eklemeyle ilgili temel bilgiler
- Öğretici: .NET'te bağımlılık ekleme kullanma
- Bağımlılık enjeksiyonu yönergeleri
- ASP.NET Core'da bağımlılık ekleme
- DI uygulaması geliştirme için NDC Konferans Desenleri
- Açık bağımlılıklar ilkesi
- Denetim kapsayıcılarının ve bağımlılık ekleme deseninin ters çevrilmesi (Martin Fowler)