依赖注入是.NET 8
开发项目的核心组件,主要用于项目中创建对象、存储对象、获取对象、对象属性赋值(依赖注入)、管理生命周期等功能,也被称为IOC容器
或者控制反转容器。
在.NET 8
中使用微软官方提供的扩展包Microsoft.Extensions.DependencyInjection
来实现依赖注入功能
1. Microsoft.Extensions.DependencyInjection
① 安装依赖包
Install-Package Microsoft.Extensions.DependencyInjection
② 创建一个商品服务
namespace Injection
{
public class ProductService
{
public void getProduct()
{
Console.WriteLine("获取到商品");
}
}
}
③ 使用依赖注入基础流程
using Injection;
using Microsoft.Extensions.DependencyInjection;
ServiceCollection services = new ServiceCollection();
services.AddTransient<ProductService>();
var provider = services.BuildServiceProvider();
ProductService? productService = provider.GetService<ProductService>();
productService?.getProduct();
④ 依赖注入容器支持三种主要的服务生命周期:
- Transient:每次请求服务时,都会创建一个新的实例。
- Scoped:在一个请求的范围内,服务的实例是共享的,但在不同的请求中是不同的。
- Singleton:整个应用程序生命周期内,服务的实例是单一的。
using Injection;
using Microsoft.Extensions.DependencyInjection;
// 创建服务集合并注册服务
var services = new ServiceCollection();
services.AddTransient<ProductService>(); // Transient
services.AddScoped<ProductService>(); // Scoped
services.AddSingleton<ProductService>(); // Singleton
// 构建服务提供者
var serviceProvider = services.BuildServiceProvider();
// 获取Transient服务
var transientService = serviceProvider.GetService<ProductService>();
transientService?.GetProduct();
// 获取Scoped服务
using (var scope = serviceProvider.CreateScope())
{
var scopedService = scope.ServiceProvider.GetService<ProductService>();
scopedService?.GetProduct();
}
// 获取Singleton服务
var singletonService = serviceProvider.GetService<ProductService>();
singletonService?.GetProduct();
⑤ 属性注入
要实现属性注入,首先需要定义一个接口,然后让ProductService
实现这个接口。接着,你可以在依赖注入容器中配置属性注入,以便在创建ProductService
实例时注入所需的依赖。
namespace Injection
{
public interface IProductService
{
void GetProduct();
}
}
namespace Injection
{
public class ProductService : IProductService
{
public void GetProduct()
{
Console.WriteLine("获取到商品");
}
}
}
using Injection;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
// 注册服务和接口
services.AddTransient<IProductService, ProductService>();
var serviceProvider = services.BuildServiceProvider();
var productService = serviceProvider.GetService<IProductService>();
productService?.GetProduct();
为了实现属性注入,你需要定义一个属性,该属性将被注入到ProductService
中。
namespace Injection
{
public class ProductService : IProductService
{
private readonly ILogger _logger;
public ProductService(ILogger logger)
{
_logger = logger;
}
public void GetProduct()
{
_logger.Log("正在获取商品");
Console.WriteLine("获取到商品");
}
}
}
在这里,ILogger
是一个假设的日志服务,你需要在依赖注入容器中注册它。
namespace Injection
{
public interface ILogger
{
void Log(string message);
}
public class Logger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"日志: {message}");
}
}
}
在依赖注入容器中注册ILogger
服务,并配置属性注入。
using Injection;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
// 注册服务和接口
services.AddTransient<IProductService, ProductService>();
services.AddTransient<ILogger, Logger>();
var serviceProvider = services.BuildServiceProvider();
var productService = serviceProvider.GetService<IProductService>();
productService?.GetProduct();
⑥ 接口多实现类
在依赖注入(DI)中,一个接口可以有多个实现类,这在实现多种策略或者行为时非常有用。注册多个实现类到DI容器,并在需要时获取它们,可以通过几种方式实现。
namespace Injection
{
public interface IMessageService
{
void SendMessage(string message);
}
public class EmailService : IMessageService
{
public void SendMessage(string message)
{
Console.WriteLine($"Sending email: {message}");
}
}
public class SmsService : IMessageService
{
public void SendMessage(string message)
{
Console.WriteLine($"Sending SMS: {message}");
}
}
}
using Injection;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddTransient<IMessageService, EmailService>();
services.AddTransient<IMessageService, SmsService>();
如果你需要获取所有实现类的实例,可以使用 IEnumerable<T>
:
var serviceProvider = services.BuildServiceProvider();
var messageServices = serviceProvider.GetServices<IMessageService>();
foreach (var service in messageServices)
{
service.SendMessage("Hello, World!");
}
如果你只需要获取特定的实现类,可以直接通过服务提供者获取:
var emailService = serviceProvider.GetService<IMessageService>() as EmailService;
emailService?.SendMessage("Hello via Email");
var smsService = serviceProvider.GetService<IMessageService>() as SmsService;
smsService?.SendMessage("Hello via SMS");
如果你需要更灵活地创建服务实例,可以实现一个工厂接口:
public interface IMessageServiceFactory
{
IMessageService CreateEmailService();
IMessageService CreateSmsService();
}
public class MessageServiceFactory : IMessageServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public MessageServiceFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IMessageService CreateEmailService()
{
return _serviceProvider.GetService<EmailService>();
}
public IMessageService CreateSmsService()
{
return _serviceProvider.GetService<SmsService>();
}
}
// 注册工厂
services.AddSingleton<IMessageServiceFactory, MessageServiceFactory>();
然后,你可以使用工厂来创建特定的服务实例:
var messageServiceFactory = serviceProvider.GetService<IMessageServiceFactory>();
var emailService = messageServiceFactory.CreateEmailService();
emailService?.SendMessage("Hello via Email");
var smsService = messageServiceFactory.CreateSmsService();
smsService?.SendMessage("Hello via SMS");
2. Autofac
Autofac
是一个流行的第三方依赖注入容器,它提供了更灵活的依赖注入功能,包括属性注入、模块化、拦截器等,这些功能可以帮助你简化依赖注入的配置和增强服务的灵活性。
① 确保你已经安装了 Autofac 和其对 ASP.NET Core 的集成包:
dotnet add package Autofac
dotnet add package Autofac.Extensions.DependencyInjection
② 配置Autofac
using Autofac;
using Injection;
using Microsoft.Extensions.DependencyInjection;
var builder = new ContainerBuilder();
// 注册服务
builder.RegisterType<ProductService>().As<IProductService>().InstancePerLifetimeScope();
builder.RegisterType<EmailService>().As<IMessageService>().InstancePerDependency();
// 构建容器
var container = builder.Build();
// 解析服务
var productService = container.Resolve<IProductService>();
var emailService = container.Resolve<IMessageService>();
③ 生命周期与特性
Autofac
支持生命周期管理,并且可以通过标签、属性注入等方式提供更细粒度的控制。
Instance Per Lifetime Scope
:在每个生命周期范围内创建一个实例。Instance Per Dependency
:每次解析服务时创建一个新的实例。
builder.RegisterType<ProductService>().As<ProductService>().InstancePerLifetimeScope();
builder.RegisterType<ProductService>().As<ProductService>().InstancePerDependency();
④ 批量注册
你可以使用 RegisterAssemblyTypes
方法来注册程序集中的所有类型,并且使用 AsImplementedInterfaces
方法来指定这些类型应该注册为它们的实现的接口,还可以使用 Where
方法来过滤。Autofac
允许你使用标签来注册和解析服务,这在需要对同一接口有多个实现时非常有用,你还可以在使用 RegisterAssemblyTypes
方法时配置服务的生命周期。
var builder = new ContainerBuilder();
// 1.1 整个项目注册
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsImplementedInterfaces()
.PropertiesAutowired(); // 开启属性注入
// 1.2 只注册以 "Service" 结尾的类型
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.PropertiesAutowired();
// 1.3 使用标签进行注册
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsImplementedInterfaces()
.WithAttributeFilter() // 使用特性进行过滤
.PropertiesAutowired();
// 1.4 批量注册并配置生命周期
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerLifetimeScope(); // 设置生命周期为 InstancePerLifetimeScope
// 构建容器
var container = builder.Build();
// 解析服务
var messageService = container.Resolve<IMessageService>();
messageService.SendMessage("Hello, Autofac!");