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

背景介绍

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!

本篇源代码 (本地下载)

总结

以上是 在.NET Core控制台程序中如何使用依赖注入详解 的全部内容, 来源链接: utcz.com/z/332832.html

回到顶部