基础才是重中之重~泛型类的静态构造方法可不是只执行一次呀

回到目录

最近做了一个数据库的读写分离项目,使用到了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对象中都只会出现一次,再也不会出现一个拦截器被执行多次的情况了,呵呵。

回到目录

时间: 2024-07-30 11:29:41

基础才是重中之重~泛型类的静态构造方法可不是只执行一次呀的相关文章

基础才是重中之重~再说面向接口的编程

之前在我的文章中有对接口进行过讲解,但感觉讲的还是不够清晰,不够利针见血,这次我把面向接口的编程里,自认为比较核心的两点说一下: 接口详细介绍请看我的这篇文章 基础才是重中之重~为什么C#有显示实现接口 一切依赖于抽象,而不是实现 多个接口相同的行为,被一个对象实现 #region 多个接口相同的行为,被一个对象实现(一切依赖于抽象,而不是实现) interface IPainter { void Draw(); } interface ICowBoy { void Draw(); } clas

C# 基础才是重中之重~对象的生与死

为何要写 之所以写这篇文章,完全是因为学生们在实际开发中遇到的问题,一个对象占用的内存空间总不被释放,导致系统内存不断攀升,其最主要原因是我们对“对象的生与死”不清楚,或者从来没有认真去考虑过这件事,确实一个对象在被声音,初始化,使用或者最后被系统回收,整个的过程与我们关系确实不大,我们开发人员直接用就行了,对于C#这种托管语言你没必要去自己回收它,但有时,我们多了解一点系统的回收机制,对我们的程序还是很有好处的. 对象的种类(根据作用域) 1 类对象,静态对象,使用static修饰符进行声明,

基础才是重中之重~对象的生与死

回到目录 为何要写 之所以写这篇文章,完全是因为学生们在实际开发中遇到的问题,一个对象占用的内存空间总不被释放,导致系统内存不断攀升,其最主要原因是我们对“对象的生与死”不清楚,或者从来没有认真去考虑过这件事,确实一个对象在被声音,初始化,使用或者最后被系统回收,整个的过程与我们关系确实不大,我们开发人员直接用就行了,对于C#这种托管语言你没必要去自己回收它,但有时,我们多了解一点系统的回收机制,对我们的程序还是很有好处的. 对象的种类(根据作用域) 1 类对象,静态对象,使用static修饰符

基础才是重中之重~这时应该用泛型方法了

回到目录 泛型方法:是一个抽象的概念,将批量具有共性的操作进行抽象,使用泛型类型来表示这个方法,实现这些类型的方法具有相同的逻辑,而唯一不同的是,它们的类型,即类型在泛型方法里是个变量,这话感觉是用肺说出来的,呵呵! 今天在做开发时,遇到了这个问题,最后重构了自己的代码,重构后,使用了泛型方法,感觉代码美丽多了 没用泛型方法前 /// <summary> /// 更新老师与学生的关系 /// </summary> /// <param name="list"

基础才是重中之重~内存里的堆和栈

内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区. 栈 先进后出(FILO—First-In/Last-Out) 就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数等.(C#里,值类型被存储在栈上) 堆 先进先出(FIFO—first in first out) 就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete.如果程序员没有释放掉,那么在程序结束

基础才是重中之重~Dictionary&lt;K,V&gt;里V的设计决定的性能

回到目录 字典对象Dictionary<K,V>我们经常会用到,而在大数据环境下,字典使用不当可能引起性能问题,严重的可能引起内在的溢出! 字典的值建议为简单类型,反正使用Tuple<T> 字典的键在查找时,时间复杂度为O(1),性能不会有任何问题,所以不要愿望它 下面代码是对500万的字典进行测试,首先赋值,然后取出一个随机机,性能在毫秒级 static void Draw() { int count = 5000000; Console.WriteLine("test

基础才是重中之重~Emit动态构建方法(参数和返回值)

回到目录 对于Emit我们知道它的可以动态构建程序集,类型,方法,属性等,或者说只要手动使用C#创建的东西使用Emit也都可以动态创建它们,Emit由于它的特别之处,所以在很多领域得到了广泛的应用,像最近比较火的AOP技术,它最核心的功能就是方法拦截了,我们使用Emit也是可以实现方法拦截功能的,详细可以看大叔这篇文章<Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP>. 有参数,没有返回值的方法构建与调用 [TestMethod] public voi

基础才是重中之重~用好configSections让配置信息更规范

对于小型项目来说,配置信息可以通过appSettings进行配置,而如果配置信息太多,appSettings显得有些乱,而且在开发人员调用时,也不够友好,节点名称很容易写错,这时,我们有几种解决方案 1 自己开发一个配置信息持久化类,用来管理配置信息,并提供面向对象的支持 2 使用.net自带的configSections,将配置信息分块管理,并提供实体类,便于开发人员友好的去使用它 本文主要说说第二种方案,它由实体类,实体类工厂及配置文件三个部分,看代码: 实体类设计: namespace C

基础才是重中之重~网站bin目录下的程序集自动加载

回到目录 网站bin目录下的程序集一般由系统项目,项目引用的外部DLL及外挂DLL组成,它们在网站运行时会自动加载,这一点很重要,项目本身DLL及项目引入的DLL会自动加载,这没有问题,而外挂在bin目录的DLL也会自动加载,这很重要,因为,它可以使我们的应用程序更加灵活,在开发通用功能上,也显得扩展性更强! 一个例子,比如一个HttpModule,它是一个通用的功能,向页面添加一些缓存过期的共用信息,这对于你所有网站都是共用的,这时,可以建立一个HttpModule项目,它代码可能是这样 na