
先來看一下Entity Framework緩慢的初始化速度給我們更新程序帶來的一種痛苦。
我們手動更新程序時通常的操作步驟如下:
1)把Web服務器從負載均衡中摘下來
2)更新程序
3)預熱(發出一個請求,完成程序的初始化)
4)把完成更新的Web服務器掛上負載均衡
在預熱階段,我們一般是向首頁(www.cnblogs.com)發出請求(首頁的加載沒有用到Entity Framework)。
如果僅這樣預熱后就將Web服務器上線,將會給部分用戶帶來糟糕的用戶體驗——比如,第1位在發布后推薦博文的用戶將會等待7秒鍾左右(推薦功能中使用了Entity Framework Code First)。
![]()
為了避免這樣的糟糕體驗,我們不得不在預熱時發出推薦博文的請求,等EF完成初始化后再發布。這不是一個好的解決方法,因為每個定義的DbContext類型都要進行這個初始化操作。
昨天,我們找到了一個更好的緩解這個問題的方法,在這篇博文中向大家分享一下。
為什么Entity Framework的初始化速度慢如蝸牛呢?
對於在應用程序中定義的每個DbContext類型,在首次使用時,Entity Framework都會根據數據庫中的信息在內存生成一個映射視圖(mapping views),而這個操作非常耗時。
using (var dbcontext = new CnblogsDbContext()) { //... }
比如上面的代碼,在第1次調用CnblogsDbContext進行數據庫操作時會進行緩慢的mapping views生成操作,后續的CnblogsDbContext操作會共享已經生成的mapping views,不受這個問題影響。但是要注意的是你定義的每一個DbContext都會面臨這個問題。
而我們的緩解之道則是在應用程序初始化時一次性觸發所有的DbContext進行mapping views的生成操作——調用StorageMappingItemCollection的GenerateViews()方法。
代碼如下(Entity Framework的版本至少是6.0才支持):
using (var dbcontext = new CnblogsDbContext()) { var objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext; var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); mappingCollection.GenerateViews(new List<EdmSchemaError>()); } //對程序中定義的所有DbContext逐一進行這個操作
對於ASP.NET應用程序 ,可以將上面的代碼放在Application_Start或者PreApplicationStartMethod中執行。
我們采用這個方法之后,在程序更新時只需發一個請求讓程序啟動起來,比如訪問首頁(www.cnblogs.com),就可以直接發布。而第1位進行推薦博文操作的用戶,等待時間由原來7秒減少到0.5-0.6秒。
![]()
【參考資料】
