如何在.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<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控制臺程序中使用依賴註入