参考:
https://www.cnblogs.com/zoro-zero/p/13490459.html
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
DI:是一种软件设计模式,Asp.net core支持该模式。这是一种在类及其依赖关系之间实现控制反转 (IoC) 的技术。
依赖项:指另一个对象所依赖的对象。
常见的DI框架:Autofac、Unity......
依赖关系注入概述—— 谁依赖谁,将谁注入谁
依赖注入是指在创建一个对象时,自动地创建它所依赖的对象,并注入。
依赖注入的方式:1.构造方法注入; 2.Set方法注入; 3.方法参数注入
1.构造方法注入:
优点:
- 在构造方法中体现出对其他类的依赖,直接就可以看出这个类所需要的依赖那些类才能工作。
- 脱离了IOC框架,这个类仍然可以工作。
- 一旦对象初始化成功了,这个对象的状态肯定是正确的。
缺点:
- 构造函数会有很多参数(Bad smell)。
- 有些类是需要默认构造函数的,比如MVC框架的Controller类,一旦使用构造函数注入,就无法使用默认构造函数。
- 这个类里面的有些方法并不需要用到这些依赖(Bad smell)。
2.Set方法注入
- 在对象的整个生命周期内,可以随时动态的改变依赖。
- 非常灵活。
- 对象在创建后,被设置依赖对象之前这段时间状态是不对的。
- 不直观,无法清晰地表示哪些属性是必须的。
3.方法参数注入--即以入参的方法,将对象赋值给方法内的对象
方法参数注入的意思是在创建对象后,通过自动调用某个方法来注入依赖。类似如下代码:
public class MovieRecommender { private MovieCatalog movieCatalog; public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... } private CustomerPreferenceDao customerPreferenceDao; public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
- 比较灵活。
- 新加入依赖时会破坏原有的方法签名,如果这个方法已经被其他很多模块用到就很麻烦。
- 与构造方法注入一样,会有很多参数。
依赖注入使用注意事项:
1.使用接口或基类将依赖关系实现抽象化
2.在服务容器中注册依赖关系。 ASP.NET Core 提供了一个内置的服务容器 IServiceProvider。 服务通常已在应用的 Startup.ConfigureServices
方法中注册。
3.将服务注入到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时将其释放。
如下所示:
using System; namespace Models { public interface IMyDependency { void WriteMessage(string message); } } using System; namespace Models { /// <summary> /// 示例应用使用具体类型 MyDependency 注册 IMyDependency 服务 /// </summary> public class MyDependency : IMyDependency { /// <summary> /// 传参注入 /// </summary> /// <param name="message">依赖项</param> public void WriteMessage(string message) { Console.WriteLine($"MyDependency.WriteMessage Message: {message}"); } } }
注:以上示例应用使用具体类型 MyDependency 注册 IMyDependency 服务; AddScoped 方法使用范围内生存期(单个请求的生存期)注册服务 public void ConfigureServices(IServiceCollection services) { services.AddScoped<IMyDependency, MyDependency>(); services.AddRazorPages(); }
在示例应用中,请求 IMyDependency 服务并用于调用 WriteMessage 方法: namespace Models.Model.Demo1 { public class OneModel { private readonly IMyDependency _myDependency; public OneModel(IMyDependency myDependency) { _myDependency = myDependency; } public void OnGet() { _myDependency.WriteMessage("OneModel.OnGet"); } } }
通过使用 DI 模式,表示控制器:
- 不使用具体类型
MyDependency
,仅使用它实现的IMyDependency
接口。 这样可以轻松地更改控制器使用的实现,而无需修改控制器。 - 不创建
MyDependency
的实例,这由 DI 容器创建。
可以通过使用内置日志记录 API 来改善 IMyDependency
接口的实现:using Microsoft.Extensions.Logging;
namespace Models.Model.Demo1 { public class MyDependency2 : IMyDependency { private readonly ILogger<MyDependency2> _logger; /// <summary> /// 构造注入 /// </summary> /// <param name="logger">依赖项</param> public MyDependency2(ILogger<MyDependency2> logger) { _logger = logger; } /// <summary> /// 传参注入 /// </summary> /// <param name="message">依赖项</param> public void WriteMessage(string message) { _logger.LogInformation($"MyDependency2.WriteMessage Message: {message}"); } } }
更新的 ConfigureServices 方法注册新的 IMyDependency 实现: public void ConfigureServices(IServiceCollection services) { services.AddScoped<IMyDependency, MyDependency2>(); } MyDependency2 依赖于 ILogger<TCategoryName>,并在构造函数中对其进行请求。 ILogger<TCategoryName> 是框架提供的服务
必须被解析的依赖关系的集合通常被称为“依赖关系树”、“依赖关系图”或“对象图”
容器通过利用(泛型)开放类型解析 ILogger<TCategoryName>
,而无需注册每个(泛型)构造类型。
在依赖项注入术语中,服务:
- 通常是向其他对象提供服务的对象,如
IMyDependency
服务。 - 与 Web 服务无关,尽管服务可能使用 Web 服务。