依赖项是指另一个对象所依赖的对象。 使用其他类所依赖的 WriteMessage 方法检查以下 MyDependency 类:

 
public class MyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");
    }
}

类可以创建 MyDependency 类的实例,以便利用其 WriteMessage 方法。 在以下示例中,MyDependency 类是 IndexModel 类的依赖项:

 
public class IndexModel : PageModel
{
    private readonly MyDependency _dependency = new MyDependency();

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet");
    }
}

该类创建并直接依赖于 MyDependency 类。 代码依赖项(如前面的示例)会产生问题,应避免使用,原因如下:

  • 要用不同的实现替换 MyDependency,必须修改 IndexModel 类。
  • 如果 MyDependency 具有依赖项,则必须由 IndexModel 类对其进行配置。 在具有多个依赖于 MyDependency 的类的大型项目中,配置代码将分散在整个应用中。
  • 这种实现很难进行单元测试。

依赖关系注入通过以下方式解决了这些问题:

  • 使用接口或基类将依赖关系实现抽象化。
  • 在服务容器中注册依赖关系。 ASP.NET Core 提供了一个内置的服务容器 IServiceProvider。 服务通常已在应用的 Program.cs 文件中注册。
  • 将服务注入到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时将其释放。

 

在示例应用中, 接口定义 WriteMessage 方法:

public interface IMyDependency
{
    void WriteMessage(string message);
}

此接口由具体类型 MyDependency 实现:

public class MyDependency : IMyDependency
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

示例应用使用具体类型 MyDependency 注册 IMyDependency 服务。 AddScoped 方法使用范围内生存期(单个请求的生存期)注册服务。

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddScoped<IMyDependency, MyDependency>();

var app = builder.Build();

在示例应用中,请求 IMyDependency 服务并用于调用 WriteMessage 方法:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

    public Index2Model(IMyDependency myDependency)
    {
        _myDependency = myDependency;            
    }

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

通过使用 DI 模式,控制器或 Razor 页面:

  • 不使用具体类型 MyDependency,仅使用它实现的 IMyDependency 接口。 这样可以轻松地更改实现,而无需修改控制器或 Razor 页面。
  • 不创建 MyDependency 的实例,这由 DI 容器创建。

 

这样就做到了完美解耦,我们的控制器也就不再依赖于某个具体的对象了。

我们书写这样的代码,也符合设计模式中的:继承原则,单一职责原则,开放封闭原则,依赖倒转原则

上述说的设计模式原则简单介绍下:

关于继承无需多说

所谓单一职责原则是指:就一个类而言,应该仅有一个引起它变化的原因

所谓开闭原则是指:对于扩展是开放的,对于修改是封闭的(ASD原则)

依赖倒转原则是指:高层不应该依赖底层模块(强内聚,松耦合),就上述代码中的控制器(IndexModel、Index2Model)属于高层模块,接口及其实现类,服务注册类/方法(startup.cs中的ConfigureServices)属于底层模块。

 

参考博客:https://www.cnblogs.com/chenwolong/p/Scoped.html

微软文档:https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0