在.NET Core控制臺(tái)程序中如何使用依賴注入詳解
背景介紹
Dependency Injection:又稱依賴注入,簡(jiǎn)稱DI。在以前的開(kāi)發(fā)方式中,層與層之間、類與類之間都是通過(guò)new一個(gè)對(duì)方的實(shí)例進(jìn)行相互調(diào)用,這樣在開(kāi)發(fā)過(guò)程中有一個(gè)好處,可以清晰的知道在使用哪個(gè)具體的實(shí)現(xiàn)。隨著軟件體積越來(lái)越龐大,邏輯越來(lái)越復(fù)雜,當(dāng)需要更換實(shí)現(xiàn)方式,或者依賴第三方系統(tǒng)的某些接口時(shí),這種相互之間持有具體實(shí)現(xiàn)的方式不再合適。為了應(yīng)對(duì)這種情況,就要采用契約式編程:相互之間依賴于規(guī)定好的契約(接口),不依賴于具體的實(shí)現(xiàn)。這樣帶來(lái)的好處是相互之間的依賴變得非常簡(jiǎn)單,又稱松耦合。至于契約和具體實(shí)現(xiàn)的映射關(guān)系,則會(huì)通過(guò)配置的方式在程序啟動(dòng)時(shí)由運(yùn)行時(shí)確定下來(lái)。這就會(huì)用到DI。
依賴注入(Dependency Injection), 是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,可以用來(lái)減低代碼之間的耦合度。在.NET Core MVC中
我們可以在Startup.cs文件的ConfigureService方法中使用服務(wù)容器IServiceCollection注冊(cè)接口及其實(shí)現(xiàn)類的映射。
例如,當(dāng)我們需要訪問(wèn)Http上下文時(shí),我們需要配置IHttpContextAccessor接口及其實(shí)現(xiàn)類HttpContextAccessor
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
那么當(dāng)我們編寫一個(gè).NET Core控制臺(tái)程序的時(shí)候,我們?cè)撊绾问褂靡蕾囎⑷肽兀?/p>
使用內(nèi)置依賴注入
在.NET Core中,內(nèi)置依賴注入模塊使用的程序集是Microsoft.Extensions.DependencyInjection。
所以如果希望在控制臺(tái)程序中使用內(nèi)置依賴注入,我們首先需要使用NUGET添加對(duì)Microsoft.Extensions.DependencyInjection程序集的引用。
PM> Install-Package Microsoft.Extensions.DependencyInjection
這里為了說(shuō)明如何使用.NET Core內(nèi)置的依賴注入模塊, 我們創(chuàng)建以下2個(gè)服務(wù)接口。
public interface IFooService
{
void DoThing(int number);
}
public interface IBarService
{
void DoSomeRealWork();
}
然后我們針對(duì)這2個(gè)服務(wù)接口,添加2個(gè)對(duì)應(yīng)的實(shí)現(xiàn)類
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類構(gòu)造函數(shù)依賴了一個(gè)IFooService接口的實(shí)現(xiàn)
- FooService類構(gòu)造函數(shù)依賴一個(gè)ILoggerFactory接口的實(shí)現(xiàn)
- FooService中,我們輸出了一個(gè)Information級(jí)別的日志
在以上實(shí)現(xiàn)類代碼中,我們使用了.NET Core內(nèi)置的日志模塊, 所以我們還需要使用NUGET添加對(duì)應(yīng)的程序集Microsoft.Extensions.Logging.Console
PM> Install-Package Microsoft.Extensions.Logging.Console
最后我們來(lái)修改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!");
}
}
代碼解釋
- 這里我們手動(dòng)實(shí)例化了一個(gè)ServiceCollection類, 這個(gè)類是IServiceCollection>接口的一個(gè)實(shí)現(xiàn)類,它就是一個(gè).NET Core內(nèi)置服務(wù)容器。
- 然后我們?cè)诜?wù)容器中注冊(cè)了IFooService接口的實(shí)現(xiàn)類FooService以及IBarService接口的實(shí)現(xiàn)類BarService。
- 當(dāng)時(shí)需要從服務(wù)容器中獲取接口類的對(duì)應(yīng)實(shí)現(xiàn)類時(shí),我們只需要調(diào)用服務(wù)容器類的GetSerivce方法。
最終效果
運(yùn)行程序,我們期望的日志,正確的輸出了
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!
使用第三方依賴注入
除了使用內(nèi)置的依賴注入模塊,我們還可以直接使用一些第三方的依賴注入框架,例如Autofac, StructureMap。
這里我們來(lái)使用StructureMap來(lái)替換當(dāng)前的內(nèi)置的依賴注入框架。
首先我們需要先添加程序集引用。
PM> Install-Package StructureMap.Microsoft.DependencyInjection
然后我們來(lái)修改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();
}
}
}
代碼解釋
- 這里我們實(shí)例化了一個(gè)StructureMap的服務(wù)容器Container, 并在其Configure方法中配置了接口類及其實(shí)現(xiàn)類的自動(dòng)搜索。這里使用的是一種約定,接口類必須以字母“I”開(kāi)頭, 實(shí)現(xiàn)類的名字和接口類只相差一個(gè)字母“I”, 例IFooService, FooService, IBarService, BarService
- 后續(xù)代碼和前一個(gè)例子基本一樣。雖然看起來(lái)代碼多了很多,但是實(shí)際上這種使用約定的注入方式非常強(qiáng)力,可以省去很多手動(dòng)配置的代碼。
最終效果
運(yùn)行程序,代碼和之前的效果一樣
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!
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
C# 文件保存到數(shù)據(jù)庫(kù)中或者從數(shù)據(jù)庫(kù)中讀取文件
在編程中我們常常會(huì)遇到“將文件保存到數(shù)據(jù)庫(kù)中”這樣一個(gè)問(wèn)題,雖然這已不是什么高難度的問(wèn)題,但對(duì)于一些剛剛開(kāi)始編程的朋友來(lái)說(shuō)可能是有一點(diǎn)困難。2009-03-03
Visual Studio 2019 使用 Live Share的教程圖解
Visual Studio 2019 在今天發(fā)布(北京時(shí)間)了,這次帶來(lái)了一個(gè)比較有趣的 Live Share 功能,使用它可以進(jìn)行更好的協(xié)作開(kāi)發(fā)。這篇文章主要介紹了Visual Studio 2019 使用 Live Share的教程,需要的朋友可以參考下2019-04-04
詳解.NET Core使用Quartz執(zhí)行調(diào)度任務(wù)進(jìn)階
這篇文章主要介紹了.NET Core使用Quartz執(zhí)行調(diào)度任務(wù)進(jìn)階,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
JavaScript驗(yàn)證用戶輸入的是字符或數(shù)字及ASCII Chart應(yīng)用
我們可以根據(jù)onkeydown事件的event.keyCode即是ASCII Chart來(lái)判斷字符或數(shù)字等,本文提供了相關(guān)鍵盤key對(duì)應(yīng)的ASCII碼,以供用戶使用是參考及使用onpaste事件只能輸入字符和數(shù)字防止粘貼等的,感興趣的朋友可以了解下2013-01-01
.net中的session與cookies區(qū)別及使用方法
cookie數(shù)據(jù)存放在客戶的瀏覽器上,session數(shù)據(jù)放在服務(wù)器上,cookie不是很安全,別人可以分析存放在本地的COOKIE并進(jìn)行COOKIE欺騙,考慮到安全應(yīng)當(dāng)使用session2013-04-04
Asp.Net在線預(yù)覽Word文檔的解決方案與思路詳解
這篇文章主要介紹了Asp.Net在線預(yù)覽Word文檔的解決方案與思路,大致思路是先將Word文檔轉(zhuǎn)換Html,再預(yù)覽Html,需要注意電腦安裝Office,需要的朋友可以參考下2022-04-04

