如何在.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 FooService FooService
在以上實現類程式碼中,我們使用了.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!
ofollow,noindex" target="_blank">本篇原始碼