背景介绍
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!
本篇源代码 (本地下载)
总结
以上就是这篇文章的全部内容了, 希望本文的内容对大家的学习或者工作具有一定的参考学习价值, 如果有疑问大家可以留言交流, 谢谢大家对脚本之家的支持.
来源: https://www.jb51.net/article/148630.htm