ASP.Net依赖注入!使用Microsoft.Extensions.DependencyInjection配置依赖注入
文章目录
- 前言
- 一、依赖注入是什么?
- 二、使用步骤
- 三、示例
- 四、依赖注入服务生命周期
- 五、依赖注入的方式
前言
ASP.Net依赖注入!使用Microsoft.Extensions.DependencyInjection配置依赖注入
一、依赖注入是什么?
百度百科:
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
(简单解释)
依赖注入就是IOC容器会自动给你创建和维护对象,你只需要直接使用这个对象即可。
二、使用步骤
第一、导入Microsoft.Extensions.DependencyInjection库和Microsoft.Extensions.Hosting库
Host.CreateDefaultBuilder(args).ConfigureServices((hostContext, services) =>{// 注册服务services.AddSingleton<LoginForm>();services.AddSingleton<UserService>();// 其他服务注册...});
1、调用 Host.CreateDefaultBuilder 方法创建一个默认的主机构建器。
CreateDefaultBuilder 方法会配置一些默认的服务和设置,包括:
加载配置文件(如 appsettings.json)。
配置日志记录。
设置内容根目录。
args 参数通常是从 Main 方法传递的命令行参数
2、ConfigureServices((hostContext, services) => { … }):
使用 ConfigureServices 方法配置依赖注入容器。
hostContext 参数提供了主机的配置和环境信息。
services 参数是一个 IServiceCollection,用于注册服务。
3、服务注册:
services.AddSingleton():将 LoginForm 类注册为单例服务。这意味着在应用程序的整个生命周期内只会创建一个 LoginForm 的实例。
services.AddSingleton():将 UserService 类注册为单例服务。同样,这个服务在应用程序的整个生命周期内只会创建一个实例。
这些服务注册确保了 LoginForm 和 UserService 可以通过依赖注入的方式在应用程序中使用。
三、示例
主程序入口
class Program
{static void Main(string[] args){using var host = CreateHostBuilder(args).Build();var loginForm = host.Services.GetRequiredService<LoginForm>();Application.Run(loginForm);}public static IHost CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureServices((hostContext, services) =>{services.AddSingleton<LoginForm>();services.AddSingleton<UserService>();});
}
窗体类
public partial class LoginForm : Form
{private readonly UserService _userService;public LoginForm(UserService userService){InitializeComponent();_userService = userService;}private void LoginButton_Click(object sender, EventArgs e){var username = UsernameTextBox.Text;var password = PasswordTextBox.Text;if (_userService.ValidateUser(username, password)){MessageBox.Show("Login successful!");}else{MessageBox.Show("Invalid username or password.");}}
}
用户服务类
public class UserService
{public bool ValidateUser(string username, string password){// 实现用户验证逻辑return username == "admin" && password == "password";}
}
使用流程解释
- 服务注册
在 CreateHostBuilder 方法中,使用 services.AddSingleton() 将 UserService 类注册到依赖注入容器中。这告诉容器 UserService 是一个单例服务,即在整个应用程序生命周期内只会创建一个实例。
public static IHost CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureServices((hostContext, services) =>{services.AddSingleton<LoginForm>();services.AddSingleton<UserService>();});
- 构造函数注入
在 LoginForm 类中,构造函数定义了一个参数 UserService userService。这表示 LoginForm 依赖于 UserService,并且这个依赖项需要在创建 LoginForm 实例时提供。
public partial class LoginForm : Form
{private readonly UserService _userService;public LoginForm(UserService userService){InitializeComponent();_userService = userService;}
}
- 依赖注入容器的作用
当使用 host.Services.GetRequiredService() 从依赖注入容器中获取 LoginForm 实例时,容器会自动解析 LoginForm 的构造函数所需的参数。
容器检查 LoginForm 的构造函数,发现它需要一个 UserService 类型的参数。
容器已经在前面注册了 UserService 为单例服务,因此它会使用之前创建的 UserService 实例来满足这个依赖。
容器将这个 UserService 实例传递给 LoginForm 的构造函数,从而完成依赖注入。 - 获取服务实例
最终,host.Services.GetRequiredService() 返回一个完全初始化的 LoginForm 实例,其中 _userService 字段已经被注入了 UserService 的实例。
var loginForm = host.Services.GetRequiredService<LoginForm>();
四、依赖注入服务生命周期
我得解释 AddScoped,它用于注册作用域服务。这类服务在同一个作用域内共享一个实例,通常用于处理单个请求或操作。我得给用户一个具体的例子,比如数据访问层的 EF Core DbContext,来说明它的实际应用。
接着,我得解释 AddSingleton,它用于注册单例服务。这个很好理解,就是整个应用程序生命周期内只创建一个实例,适合无状态的工具类或跨请求共享数据的服务。我得强调单例服务的使用注意事项,避免因状态共享导致的线程安全问题。
最后,我得解释 AddTransient,它用于注册瞬态服务。这种服务每次请求都会创建新的实例,适合轻量级、无状态的服务,或者每次调用都需要新实例的场景。我得让用户明白,虽然每次创建新实例,但对于无状态的服务来说,这并不会带来性能问题。
五、依赖注入的方式
依赖注入三种方式
- 构造函数注入
构造函数注入是最常见和推荐的依赖注入方式。通过构造函数注入,类的依赖项在创建实例时通过构造函数参数传递。
public class OrderService
{private readonly IOrderRepository _orderRepository;private readonly ILogger<OrderService> _logger;public OrderService(IOrderRepository orderRepository, ILogger<OrderService> logger){_orderRepository = orderRepository;_logger = logger;}public void ProcessOrder(Order order){_logger.LogInformation("Processing order: {OrderId}", order.Id);_orderRepository.SaveOrder(order);}
}
优点:确保对象在创建时所有依赖项都已就绪。
缺点:对于具有许多依赖项的类,构造函数可能会变得臃肿。
2. 属性注入
属性注入通过设置类的属性来注入依赖项。这种方式适用于可选依赖项或配置。
public class ReportGenerator
{public IExportService ExportService { get; set; }public INotificationService NotificationService { get; set; }public ReportGenerator(){}public void GenerateAndExportReport(){var report = GenerateReport();ExportService.Export(report);NotificationService.SendNotification("Report generated and exported.");}
}
优点:允许在对象创建后动态设置依赖项。
缺点:依赖项可能在使用前未被设置,导致运行时错误。
3. 方法注入
方法注入通过方法参数传递依赖项。这种方式适用于临时依赖项或方法级别的依赖。
public class ReportService
{public void GenerateReport(IReportTemplate template){template.ApplyFormatting();}
}
优点:适用于短期或方法级别的依赖项。
缺点:每次调用方法都需要传递依赖项,可能导致代码重复。
4.总结
构造函数注入:最适合大多数场景,确保对象创建时依赖项就绪。
属性注入:适用于可选依赖项或配置。
方法注入:适用于临时或方法级别的依赖项。