基礎才是重中之重~泛型類的靜態構造方法可不是隻執行一次呀

張佔嶺發表於2015-01-14

回到目錄

最近做了一個資料庫的讀寫分離專案,使用到了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物件中都只會出現一次,再也不會出現一個攔截器被執行多次的情況了,呵呵。

回到目錄

相關文章