如何在.NET Core控制台程序中使用依赖注入
背景介绍
依赖注入(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
}
那么当我们编写一个.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
public FooService(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger
}
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
.AddSingleton
.BuildServiceProvider();
//configure console logging
serviceProvider
.GetService
.AddConsole(LogLevel.Debug);
var logger = serviceProvider.GetService
.CreateLogger
logger.LogInformation("Starting application");
//do the actual work here
var bar = serviceProvider.GetService
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
serviceProvider.GetService
var logger = serviceProvider.GetService
logger.LogInformation("Start application.");
var bar = serviceProvider.GetService
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.www.fengshen157.com/ FooService[0]
Doing the thing 5
info: DIInConsoleApp.FooService[0]
Doing the thing 6
info: DIInConsoleApp.www.baidu620.com/ FooService[0]
Doing the thing 7
info: DIInConsoleApp.www.thd540.com FooService[0]
Doing the thing 8
info: DIInConsoleApp.www.dasheng178.com FooService[0]
Doing the thing 9
info: DIInConsoleApp.Program[0]
All done!