十年河東,十年河西
莫欺少年窮
NetCore中依賴注入無處不在,關於依賴注入的好處,想必大家都能想到二個字:解耦
但依賴注入是如何做到解耦的呢?
下面以具體實例來描述,如下:
首先,在項目中創建一個發送消息的接口及實現類

public interface IMessage { string SendMessage(); } /// <summary> /// 傳真發送消息類 /// </summary> public class MessageService_ChuanZhen:IMessage { public string SendMessage() { return "90年代的我使用傳真發送消息"; } }
傳統的方式是這樣調用此方法的

public class MessageController : Controller { IMessage service = new MessageService_ChuanZhen(); public ViewResult Index() { var result = service.SendMessage(); return View(); } }
代碼上沒有任何問題,但隨着時代的發展,傳真發送消息過時了,現在需要使用郵件的方式發送消息,那么我們的實現如下:
增加郵件發送類、

/// <summary> /// 郵箱發送消息類 /// </summary> public class MessageService_Email : IMessage { public string SendMessage() { return "21世紀我使用郵件發送消息"; } }
修改控制器代碼,如下:

public class MessageController : Controller { IMessage service = new MessageService_Email(); public ViewResult Index() { var result = service.SendMessage(); return View(); } }
從上述代碼可以看出,我們要向發送消息,就必須完全依賴創建的Service對象,
首先在startpUP.cs中注冊服務,如下:

public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IMessage, MessageService_ChuanZhen>(); services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
然后在控制器中注入服務對象,如下:

public class MessageController : Controller { private readonly IMessage _messageService; public MessageController(IMessage MessageService) { _messageService = MessageService; } public ViewResult Index() { var result = _messageService.SendMessage(); return View(); } }
根據上述代碼,無論你將來增加多少中通信方式,我的控制器都不依賴於具體的service對象,我要要做的就是擴展我們的接口實現類及在startUP.cs中的ConfigureServices方法中重新注冊服務即可,在這里,我們可以將ConfigureServices方法看做一個注接口對應冊服務類的大容器,你需要什么服務,你就去注冊好了,客戶端無需修改任何代碼
譬如,現在我們需要將傳真方式修改為郵件方式,只需修改下我們注冊的服務
將
services.AddSingleton<IMessage, MessageService_ChuanZhen>();
修改為:
services.AddSingleton<IMessage, MessageService_Email>();
這樣就做到了完美解耦,我們的控制器也就不再依賴於bou某個具體的對象了。
我們書寫這樣的代碼,也符合設計模式中的:繼承原則,單一職責原則,開放封閉原則,依賴倒轉原則。
上述說的設計模式原則簡單介紹下:
關於繼承無需多說
所謂單一職責原則是指:就一個類而言,應該僅有一個引起它變化的原因
所謂開閉原則是指:對於擴展是開放的,對於修改是封閉的(ASD原則)
依賴倒轉原則是指:高層不應該依賴底層模塊(強內聚,松耦合),就想上述代碼中的控制器屬於高層模塊,接口及其實現類,服務注冊類/方法(startup.cs中的ConfigureServices)屬於底層模塊。
截止到這兒,我們就把依賴注入的好處說完了,下面介紹下本文的重點,NetCore的三種不同類型的對象
netcore提供了三種不同類型的對象,分別為:全局單例對象(AddSingleton),作用域單例對象(AddScoped)、臨時對象(AddTransient)。
具體還是結合代碼來說明:
首先創建接口,如下:

/// <summary> /// 全局的 /// </summary> public interface ITestService_Singleton { Guid MyProperty { get; } } /// <summary> /// 作用域內的 /// </summary> public interface ITestService_Scoped { Guid MyProperty { get; } } /// <summary> /// 臨時的 /// </summary> public interface ITestService_Transient { Guid MyProperty { get; } }
其次,創建接口的實現類,如下:

public class TestService_Singleton : ITestService_Singleton { public TestService_Singleton() { MyProperty = Guid.NewGuid(); } public Guid MyProperty { get; set; } } public class TestService_Scoped : ITestService_Scoped { public TestService_Scoped() { MyProperty = Guid.NewGuid(); } public Guid MyProperty { get; set; } } public class TestService_Transient : ITestService_Transient { public TestService_Transient() { MyProperty = Guid.NewGuid(); } public Guid MyProperty { get; set; } }
然后,將接口與實現類注冊在startUp類中,如下:

public void ConfigureServices(IServiceCollection services) { services.Configure<MyOptions>(Configuration); //全局單例對象 全局單例 services.AddSingleton<ITestService_Singleton, TestService_Singleton>(); //作用域內單例對象 作用域內不會重新創建 services.AddScoped<ITestService_Scoped, TestService_Scoped>(); //臨時對象,每次都回重新創建 services.AddTransient<ITestService_Transient, TestService_Transient>(); // services.AddSingleton<IMessage, MessageService_Email>(); services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
最后,我們書寫控制器代碼,如下:

public class HomeController : Controller { /// <summary> /// 全局的 /// </summary> private ITestService_Singleton _singletonService; /// <summary> /// 作用域內的 /// </summary> private ITestService_Scoped _scopedService; /// <summary> /// 臨時的 /// </summary> private ITestService_Transient _transientService; public HomeController(ITestService_Singleton SingletonService , ITestService_Scoped ScopedService , ITestService_Transient TransientService) { _singletonService = SingletonService; _scopedService = ScopedService; _transientService = TransientService; } /// <summary> /// //這里采用了Action注入的方法 /// </summary> /// <param name="singletonService_2"></param> /// <param name="ScopedService_2">保證和_scopedService在同一個作用域</param> /// <param name="TransientService_3"></param> /// <returns></returns> public IActionResult Index([FromServices]ITestService_Singleton singletonService_2, [FromServices]ITestService_Scoped ScopedService_2,[FromServices]ITestService_Transient TransientService_2) { ViewData["Message_1"] = "全局對象生成的GUID:" + _singletonService.MyProperty; ViewData["Message_12"] = "全局對象生成的GUID:" + singletonService_2.MyProperty; ViewData["Message_2"] = "作用域內對象生成的GUID:" + _scopedService.MyProperty; ViewData["Message_22"] = "作用域內對象生成的GUID:" + ScopedService_2.MyProperty; ViewData["Message_3"] = "臨時對象生成的GUID:" + _transientService.MyProperty; ViewData["Message_32"] = "臨時對象生成的GUID:" + TransientService_2.MyProperty; return View(); } }
執行程序,通過結果,我們來分析:
由上圖可以看出,全局單例的對象生成的GUID每次都是同一個結果。
作用域生成的結果,只有在同一作用域時才會一樣
臨時對象每次都不一樣。
參考博客:
https://www.cnblogs.com/zhangzhiping35/p/11058761.html
https://www.cnblogs.com/GuZhenYin/p/8297145.html
https://www.cnblogs.com/chenwolong/p/yz.html
@天才卧龍的博客