最近做了一個資料庫的讀寫分離專案,使用到了DbCommand攔截器,在程式開發過程中沒有發現什麼特別的問題,而當開發完成後,在進行測試階段時,一個偶然的機會讓我發現了,原來我的攔截器注入不只是注入一次,而是每種型別的倉儲都會注入一次,這個問題事實上是相關嚴重的一件事,如果你的攔截器處理邏輯很多,那麼,這將是非常消耗效能的。
原因,靜態構造方法對泛型類不是唯一的,而是相互獨立的
public abstract class DbContextRepository<TEntity> : ISpecificationRepository<TEntity> where TEntity : class { #region Constructors /// <summary> /// 靜態構造方法對每個TEntity是獨立的,有多少型別初始化,這個方法就執行多少次 /// </summary> static DbContextRepository() { //泛型類的靜態構造方法... } }
結果,每個型別初始化時,都會向攔截器字典中新增一條
IRepository<WebManageUsers> userWrite = new BackgroundRepositoryBase<WebManageUsers>(db); IRepository<WebManageMenus> menuWrite = new BackgroundRepositoryBase<WebManageMenus>(db); //每次初始化,靜態構造方法都會被執行
事實上,這是可以理解的,因為泛型類本身就是未定義的,當你初始化它時,具體的型別才被執行時得知,這時,在第一次使用它時,“這個類”的靜態構造方法才會被執行,這是完全沒問題的,可能開發人員有時就忽略了這一點。
解決,使用反射來實現自己的按需新增
/// <summary> /// DbCommand攔截器擴充套件 /// </summary> public static class DbCommandInterceptorExtensions { /// <summary> /// 將DbCommand的攔截器以單例的形式新增到DbInterception靜態物件中 /// </summary> /// <param name="action"></param> public static void UsingSingletonInterceptor(DbCommandInterceptor interceptor) { #region SQL語句攔截器,攔截器只載入一次 var property = typeof(DbCommandDispatcher).GetProperty("InternalDispatcher", BindingFlags.Instance | BindingFlags.NonPublic); if (property != null) { var val = property.GetValue(System.Data.Entity.Infrastructure.Interception.DbInterception.Dispatch.Command); if (val != null) { var list = val.GetType().GetField("_interceptors", BindingFlags.Instance | BindingFlags.NonPublic); if (list != null) { var listVal = list.GetValue(val) as List<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor>; if (listVal != null) { if (listVal.FirstOrDefault(i => i.ToString() == interceptor.GetType().ToString()) == null) { System.Data.Entity.Infrastructure.Interception.DbInterception.Add(interceptor); } } } } } #endregion } }
呼叫這很方便
EntityFrameworks.Data.Core.Extensions.DbCommandInterceptorExtensions.UsingSingletonInterceptor(new CommandInterceptor());
OK,這樣我們的每個攔截器在DbInterception物件中都只會出現一次,再也不會出現一個攔截器被執行多次的情況了,呵呵。