.Net 8 进阶——依赖注入

1,002 阅读4分钟

依赖注入是.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!");