1、前言
面向對象設計(OOD)裡有一個重要的思想就是依賴倒置原則(DIP),并由該原則牽引出依賴注入(DI)、控制反轉(IOC)及其容器等概念。在學習Core依賴注入、服務生命周期之前,下面讓我們先了解下依賴倒置原則(DIP)、依賴注入(DI)、控制反轉(IOC)等概念,然後再深入學習Core依賴注入服務。
2、依賴倒置原則(Dependency Inversion Principle, DIP)
抽象不應該依賴于細節,細節應當依賴于抽象,高層子產品不依賴于低層子產品的實作,而低層子產品依賴于高層子產品定義的接口。一般來講,就是高層子產品定義接口,低層子產品負責具體的實作。針對接口程式設計而不是針對細節程式設計
3、什麼是依賴注入(Denpendency Injection)
3.1、依賴
人與人之間都有依賴(尤其我,就是離不開女人哈哈)何況軟體呢?所謂依賴就是:當一個類需要另一個類協作來完成工作的時候就産生了依賴。比如使用者登入,我們在控制器中UserController要完成使用者登入、注冊、修改密碼等等事情、其中操作到資料庫的(登入)我們用EF來完成,這裡我們封裝了一個EFLogin,這裡的UserController就有一個ILogin的依賴。需要知道的是這裡依賴于一個抽象為不是具體的某一個實作,是以給EFLogin定義了一個接口ILogin抽象了EFLogin的行為

3.2、注入
注入展現的是一個IOC(控制反轉的的思想)。
public interface IUser
{
string BB();
}
public class User : IUser
{
public string BB()
{
return "LP整天隻會BB";
}
}
public class ShowInfo
{
IUser user = new User();
public void UserBB()
{
user.BB();
}
}
當我們調用ShowInfo的時候,是通過IUser接口執行個體化一個User類去實作其方法的這叫控制正傳, 但是大濕兄說,我們不應該建立User類,而是讓調用者給你傳遞,于是你通過構造函數讓外界把這兩個依賴給你。把依賴的建立丢給其它人。自己隻負責使用,其它人丢給你依賴的這個過程了解為注入其它人丢給你依賴的這個過程了解為注入。也叫控制反轉(IOC)。
public interface IUser
{
string BB();
}
public class User : IUser
{
public string BB()
{
return "LP整天隻會BB";
}
}
public class ShowInfo2
{
private readonly IUser _user;
public ShowInfo2 (IUser user)
{
_user = user;
}
public void UserBB()
{
_user.BB();
}
}
3.3、為什麼要使用依賴注入?
使用依賴注入我們可以很好的管理類跟類之間的依賴,在我們設計應用程式的時候遵循這幾原則,確定代碼的可維護性和擴充性;另外在Core的架構中依賴注入提供了對象建立和生命周期管理的核心能力,各個元件之間的互相協作也是由依賴注入架構來實作的
4、服務生命周期
在ConfigureServices方法中的容器注冊每個應用程式的服務,Asp.Core都可以為每個應用程式提供三種服務生命周期:
Transient(暫時):每次請求都會建立一個新的執行個體。這種生命周期最适合輕量級,無狀态服務。
Scoped(作用域):在同一個作用域内隻初始化一個執行個體 ,可以了解為每一個請求隻建立一個執行個體,同一個請求會在一個作用域内。在Scooped的生存周期内,如果容器釋放 它也就被釋放了
Singleton(單例):整個應用程式生命周期以内隻建立一個執行個體,後續每個請求都使用相同的執行個體。如果應用程式需要單例行為,建議讓服務容器管理服務的生命周期,而不是在自己的類中實作單例模式。
為了示範生命周期和注冊選項之間的差異,請考慮以下代碼:
IGuid接口傳回一個Guid
public interface IGuid
{
Guid GetGuid { get; }
}
接口IScopedService、ISingletonService、ITransientService、都繼承接口IGuid
public interface IScopedService:IGuid
{
}
public interface ISingletonService: IGuid
{
}
public interface ITransientService: IGuid
{
}
GuidShow類繼承接口IScopedService、ISingletonService、ITransientService
public class GuidShow : IScopedService, ISingletonService, ITransientService
{
public GuidShow() : this(Guid.NewGuid())
{
}
public GuidShow(Guid id)
{
GetGuid = id;
}
public Guid GetGuid { get; private set; }
}
在Starup裡面注冊
public void ConfigureServices(IServiceCollection services)
{
#region//注冊不同生命周期的服務
services.AddSingleton<ISingletonService, SingletonService>();
services.AddTransient<ITransientService, TransientService>();
services.AddScoped<IScopedService, ScopedService>();
#endregion
services.AddControllers();
}
在WeatherForecastController Api裡寫一個Api
FromServices就是從容器裡面擷取我們的對象 每個對象都擷取兩邊來來對比每個生命周期是怎麼樣的
[ApiController]
[Route("[controller]/[action]")]
//路由
//API
[HttpGet]
public string GetService(
[FromServices] IScopedService scoped1, [FromServices] IScopedService scoped2,
[FromServices] ITransientService transient1, [FromServices] ITransientService transient2,
[FromServices] ISingletonService singleton, [FromServices] ISingletonService singleton2)
{
Console.WriteLine();
Console.WriteLine();
Console.WriteLine($"作用域1-->{scoped1.GetGuid}");
Console.WriteLine($"作用域2-->{scoped2.GetGuid}");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine($"瞬時1-->{transient1.GetGuid}");
Console.WriteLine($"瞬時2-->{transient2.GetGuid}");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine($"單例1-->{singleton.GetGuid}");
Console.WriteLine($"單例2-->{singleton2.GetGuid}");
Console.WriteLine("===========分割線=====================");
Console.WriteLine();
Console.WriteLine();
return "成功";
}
修改應用程式啟動
啟動應用程式
可以看出來單例跟作用域的都是一樣的Guid 隻有瞬時的不一樣 再次重新整理浏覽器
單例的沒有改變是以
Transient(暫時):每次調用服務的時候都會建立一個新的執行個體
Scoped(作用域):一次請求(Action)内對象執行個體是相同的,但每次請求會産生一個新執行個體。
Singleton(單例):首次請求初始化同一個執行個體,後續每次請求都使用同一個執行個體。相當于在整個應用Application中隻執行個體化一次執行個體,常見的單例模式。
下面是其他的注冊
#region//工程模式注冊 單例作用域、瞬時 都可以用
services.AddSingleton<ISingletonService>(s=> {
return new SingletonService();
});
#region//嘗試注冊
//注冊過了就不在注冊了
//using Microsoft.Extensions.DependencyInjection.Extensions;
services.TryAddScoped<IScopedService, ScopedService>();
#endregion
#region//移除注冊 移除所有IScopedService的注冊 不同實作的
services.RemoveAll<IScopedService>(); #endregion
注冊泛型 先寫一個泛型類
public interface ITypeT<T>
{
}
public class TypeT<T> : ITypeT<T>
{
public T GetT { get; }
public TypeT(T getT)
{
this.GetT = getT;
}
}
建立一個api Test
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
public ITypeT<IScopedService> _typeT;
public TestController(ITypeT<IScopedService> typeT)
{
_typeT = typeT;
}
[HttpGet]
public string TestGet()
{
return _typeT.GetHashCode().ToString();
}
}
注冊一下 裡面具體的參數不用謝 實作的時候隻要帶入某個具體的類就可以了,第一個參數服務的額類型,第二個參數服務的實作類型
services.AddScoped(typeof(ITypeT<>),typeof(TypeT<>));
位址欄輸入https://localhost:5001/api/test/testGet
看斷點
GetT他得到的是ScopedService
5、依賴注入的方式
5.1、構造函數注入
我們可以在定義的Controller中以構造函數注入的方式注入所需的服務。他的服務是大部分接口都需要的話就用它
public ITypeT<IScopedService> _typeT;
public TestController(ITypeT<IScopedService> typeT)
{
_typeT = typeT;
}
5.2、FromServices
上面的GetService就是這種方式注入的,這個服務隻是在某一個接口下用FromServices
當然還有其他的注入方式就不在研究了。
原文連結:https://www.cnblogs.com/w5942066/p/12808405.html
版權聲明:本文為
魏楊楊原創文章并釋出到部落格園, 除了【萬仟網】外, 其他平台歡迎轉載,但必須在文章頁面明顯位置寫明作者和出處,非常感謝。技術交流QQ群 99210270
微信掃一掃關注我公衆号
一起學習,一起進步