一、Autofac中的生命周期相關概念
服務的生命周期:是服務實例在您的應用程序中生存的時間 ,即從原始實例化到釋放期間。例如,如果你“新建”了一個實現了IDisposable
的對象,然后再調用Dispose()
,那么這個對象的生命周期就是從你實例化的時候開始,被釋放時結束(或者垃圾回收,如果你沒有主動處置它)。
服務范圍:應用程序中可以與其他使用該服務的組件共享該服務的區域。例如,在你的應用程序中你可能有一個全局靜態的單例 - 全局對象實例的“范圍”將是整個應用程序。另一方面,您可以在使用全局單例的for循環中創建局部變量 - 局部變量的范圍比全局范圍小得多。
Autofac
中的生命周期概念:結合了這兩個概念。生命周期的范圍等同於您的應用程序中的一個工作單元。在解析服務問題時,Autofac
跟蹤已解析的一次性(IDisposable
)組件,在工作單元結束時,您將釋放關聯的生命周期范圍(scope),Autofac
將自動清理/處理已解析的服務。
生命周期管理的兩件重要的事情就是共享和清理。
我們來看一個Web應用程序作為更具體的例子來說明生命周期范圍的使用。 假設你有以下情況:
你有一個全局的單例日志記錄服務。
兩個並發請求進入Web應用程序。
每個請求都是一個邏輯的“工作單元”,每個請求都需要自己的訂單處理服務。
每個訂單處理服務都需要將日信息記錄到日志服務中。
在這種情況下,您將擁有包含單例記錄服務的根生存期范圍,並且每個請求都有一個子生命周期范圍,每個范圍都有自己的訂單處理服務:
+---------------------------------------------------+ | Autofac Container | | Root Lifetime Scope | | | | Logging Service | | ( 在所有請求中共享 ) | | | | +----------------------+ +----------------------+ | | | First Request Scope | | Second Request Scope | | | | | | | | | | Order Processor | | Order Processor | | | +----------------------+ +----------------------+ | +---------------------------------------------------+
When each request ends, the request lifetime scope ends and the respective order processor gets disposed. The logging service, as a singleton, stays alive for sharing by future requests.
當每個請求結束時,請求生命周期范圍(scope)被處理,相應的訂單處理服務被銷毀。 日志記錄服務作為一個單例對象,在將來的請求中保持共享。
二、創建一個新的生命周期范圍
您可以通過在任何現有生命周期作用域上從根容器開始調用BeginLifetimeScope()
方法來創建生命周期作用域。生命周期作用域是可銷毀的,他們跟蹤組件的處置,所以確保你總是調用“Dispose()”
或者把它們包裝在“using”
語句中。
1 using(var scope = container.BeginLifetimeScope()) 2 { 3 //從作為根容器子項的作用域來解析服務 4 var service = scope.Resolve<IService>(); 5 6 //您也可以創建嵌套的作用域... 7 using(var unitOfWorkScope = scope.BeginLifetimeScope()) 8 { 9 var anotherService = unitOfWorkScope.Resolve<IOther>(); 10 } 11 }
三、實例周期范圍
3.1 每個依賴一個實例(InstancePerDependency)
使用這個選項,每次請求服務都會返回一個新實例,這是默認選項。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>(); builder.RegisterType<Worker>().InstancePerDependency();
下面的代碼,每次循環都生成一個新的實例,一共生成 100 個實例。
1 using(var scope = container.BeginLifetimeScope()) 2 { 3 for(var i = 0; i < 100; i++) 4 { 5 //每次解析都獲取一個新實例 6 var w = scope.Resolve<Worker>(); 7 w.DoWork(); 8 } 9 }
3.2 單個實例(SingleInstance)
使用這個選項,在根范圍或嵌套范圍中請求服務,都返回同一個的實例。使用 SingleInstance() 指定。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>().SingleInstance();
下面的代碼,w1 和 w2 始終是同一個對象,100 次循環只有一個 Worker 類的實例。
using(var scope1 = container.BeginLifetimeScope()) { for(var i = 0; i < 100; i++) { var w1 = scope1.Resolve<Worker>(); using(var scope2 = scope1.BeginLifetimeScope()) { var w2 = scope2.Resolve<Worker>(); } } }
3.3 每個生命周期范圍一個實例 (InstancePerLifetimeScope)
使用這個選項,在特定的 ILifetimeScope 中請求服務,只返回一個實例。下面的代碼中,scope1 中的 100 次 w1 是同一個對象,scope2 中的 100 次 w2 是同一個對象,但是 w1 和 w2 不是同一個對象。
1 var builder = new ContainerBuilder(); 2 builder.RegisterType<Worker>().InstancePerLifetimeScope(); 3 using(var scope1 = container.BeginLifetimeScope()) 4 { 5 for(var i = 0; i < 100; i++) 6 { 7 var w1 = scope1.Resolve<Worker>(); 8 } 9 } 10 11 using(var scope2 = container.BeginLifetimeScope()) 12 { 13 for(var i = 0; i < 100; i++) 14 { 15 var w2 = scope2.Resolve<Worker>(); 16 } 17 }
3.4 每個匹配的生命周期范圍一個實例(InstancePerMatchingLifetimeScope)
類似於上面【每個生命周期范圍一個實例】,但可以提供更多控制。使用此選項,允許為 ILifetimeScope 對象提供“標記”。在標記匹配的范圍中只有一個實例。使用 InstancePerMatchingLifetimeScope() 方法指定。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("myscope");
下面的代碼中,w1 和 w2 相同,w3 和 w4 相同,但 w1 和 w3 不同。
1 using(var scope1 = container.BeginLifetimeScope("myscope")) 2 { 3 for(var i = 0; i < 100; i++) 4 { 5 var w1 = scope1.Resolve<Worker>(); 6 using(var scope2 = scope1.BeginLifetimeScope()) 7 { 8 var w2 = scope2.Resolve<Worker>(); 9 } 10 } 11 } 12 13 using(var scope3 = container.BeginLifetimeScope("myscope")) 14 { 15 for(var i = 0; i < 100; i++) 16 { 17 var w3 = scope3.Resolve<Worker>(); 18 using(var scope4 = scope1.BeginLifetimeScope()) 19 { 20 var w4 = scope4.Resolve<Worker>(); 21 } 22 } 23 }
3.5 每個請求一個實例( InstancePerRequest)
有些應用程序天然具有【請求】語義,例如 ASP.NET MVC 或 WebForm 應用程序。【每個請求一個實例】在【每個匹配的生命周期范圍一個實例】基礎上,通過提供范圍標記,注冊函數和常見類型集成實現。本質上是【每個匹配的生命周期范圍一個實例】。
var builder = new ContainerBuilder(); builder.RegisterType<Worker>().InstancePerRequest();
ASP.NET Core 使用【每個生命周期范圍一個實例】,而不是【每個請求一個實例】。
3.6 每個 Owned 一個實例 ( InstancePerOwned)
Owned<T> 隱式關聯類型創建嵌套的生命周期范圍。使用 instance-per-owned 注冊,可將依賴限定在 owned 實例中。
var builder = new ContainerBuilder(); builder.RegisterType<MessageHandler>(); builder.RegisterType<ServiceForHandler>().InstancePerOwned<MessageHandler>();
本例中 ServiceForHandler 服務會限制在 MessageHandler 實例范圍內。
using(var scope = container.BeginLifetimeScope()) { // MessageHandler 和附屬的 ServiceForHandler // 在 scope 下面的一個微型的 lifetime scope 中。 // 解析 Owned<T> 需要程序員負責執行清理工作。 var h1 = scope.Resolve<Owned<MessageHandler>>(); h1.Dispose(); }
3.7 線程范圍通過
InstancePerLifetimeScope,每個線程建立自己的LifetimeScope
var builder = new ContainerBuilder(); builder.RegisterType<Service>() .InstancePerLifetimeScope(); var container = builder.Build();
然后讓每個創建自己的 lifetime scope
void ThreadStart() { using (var threadLifetime = container.BeginLifetimeScope()) { var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>();
}
}
重要:在多線程場景下,要小心不要將父范圍清理掉。否則,派生線程中的子范圍將無法解析服務。
每個線程都將有自己的 MyThreadScopedComponent 實例,本質上是生命周期范圍內的單例。范圍內的實例不會提供到外部,因此很容易保持線程間的組件隔離。
通過添加 ILifetimeScope 參數,可將父范圍注入到生成線程的代碼中,Autofac 會將當前范圍自動注入,接下來可以使用它創建嵌套范圍。
1 public class ThreadCreator 2 { 3 //把父范圍注入生成線程的代碼 4 private ILifetimeScope _parentScope; 5 public ThreadCreator(ILifetimeScope parentScope) 6 { 7 this._parentScope = parentScope; 8 } 9 10 public void ThreadStart() 11 { 12 using (var threadLifetime = this._parentScope.BeginLifetimeScope()) 13 { 14 //開啟一個線程時,在嵌套scope中解析,以此實現線程間組件的隔離 15 var thisThreadsInstance = threadLifetime.Resolve<MyThreadScopedComponent>(); 16 } 17 } 18 }
參考文章:
1、https://blog.csdn.net/WuLex/article/details/78704903
2、http://www.yuanjiaocheng.net/Autofac/instance-scope.html
3、https://www.cnblogs.com/dongbeifeng/p/autofac-instance-scope.html