《HiBlogs》重写笔记[1]--从DbContext到依赖注入再到自动注入
本篇文章主要分析DbContext的線程內唯一,然后ASP.NET Core的注入,再到實現自動注入。
DbContext為什么要線程內唯一(非線程安全)
我們在使用EF的時候,可能使用相關框架封裝過了,也可能是自己直接使用DbContext。但是有沒有想過怎么使用DbContext才是正確的姿勢呢?
DbContext可以訪問操作所有數據表、保持跟蹤狀態、SaveChanges統一提交等等強大的功能。我們會不會想,它的創建和銷毀是否要付出昂貴的代價?
其實不是的,DataContext 是輕量的,創建它不需要很大的開銷。
在EF6的DbContext文檔 https://msdn.microsoft.com/zh-cn/library/system.data.entity.dbcontext(v=vs.113).aspx 最下面有句話 此類型的任何公共 static(在 Visual Basic 中為 Shared) 成員都是線程安全的。但不保證所有實例成員都是線程安全的。DbContext實例不保證線程安全。也就是說多線程同時操作一個DbContext實例,可能會有意想不到的問題。
比如我前面的文章 http://www.cnblogs.com/zhaopei/p/async_two.html 遇到的問題就是如此
之所以本地iis和iis express測試都是沒問題,是因為本地訪問速度快,沒有并發。
更加極端點的體現,全局使用一個靜態DbContext實例(之前我就這么想過)。
比如:線程a正在修改一個實體到一半,線程b給不小心保存了。線程c在修改一個實體,線程d又把這個實體不小心刪了。這玩笑就開大了。并發越大,此類情況越多。所以DbContext實例只能被單個線程訪問。還有,在執行異步的方法的時候切不可能自認為的“效率提升”同時發起多個異步查詢。
當然,這也只是我個人認為可能存在的問題。不過你只要記住DbContext不是線程安全類型就夠了。
如此,我們是不是應該每次數據操作都應該實例一個新的DbContext呢?也不盡然。比如方法a中的DbContext實例查詢出實體修改跟蹤,并把實體傳入了方法b,而方法b是另外實例的DbContext,那么在方法b中就不能保存方法a傳過來的實體了。如果非得這么做方法b中的DbContext也應該由方法a傳過來。也就是說我們要的效果是線程內的DbContext實例唯一。
DbContext怎么做到線程內唯一(依賴注入)
在使用EF x時你可能是
public static BlogDbContext dbEntities {get{DbContext dbContext = CallContext.GetData("dbContext") as DbContext;if (dbContext == null){dbContext = new BlogDbContext();//將新創建的 ef上下文對象 存入線程CallContext.SetData("dbContext", dbContext);}return dbContext as BlogDbContext;} }而在EF Core中沒有了CallContext。其實我們不需要CallContext,通過自帶的注入框架就可以實現線程內唯一。
我們來寫個demo
首先創建一個類庫,通過注入得到DbContext。然后在web里面也注入一個DbContext,然后在web里面調用類庫里面的方法。驗證兩個DbContext的GetHashCode()值是否一致。
類庫內獲取DbContext的HashCode
然后在web里面也注入DbContext,并對比HashCode
public IActionResult Index() {// 獲取類庫中的DbContext實例Codevar code1 = tempDemo.GetDBHashCode();// 獲取web啟動項中DbContext實例Codevar code2 = bloggingContext.GetHashCode();return View(); }效果圖:
由此可見通過注入得到的DbContext對象是同一個(起碼在一個線程內是同一個)
另外,我們還可以反面驗證通過new關鍵字實例DbContext對象在線程內不是同一個
為什么可以通過注入的方式得到線程內唯一(注入的原理)
這里不說注入的定義,也不說注入的好處有興趣可查看。我們直接來模擬實現注入功能。
首先我們定義一個接口IUser和一個實現類User
然后通過不同方式獲取User實例
第一種不用說大家都懂的
第二種和第三種我們看到使用到了DI類(自己實現的一個簡易注入"框架"),下面我們來看看DI類中的Resolve到底是個什么鬼
是不是想說“靠,這么簡單”。簡單的注入就這樣簡單的實現了。如果是相對復雜點的呢?比如我們經常會用到,構造注入里面的參數本身也需要注入。
比如我們再創建一個IUserService接口和一個UserService類
我們發現UserService的構造需要傳入IUser,而IUser的實例使用也是需要注入IUser的實例。
這里需要思考的就是userService.GetUser()怎么可以得到IUser的實現類實例。所以,我們需要繼續看Resolve2的具體實現了。
仔細看了也不難,就是稍微有點繞。
既然知道了注入的原理,那我們控制通過方法A注入創建實例每次都是重新創建、通過方法B創建的實例在后續參數使用相同的實例、通過方便C創建的實例全局單例,就不是難事了。
以下偽代碼:
通過偽代碼,應該不難理解怎么通過注入框架實現一個請求內實現DbContext的唯一實例了吧。
同時也應該更加深刻的理解了ASP.NET Core中對應的AddScoped、AddTransient、AddSingleton這三個方法和生命周期了吧。
在ASP.NET Core中實現自動注入
不知道你有沒有在使用AddScoped、AddTransient、AddSingleton這類方法的時候很煩。每次要使用一個對象都需要手動注入,每次都要到Startup.cs文件里面去做對應的修改。真是煩不勝煩。
使用過ABP的同學就有種感覺,那就是根本體會不到注入框架的存在。我們寫的接口和實現都自動注入了。使用的時候直接往構造函數里面扔就好了。那我們在使用ASP.NET Core的時候很是不是也可以實現類似的功能呢?
答案是肯定的。我們先定義這三種生命周期的標識接口,這三個接口僅僅只是做標記作用。(名字你可以隨意)
我們以ISingletonDependency為例
/// 自動注入 /// </summary> private void AutoInjection(IServiceCollection services, Assembly assembly) {//獲取標記了ISingletonDependency接口的接口var singletonInterfaceDependency = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(ISingletonDependency))).SelectMany(t => t.GetInterfaces().Where(f => !f.FullName.Contains(".ISingletonDependency"))).ToList();//獲取標記了ISingletonDependency接口的類var singletonTypeDependency = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(ISingletonDependency))).ToList();//自動注入標記了 ISingletonDependency接口的 接口foreach (var interfaceName in singletonInterfaceDependency){var type = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(interfaceName)).FirstOrDefault();if (type != null)services.AddSingleton(interfaceName, type);}//自動注入標記了 ISingletonDependency接口的 類foreach (var type in singletonTypeDependency){ services.AddSingleton(type, type);}然后在Startup.cs文件的ConfigureServices方法里調用下就好了
public void ConfigureServices(IServiceCollection services) {var assemblyWeb = Assembly.GetExecutingAssembly();// 自動注入AutoInjection(services, assemblyApplication);這樣以后我們只要給某個接口和類定義了ISingletonDependency接口就會被自動單例注入了。是不是很酸爽!
什么?反射低效?別鬧了,這只是在程序第一次啟動的時候才運行的。
嗨-博客,的源代碼就是如此實現。
當然,給你一個跑不起來的Demo是很痛苦的,沒有對應源碼的博文看起來更加痛苦。特別是總有這里或那里有些細節沒注意,導致達不到和博文一樣的效果。
所以我又另外重寫了一個Demo。話說,我都這么體貼了你不留下贊了再走真的好嗎?如果能在github上送我顆星星就再好不過了!
?
-------------------- 更新 -------------------------
基于園友對關于DbContext能不能單次請求內唯一?DbContex需不需要主動釋放?:http://www.cnblogs.com/zhaopei/p/dispose-on-dbcontext.html
?
博文源碼
- 嗨博客,基于ASP.NET COre 2.0的跨平臺的免費開源博客 https://github.com/zhaopeiym/Hi-Blogs (求??)
- demo https://github.com/zhaopeiym/BlogDemoCode/tree/master/依賴注入/DIDemo
相關資料
- http://www.cnblogs.com/hjf1223/archive/2010/10/10/static_datacontext.html
- http://www.cnblogs.com/xishuai/p/ef-dbcontext-thread-safe.html
總結
以上是生活随笔為你收集整理的《HiBlogs》重写笔记[1]--从DbContext到依赖注入再到自动注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [译] NSCollectionView
- 下一篇: 新书发布《每天5分钟玩转Docker容器