欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

在.NET Core控制台程序中如何使用依赖注入详解

程序员文章站 2024-02-22 20:14:46
背景介绍 dependency injection:又称依赖注入,简称di。在以前的开发方式中,层与层之间、类与类之间都是通过new一个对方的实例进行相互调用,这样在...

背景介绍

dependency injection:又称依赖注入,简称di。在以前的开发方式中,层与层之间、类与类之间都是通过new一个对方的实例进行相互调用,这样在开发过程中有一个好处,可以清晰的知道在使用哪个具体的实现。随着软件体积越来越庞大,逻辑越来越复杂,当需要更换实现方式,或者依赖第三方系统的某些接口时,这种相互之间持有具体实现的方式不再合适。为了应对这种情况,就要采用契约式编程:相互之间依赖于规定好的契约(接口),不依赖于具体的实现。这样带来的好处是相互之间的依赖变得非常简单,又称松耦合。至于契约和具体实现的映射关系,则会通过配置的方式在程序启动时由运行时确定下来。这就会用到di。

依赖注入(dependency injection), 是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。在.net core mvc中

我们可以在startup.cs文件的configureservice方法中使用服务容器iservicecollection注册接口及其实现类的映射。

例如,当我们需要访问http上下文时,我们需要配置ihttpcontextaccessor接口及其实现类httpcontextaccessor

 public void configureservices(iservicecollection services)
 {
 services.addmvc().setcompatibilityversion(compatibilityversion.version_2_1);
 services.addsingleton<ihttpcontextaccessor, httpcontextaccessor>();
 }

那么当我们编写一个.net core控制台程序的时候,我们该如何使用依赖注入呢?

使用内置依赖注入

在.net core中,内置依赖注入模块使用的程序集是microsoft.extensions.dependencyinjection

所以如果希望在控制台程序中使用内置依赖注入,我们首先需要使用nuget添加对microsoft.extensions.dependencyinjection程序集的引用。

pm> install-package microsoft.extensions.dependencyinjection

这里为了说明如何使用.net core内置的依赖注入模块, 我们创建以下2个服务接口。

 public interface ifooservice
 {
 void dothing(int number);
 }

 public interface ibarservice
 {
 void dosomerealwork();
 }

然后我们针对这2个服务接口,添加2个对应的实现类

 public class barservice : ibarservice
 {
 private readonly ifooservice _fooservice;
 public barservice(ifooservice fooservice)
 {
  _fooservice = fooservice;
 }

 public void dosomerealwork()
 {
  for (int i = 0; i < 10; i++)
  {
  _fooservice.dothing(i);
  }
 }
 }

 public class fooservice : ifooservice
 {
 private readonly ilogger<fooservice> _logger;
 public fooservice(iloggerfactory loggerfactory)
 {
  _logger = loggerfactory.createlogger<fooservice>();
 }

 public void dothing(int number)
 {
  _logger.loginformation($"doing the thing {number}");
 }
 }

代码解释

  • barservice类构造函数依赖了一个ifooservice接口的实现
  • fooservice类构造函数依赖一个iloggerfactory接口的实现
  • fooservice中,我们输出了一个information级别的日志

在以上实现类代码中,我们使用了.net core内置的日志模块, 所以我们还需要使用nuget添加对应的程序集microsoft.extensions.logging.console

pm> install-package microsoft.extensions.logging.console

最后我们来修改program.cs, 代码如下

using microsoft.extensions.dependencyinjection;
using microsoft.extensions.logging;

 public class program
 {
 public static void main(string[] args)
 {
  //setup our di
  var serviceprovider = new servicecollection()
  .addlogging()
  .addsingleton<ifooservice, fooservice>()
  .addsingleton<ibarservice, barservice>()
  .buildserviceprovider();

  //configure console logging
  serviceprovider
  .getservice<iloggerfactory>()
  .addconsole(loglevel.debug);

  var logger = serviceprovider.getservice<iloggerfactory>()
  .createlogger<program>();
  logger.loginformation("starting application");

  //do the actual work here
  var bar = serviceprovider.getservice<ibarservice>();
  bar.dosomerealwork();

  logger.loginformation("all done!");

 }
 }

代码解释

  • 这里我们手动实例化了一个servicecollection类, 这个类是iservicecollection>接口的一个实现类,它就是一个.net core内置服务容器。
  • 然后我们在服务容器中注册了ifooservice接口的实现类fooservice以及ibarservice接口的实现类barservice。
  • 当时需要从服务容器中获取接口类的对应实现类时,我们只需要调用服务容器类的getserivce方法。

最终效果

运行程序,我们期望的日志,正确的输出了

info: diinconsoleapp.program[0]
      start application.
info: diinconsoleapp.fooservice[0]
      doing the thing 0
info: diinconsoleapp.fooservice[0]
      doing the thing 1
info: diinconsoleapp.fooservice[0]
      doing the thing 2
info: diinconsoleapp.fooservice[0]
      doing the thing 3
info: diinconsoleapp.fooservice[0]
      doing the thing 4
info: diinconsoleapp.fooservice[0]
      doing the thing 5
info: diinconsoleapp.fooservice[0]
      doing the thing 6
info: diinconsoleapp.fooservice[0]
      doing the thing 7
info: diinconsoleapp.fooservice[0]
      doing the thing 8
info: diinconsoleapp.fooservice[0]
      doing the thing 9
info: diinconsoleapp.program[0]
      all done!

使用第三方依赖注入

除了使用内置的依赖注入模块,我们还可以直接使用一些第三方的依赖注入框架,例如autofac, structuremap。

这里我们来使用structuremap来替换当前的内置的依赖注入框架。

首先我们需要先添加程序集引用。

pm> install-package structuremap.microsoft.dependencyinjection

然后我们来修改program.cs文件,代码如下

using microsoft.extensions.dependencyinjection;
using microsoft.extensions.logging;
using structuremap;
using system;

namespace diinconsoleapp
{
 class program
 {
 static void main(string[] args)
 {
  var services = new servicecollection().addlogging();

  var container = new container();
  container.configure(config =>
  {
  config.scan(_ =>
  {
   _.assemblycontainingtype(typeof(program));
   _.withdefaultconventions();
  });

  config.populate(services);
  });

  var serviceprovider = container.getinstance<iserviceprovider>();

  serviceprovider.getservice<iloggerfactory>().addconsole(loglevel.debug);

  var logger = serviceprovider.getservice<iloggerfactory>().createlogger<program>();
  logger.loginformation("start application.");

  var bar = serviceprovider.getservice<ibarservice>();
  bar.dosomerealwork();

  logger.loginformation("all done!");
  console.read();
 }
 }
}

代码解释

  • 这里我们实例化了一个structuremap的服务容器container, 并在其configure方法中配置了接口类及其实现类的自动搜索。这里使用的是一种约定,接口类必须以字母“i”开头, 实现类的名字和接口类只相差一个字母“i”, 例ifooservice, fooservice, ibarservice, barservice
  • 后续代码和前一个例子基本一样。虽然看起来代码多了很多,但是实际上这种使用约定的注入方式非常强力,可以省去很多手动配置的代码。

最终效果

运行程序,代码和之前的效果一样

info: diinconsoleapp.program[0]
      start application.
info: diinconsoleapp.fooservice[0]
      doing the thing 0
info: diinconsoleapp.fooservice[0]
      doing the thing 1
info: diinconsoleapp.fooservice[0]
      doing the thing 2
info: diinconsoleapp.fooservice[0]
      doing the thing 3
info: diinconsoleapp.fooservice[0]
      doing the thing 4
info: diinconsoleapp.fooservice[0]
      doing the thing 5
info: diinconsoleapp.fooservice[0]
      doing the thing 6
info: diinconsoleapp.fooservice[0]
      doing the thing 7
info: diinconsoleapp.fooservice[0]
      doing the thing 8
info: diinconsoleapp.fooservice[0]
      doing the thing 9
info: diinconsoleapp.program[0]
      all done!

本篇源代码 (本地下载

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。