日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《HiBlogs》重写笔记[1]--从DbContext到依赖注入再到自动注入

發布時間:2025/4/5 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《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

namespace DemoLibrary {public class TempDemo{BloggingContext bloggingContext;public TempDemo(BloggingContext bloggingContext){this.bloggingContext = bloggingContext;}//獲取DbContext的HashCodepublic int GetDBHashCode(){return bloggingContext.GetHashCode();}} }

然后在web里面也注入DbContext,并對比HashCode

public IActionResult Index() {// 獲取類庫中的DbContext實例Codevar code1 = tempDemo.GetDBHashCode();// 獲取web啟動項中DbContext實例Codevar code2 = bloggingContext.GetHashCode();return View(); }

效果圖:

由此可見通過注入得到的DbContext對象是同一個(起碼在一個線程內是同一個)

另外,我們還可以反面驗證通過new關鍵字實例DbContext對象在線程內不是同一個

為什么可以通過注入的方式得到線程內唯一(注入的原理)

這里不說注入的定義,也不說注入的好處有興趣可查看。我們直接來模擬實現注入功能。
首先我們定義一個接口IUser和一個實現類User

public interface IUser {string GetName(); } public class User : IUser {public string GetName(){return "農碼一生";} }

然后通過不同方式獲取User實例

第一種不用說大家都懂的
第二種和第三種我們看到使用到了DI類(自己實現的一個簡易注入"框架"),下面我們來看看DI類中的Resolve到底是個什么鬼

public class DI {//通過反射 獲取實例 并向上轉成接口類型public static IUser Resolve(string name){Assembly assembly = Assembly.GetExecutingAssembly();//獲取當前代碼的程序集return (IUser)assembly.CreateInstance(name);//這里寫死了,創建實例后強轉IUser}//通過反射 獲取“一個”實現了此接口的實例public static T Resolve<T>(){Assembly assembly = Assembly.GetExecutingAssembly();//獲取“第一個”實現了此接口的實例var type = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(T))).FirstOrDefault();if (type == null)throw new Exception("沒有此接口的實現");return (T)assembly.CreateInstance(type.ToString());//創建實例 轉成接口類型}

是不是想說“靠,這么簡單”。簡單的注入就這樣簡單的實現了。如果是相對復雜點的呢?比如我們經常會用到,構造注入里面的參數本身也需要注入。
比如我們再創建一個IUserService接口和一個UserService類

public interface IUserService {IUser GetUser(); }public class UserService : IUserService {private IUser _user;public UserService(IUser user){_user = user;}public IUser GetUser(){return _user;} }

我們發現UserService的構造需要傳入IUser,而IUser的實例使用也是需要注入IUser的實例。

這里需要思考的就是userService.GetUser()怎么可以得到IUser的實現類實例。所以,我們需要繼續看Resolve2的具體實現了。

public static T Resolve2<T>() {Assembly assembly = Assembly.GetExecutingAssembly();//獲取當前代碼的程序集//獲取“第一個”實現了此接口的實例(UserService)var type = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(T))).FirstOrDefault();if (type == null) throw new Exception("沒有此接口的實現");var parameter = new List<object>();//type.GetConstructors()[0]獲取第一個構造函數 GetParameters的所有參數(IUser接口)var constructorParameters = type.GetConstructors()[0].GetParameters();foreach (var constructorParameter in constructorParameters){//獲取實現了(IUser)這個接口類型(User)var tempType = assembly.GetTypes().Where(t => t.GetInterfaces().Contains(Type.GetType(constructorParameter.ParameterType.FullName))).FirstOrDefault();//并實例化成對象(也就是User實例) 添加到一個集合里面 供最上面(UserService)的注入提供參數 parameter.Add(assembly.CreateInstance(tempType.ToString()));}//創建實例,并傳入需要的參數 【public UserService(IUser user)】return (T)assembly.CreateInstance(type.ToString(), true, BindingFlags.Default, null, parameter.ToArray(), null, null);//true:不區分大小寫 }

仔細看了也不難,就是稍微有點繞。
既然知道了注入的原理,那我們控制通過方法A注入創建實例每次都是重新創建、通過方法B創建的實例在后續參數使用相同的實例、通過方便C創建的實例全局單例,就不是難事了。
以下偽代碼:

//每次訪問都是新的實例(通過obj1_1、obj1_2可以體現) public static T Transient<T>() {//var obj1_1 = assembly.CreateInstance(name);//var obj2 = assembly.CreateInstance(obj1_1,...)//var obj1_2 = assembly.CreateInstance(name);//var obj3 = assembly.CreateInstance(obj1_2,...)//var obj4 = assembly.CreateInstance(,...[obj2,obj3],...)//return (T)obj4; } //一次請求中唯一實例(通過obj1可以體現) public static T Scoped<T>() {//var obj1 = assembly.CreateInstance(name);//var obj2 = assembly.CreateInstance(obj1,...)//var obj3 = assembly.CreateInstance(obj1,...)//var obj4 = assembly.CreateInstance(,...[obj2,obj3],...)//return (T)obj4; } //全局單例(通過obj1 == null可以體現) public static T Singleton<T>() {//if(obj1 == null)// obj1 = assembly.CreateInstance(name);//if(obj2 == null)// obj2 = assembly.CreateInstance(obj1,...)//if(obj3 == null)// obj3 = assembly.CreateInstance(obj1,...)//if(obj4 == null)// obj4 = assembly.CreateInstance(,...[obj2,obj3],...)//return (T)obj4; }

通過偽代碼,應該不難理解怎么通過注入框架實現一個請求內實現DbContext的唯一實例了吧。
同時也應該更加深刻的理解了ASP.NET Core中對應的AddScoped、AddTransient、AddSingleton這三個方法和生命周期了吧。

在ASP.NET Core中實現自動注入

不知道你有沒有在使用AddScoped、AddTransient、AddSingleton這類方法的時候很煩。每次要使用一個對象都需要手動注入,每次都要到Startup.cs文件里面去做對應的修改。真是煩不勝煩。
使用過ABP的同學就有種感覺,那就是根本體會不到注入框架的存在。我們寫的接口和實現都自動注入了。使用的時候直接往構造函數里面扔就好了。那我們在使用ASP.NET Core的時候很是不是也可以實現類似的功能呢?
答案是肯定的。我們先定義這三種生命周期的標識接口,這三個接口僅僅只是做標記作用。(名字你可以隨意)

// 瞬時(每次都重新實例) public interface ITransientDependency //一個請求內唯一(線程內唯一) public interface IScopedDependency //單例(全局唯一) public interface ISingletonDependency

我們以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到依赖注入再到自动注入的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。