依赖项注入 (DI) 是 ASP.NET Core 中的一项重要功能,使您能够以弯曲的方式管理依赖于每个不同的实用程序的部分。虽然许多开发人员了解基础知识,但卓越的 DI 策略可以广泛增强大型或复杂计划的能力。在本文中,我们将介绍一些高级技术。
第 1 部分:深入了解服务生命周期
ASP.NET Core 支持三种类型的提供程序生存期:单一实例、作用域和瞬态。每个都有自己的用例和对实用程序性能的影响。
_单一实例_:在应用程序的生命周期内使用单个实例。适用于无状态服务或缓存。
_范围:_根据请求创建新实例。最适合想要操作请求状态但不再全局操作的服务。
_Transient_:每次请求提供程序时,都会创建一个新实例。非常适合轻量级、无状态的产品。
_高级提示:将 scoped 产品注入 singleton 产品时要小心。ASP.NET Core 将引发异常,以防你尝试从单一实例中清除范围提供程序。常见的答案是使用 IServiceProvider 或工厂示例_手动操作范围。
public class MySingletonService
{
private readonly IServiceProvider _serviceProvider;
public MySingletonService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoWork()
{
using (var scope = _serviceProvider.CreateScope())
{
var scopedService = scope.ServiceProvider.GetRequiredService<IMyScopedService>();
scopedService.PerformTask();
}
}
}
第 2 部分:使用 IServiceProvider 和 IServiceScopeFactory
在某些情况下,您可能希望在服务生命周期内进行更好的管理。_IServiceProvider_ 接口允许以编程方式引入提供程序范围,这在冗长的职责(如历史记录服务)或具有非标准请求生存期的程序中特别有用。
例如,如果你有一个希望修复范围服务的托管服务,则可以使用 _IServiceScopeFactory_:
public class MyBackgroundService : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
public MyBackgroundService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _scopeFactory.CreateScope())
{
var scopedService = scope.ServiceProvider.GetRequiredService<IMyScopedService>();
await scopedService.DoWorkAsync();
}
await Task.Delay(1000, stoppingToken);
}
}
}
此示例确保后台任务的每次迭代都有其自己的范围提供程序的 sparkling 实例。
第 3 部分:使用 IServiceProvider 的条件 DI
在高级情况下,您可能希望根据运行时配置或其他因素有条件地注册产品。将 IServiceProvider 用于运行时 DI 可以为您提供以下柔韧性:
public void ConfigureServices(IServiceCollection services)
{
if (someCondition)
{
services.AddTransient<IMyService, MyServiceA>();
}
else
{
services.AddTransient<IMyService, MyServiceB>();
}
}
ASP.NET Core 的 IServiceProvider 允许运行时的良好判断,以完全根据环境或特定情况来决定要注入哪个实现。
第 4 部分:使用 IEnumerable<T 处理多个实现>
使用 _**IEnumerable<T**_ 处理多个实现>
有时,您可能希望注入一个接口的几个实现并动态地使用它们。ASP.NET Core DI 通过 IEnumerable<T> 支持此功能:
public class MyConsumerService
{
private readonly IEnumerable<IMyService> _services;
public MyConsumerService(IEnumerable<IMyService> services)
{
_services = services;
}
public void ExecuteAll()
{
foreach (var service in _services)
{
service.PerformOperation();
}
}
}
这允许您注册多个产品,并在运行时确定要调用哪个产品。
services.AddTransient<IMyService, MyServiceA>();
services.AddTransient<IMyService, MyServiceB>();
第 5 部分:管理循环依赖关系
循环依赖关系在使用 DI.例如,如果两个产品相互依赖,则可能会导致运行时异常。ASP.NET Core 允许使用构造函数注入或通过应用技术或资产注入等策略,或者通过使用工厂来打破此类循环。
要清除循环依赖关系:
使用制造单元样本打破循环。
注入 IServiceProvider 并在运行时解析其中一个产品/服务。
public class ServiceA
{
private readonly IServiceProvider _serviceProvider;
public ServiceA(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void UseServiceB()
{
var serviceB = _serviceProvider.GetService<ServiceB>();
serviceB.PerformOperation();
}
}
第 6 部分:实现具有多个共享同一实例的接口的类
当一个类别实现多个接口时,可以登录并为特殊接口注入 equal 示例,从而确保每个接口在整个应用程序中共享相同的项。
下面是一个类的单个实例实现多个接口的示例:
public interface IEmailSender
{
void SendEmail(string to, string subject, string body);
}
public interface ISmsSender
{
void SendSms(string number, string message);
}
public class NotificationService : IEmailSender, ISmsSender
{
public void SendEmail(string to, string subject, string body)
{
// Email sending logic
}
public void SendSms(string number, string message)
{
// SMS sending logic
}
}
要确保在每个接口中共享相同的实例,请尽快登录 elegance 并将两个接口映射到该相等的实例:
services.AddSingleton<NotificationService>(); // Register the class
services.AddSingleton<IEmailSender>(provider => provider.GetService\<NotificationService>()); // Use the same instance
services.AddSingleton<ISmsSender>(provider => provider.GetService\<NotificationService>()); // Use the same instance
在这里,_NotificationService_ 注册为单一实例,并且每个 IEmailSender 和 ISmsSender 都解析为使用颁发者的 NotificationService 的等效示例_。GetService<NotificationService>()_ 的
当您将两个接口注入到一个类别中时,可以共享相同的 NotificationService 示例:
public class CommunicationController
{
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
public CommunicationController(IEmailSender emailSender, ISmsSender smsSender)
{
_emailSender = emailSender;
_smsSender = smsSender;
}
public void NotifyUser(string email, string phoneNumber)
{
_emailSender.SendEmail(email, "Subject", "Body");
_smsSender.SendSms(phoneNumber, "Message");
}
}
为什么有效
NotificationService 最好实例化一次,因为它是单例注册。
IEmailSender 和 ISmsSender 都因 NotificationService 的同一实例而被解析,从而确保稳定的行为和国家/地区。
第 7 部分:将 AddKeyedTransient 用于多个实现
在 .NET 8 中,ASP.NET Core 引入了 AddKeyed 技术,其中包括 AddKeyedTransient、AddKeyedScoped 和 _AddKeyedSingleton_,这些技术允许你注册同一运营商的多个实现,并在运行时根据密钥检索精确的实现。如果您有几个版本的相等 provider 接口,但需要根据上下文动态选择一个版本,则这主要是有益的。
让我们记住一个示例,在该示例中,您根据价格类型获得了 IPaymentProcessor 接口的不同实现,其中包括 CreditCardProcessor 和 _PayPalProcessor_:
public interface IPaymentProcessor
{
void ProcessPayment(decimal amount);
}
public class CreditCardProcessor : IPaymentProcessor
{
public void ProcessPayment(decimal amount)
{
// Logic for credit card payment
Console.WriteLine($"Processing credit card payment of {amount}");
}
}
public class PayPalProcessor : IPaymentProcessor
{
public void ProcessPayment(decimal amount)
{
// Logic for PayPal payment
Console.WriteLine($"Processing PayPal payment of {amount}");
}
}
使用 _AddKeyedTransient_,您可以使用独占键登录这两个实现:
services.AddKeyedTransient<IPaymentProcessor, CreditCardProcessor>("CreditCard");
services.AddKeyedTransient<IPaymentProcessor, PayPalProcessor>("PayPal");
要完全基于键解析正确的实现,可以注入 _IKeyedServiceProvider_:
public class PaymentService
{
private readonly IKeyedServiceProvider _keyedServiceProvider;
public PaymentService(IKeyedServiceProvider keyedServiceProvider)
{
_keyedServiceProvider = keyedServiceProvider;
}
public void MakePayment(string paymentType, decimal amount)
{
var processor = _keyedServiceProvider.GetRequiredService<IPaymentProcessor>(paymentType);
processor.ProcessPayment(amount);
}
}
现在,根据重要的事情(“_CreditCard”或“PayPal_”),可以解决并使用 IPaymentProcessor 的最佳实现。
public class PaymentService
{
private readonly IKeyedServiceProvider _keyedServiceProvider;
public PaymentService(IKeyedServiceProvider keyedServiceProvider)
{
_keyedServiceProvider = keyedServiceProvider;
}
public void MakePayment(string paymentType, decimal amount)
{
var processor = _keyedServiceProvider.GetRequiredService<IPaymentProcessor>(paymentType);
processor.ProcessPayment(amount);
}
}
当您的软件支持多种策略或行为(例如收费策略、通知渠道)并允许在独一无二的实施之间轻松分离时,这种方法特别有用。
ASP.NET Core 中的高级 DI 技术提供了对实用程序行为的更多控制,从而可以优化整体性能、操纵服务生命周期和干净地实施复杂的架构。通过以某种方式正确处理范围产品、装饰器和提供程序生命周期的专业知识,您可以充分利用 ASP.NET Core 强大的 DI 框架。
如果你喜欢我的文章,请给我一个赞!谢谢