.Net核心依赖项注入:生命周期和最佳实践

在这里插入图片描述
在讨论.Net的依赖注入(DI)之前,我们需要知道我们为什么需要使用依赖注入
依赖反转原理(DIP):
DIP允许您将两个类解耦,否则它们会紧密耦合,这有助于提高可重用性和更好的可维护性
DIP介绍:

  1. 高级模块不应依赖于低级模块。两者都应依赖抽象。
  2. 抽象不应依赖细节。细节应取决于抽象。

下面我们通过一个示例来探讨前者

class Foo {
  Foo(Car _car){
    //dosomething
  }
}

在上面的代码中,类Foo直接依赖于Car,当这两个类严重依赖时会导致两个问题

  1. Foo不能用不同的样式实例化car,即如果有新的汽车类型,例如:Audi来了,Foo则不能重复使用
  2. 每次修改Car类都会直接影响Foo类

为了避免这两个问题,DIP建议较高级别的模块Foo不应直接依赖于较低级别的模块,Car而应两者都依赖于抽象,例如:接口

class Foo {
  Foo(ICar _car){
    // something
  }
}
class Car : ICar {
  
}
class Sedan : ICar{
}

只需引入一个简单的抽象ICar,Foo它就可以兼容所有遵循契约或抽象的类ICar。
我们现在如何将当前方法使用依赖注入?
如果您需要修改较低级别的类,DIP可以提高代码的可重用性并限制波动效果。即使完美地实现了DIP,该接口也仅在较高级别的类中解耦了较低级别的类的用法,而不是其实例化。在代码的某些地方,您需要实例化接口的实现。这样可以防止您用另一种动态替换接口的实现。

依赖注入在这里发挥了作用,有助于从实例中区分使用实例。简而言之,只要DI框架在类中看到任何已注册服务的依赖项,它就会提供具体的实例化。

假设ICar已在DI框架中注册以提供的实例Car,则的构造函数将Foo始终Car为每个Foo对象实例接收一个实例

.NET中的DI:

在.NetCore框架之前,我们更多的是使用第三方DI框架,例如Autofac。但是,当.NetCore出现后。“Startup”类提供了一种称为的方法configureServices供我们将服务注册到容器内。

public class Startup
 {
    public void ConfigureServices(IServiceCollection services) {
    //服务注入
    services.AddTransient<ICar, Car>();
  }
}

因此,对于每个请求,将使用容器中解析的所有依赖项来获取实例。所有这些都可以在.Net core中使用,而无需复杂的配置。

服务注册的三种类型:

  1. Transient:需要时创建新实例
  2. Scoped:可以为每个新作用域创建一个新实例
  3. Singleton:第一个请求上创建一个新实例,并且在应用程序的剩余生命周期中,将相同的实例提供给所有使用者类。

推荐做法:

  1. 范围服务通常应由单个Web请求/线程使用。因此,不应该在线程之间共享服务范围。
  2. 配置为单例的服务可能会导致应用程序中的内存泄漏。
  3. 内存泄漏通常是由单例服务引起的。这是因为创建的实例不会被丢弃,它将保留在内存中直到应用程序结束。因此,一旦不使用它们,最好将它们释放。
  4. 将服务注册为临时服务会缩短其使用寿命,通常可能不太在乎多线程和内存泄漏。
  5. 不要在单例服务中依赖瞬态或作用域服务。因为瞬时服务在单例服务注入时成为一个单例实例,并且如果瞬态服务不旨在支持这种情况,则可能导致问题。在这种情况下,ASP.NET Core的默认DI容器已经引发异常。

依赖项注入技术使您可以进一步改进它。它提供了一种将对象的创建与使用分开的方法。这样,您可以在不更改任何代码的情况下替换依赖关系,这还可以减少业务逻辑中的代码。

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧