zoukankan      html  css  js  c++  java
  • 第二节:框架前期准备篇之AutoFac常见用法总结

    一. 说在前面的话

      凡是大约工作在两年以上的朋友们,或多或少都会接触到一些框架搭建方面的知识,只要一谈到框架搭建这个问题或者最佳用法这个问题,势必会引起一点点小小的风波,我说我的好,他说他的好,非常容易骂架,所以在本节乃至该系列我仅仅是总结了一下自己日常中的一些用法,谈一下自己浅陋的见解,谈不上最佳,只要不误导新手 能有点帮助作用就可以了,如您不喜欢,请“右上角  谢谢”。
      在框架搭建过程中,在层与层的解耦方面,势必会涉及到IOC框架,.Net 平台下我用过的IOC框架主要是: Spring.Net 、Unity、AutoFac,当然还有Castle(我没用过,就不发表任何评论了), 在用过的IOC框架中,Spring.Net 相对很老了,貌似在2015年就不在更新了,但基本的功能也够用了。 现阶段用的最多的就是Unity和AutoFac了,版本更新也比较快,Unity大约一年前写过两篇文章了,本次在该框架系列也会考虑更新一下Unity,本节主要介绍一下AutoFac的几个基本用法。
      先说一下两个概念IOC和DI,我的理解:
      ① IOC:调用者不再创建(不自己new)被调用者的实例,而是交给容器去创建(AutoFac就充当这里的容器),这就是控制反转。
      ② DI:容器创建好的实例再注入调用者的过程,就是依赖注入(比如:属性注入、构造函数注入等)。
    AutoFac的信息:
      ① 官网地址:https://autofac.org/
      ② 官方文档:http://autofac.readthedocs.io/en/latest/index.html (英文)    https://autofaccn.readthedocs.io/zh/latest/ (中文)
      ③ 最新版本:4.8.1 (截止2018-08-21)

      本节的内容主要包括:

        1. 在使用IOC框架之前的几种创建对象的方式。

        2. AutoFac的基本用法和几种生命周期。

        3. AutoFac和Asp.Net MVC5进行整合,利用属性的方式进行注入。

    事先说明一下本节要用到的实现类和接口类:

    (1). Ypf.BLL层中包括:CatBLL、DogBLL、RoleBLL、UserBLL。

    1   public class CatBLL : IAnimalBLL
    2     {
    3         public string Introduce()
    4         {
    5             return "我是猫";
    6         }
    7     }
    CatBLL
    1   public class DogBLL : IAnimalBLL
    2     {
    3         public string Introduce()
    4         {
    5             return "我是狗";
    6         }
    7     }
    DogBLL
     1   public class RoleBLL : IRoleBLL
     2     {
     3 
     4         public IUserBLL userBLL { get; set; }
     5 
     6         /// <summary>
     7         /// 展示角色信息
     8         /// </summary>
     9         /// <returns></returns>
    10         public string ShowRoleInfor()
    11         {
    12             return "我是管理员角色";
    13         }
    14 
    15 
    16         public string ShowDIDemo()
    17         {
    18             return "哈哈:" + userBLL.GetUserInfor();
    19         }
    20 
    21     }
    RoleBLL
     1  public class UserBLL : IUserBLL,IPeopleBLL
     2     {
     3         /// <summary>
     4         /// 获取用户信息
     5         /// </summary>
     6         /// <returns></returns>
     7         public string GetUserInfor()
     8         {
     9             return "我是获取用户信息的方法";
    10         }
    11 
    12         /// <summary>
    13         /// 自我介绍
    14         /// </summary>
    15         /// <returns></returns>
    16         public string Introduce()
    17         {
    18             return "我是ypf";
    19         }
    20     }
    UserBLL

    (2). Ypf.IBLL层包括:IAnimalBLL、IPeopleBLL、IRoleBLL、IUserBLL。

    1  public interface IAnimalBLL
    2     {
    3         string Introduce();
    4     }
    IAnimalBLL
    1  public interface IPeopleBLL
    2     {
    3         //自我介绍
    4         string Introduce();
    5     }
    IPeopleBLL
    1   public interface IRoleBLL
    2     {
    3         string ShowRoleInfor();
    4 
    5         string ShowDIDemo();
    6 
    7     }
    IRoleBLL
    1  public interface IUserBLL
    2     {
    3         string GetUserInfor();
    4     }
    IUserBLL

    二. 引入IOC框架之前的几个写法

    1. 最原始的方式直接new(需添加对BLL层的引用)

    1 {
    2    UserBLL userBll = new UserBLL();
    3    var result1 = userBll.GetUserInfor();
    4    Console.WriteLine(result1);
    5 }

    2. 面向接口编程(仍需添加对BLL层的引用)

    1   {
    2      IUserBLL userBll = new UserBLL();
    3      var result1 = userBll.GetUserInfor();
    4      Console.WriteLine(result1);
    5   }

    3. 接口+反射(只需将BLL层的程序集拷贝进来)

     1 {
     2   Assembly ass = Assembly.Load("Ypf.BLL");
     3   Type type = ass.GetType("Ypf.BLL.UserBLL");
     4   //调用默认的无参构造函数进行对象的创建
     5   object myUserBLL = Activator.CreateInstance(type);
     6   IUserBLL userBLL = (IUserBLL)myUserBLL;
     7   var result1 = userBLL.GetUserInfor();
     8   Console.WriteLine(result1);
     9 
    10 }

    4. 手写IOC(反射+简单工厂+配置文件)【需将BLL层的程序集拷贝进来】

     配置文件代码:

      <appSettings>
        <!--直接修改配置文件,可以切换IUserBLL的实现类,发布后可以直接通过改配置文件,代码什么也不用改,体会:反射+面向接口编程-->
        <add key="DllName" value="Ypf.BLL"/>
        <add key="ClassName" value="Ypf.BLL.UserBLL"/>
      </appSettings>

    简单工厂代码:

     1     /// <summary>
     2     /// 简单工厂,隔离对象的创建
     3     /// </summary>
     4    public class SimpleFactory
     5     {
     6         private static string DllName = ConfigurationManager.AppSettings["DllName"];
     7         private static string ClassName = ConfigurationManager.AppSettings["ClassName"];
     8         public static IUserBLL CreateInstance()
     9         {
    10             Assembly ass = Assembly.Load(DllName);
    11             Type type = ass.GetType(ClassName);
    12             object obj = Activator.CreateInstance(type);
    13             return (IUserBLL)obj;
    14         }
    15     }

    调用代码:

    1 {
    2       IUserBLL userBLL = SimpleFactory.CreateInstance();
    3       var result = userBLL.GetUserInfor();
    4       Console.WriteLine(result);
    5 }

    三. AutoFac常见用法总结

    1. 基本用法

       同时添加对Ypf.BLL层和Ypf.IBLL层的引用,然后 声明容器→注册实例→解析对象→调用方法、进行测试,代码如下:

    1  {
    2      ContainerBuilder builder = new ContainerBuilder();
    3      //把UserBLL注册为IUserBLL实现类,当请求IUserBLL接口的时候,返回UserBLL对象
    4      builder.RegisterType<UserBLL>().As<IUserBLL>();
    5      IContainer resolver = builder.Build();
    6      IUserBLL userBLL = resolver.Resolve<IUserBLL>();
    7      var result1 = userBLL.GetUserInfor();
    8      Console.WriteLine(result1);
    9 }

      评价:这种用法单纯的是为了介绍AutoFac中的几个方法,仅此而已,在实际开发没有这么用的,坑比用法,起不到任何解耦的作用。

    2. AsImplementedInterfaces的用法

       在很多情况下,一个类可能实现了多个接口, 如果我们通过  builder.RegisterType<xxxBLL>().As<IxxxBLL>(); 这种方式按部就班排着把这个类注册给每个接口,实现几个接口,就要写几行注册代码,很繁琐,我们可以通过 AsImplementedInterfaces() 方法,可以把一个类注册给它实现的全部接口。

       这样的话,想用哪个接口,通过Resolve解析即可,代码如下:

     1 {
     2      ContainerBuilder builder = new ContainerBuilder();
     3      //这样请求UserBLL实现的任何接口的时候都会返回 UserBLL 对象。
     4      builder.RegisterType<UserBLL>().AsImplementedInterfaces();
     5      IContainer resolver = builder.Build();
     6      IUserBLL iUserBLL = resolver.Resolve<IUserBLL>();
     7      IPeopleBLL iPeopleBLL = resolver.Resolve<IPeopleBLL>();
     8 
     9      var r1 = iUserBLL.GetUserInfor();
    10      var r2 = iPeopleBLL.Introduce();
    11 
    12      Console.WriteLine(r1);
    13      Console.WriteLine(r2);
    14 }

      评价:同时添加对Ypf.BLL层和Ypf.IBLL层的引用,这里也是单纯的为了介绍AsImplementedInterfaces()的用法,还是存在实现类的身影,在实际开发中没有这么用的,起不到任何解耦的作用,坑比用法。

    3. AutoFac+反射(彻底消灭实现类)

      引入反射的背景:前面两种方式都需要添加对Ypf.BLL层的引用,麻烦的要死,根本没有什么改观,还是紧耦合在一起。并且如果有很多接口和实现类的话,用RegisterType一行一行的去写,累个半死,在这种情况下引入反射的概念,简化代码量,代码如下:

     1  {
     2       ContainerBuilder builder = new ContainerBuilder();
     3       //加载实现类的程序集
     4       Assembly asm = Assembly.Load("Ypf.BLL");
     5       builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();
     6       IContainer resolver = builder.Build();
     7 
     8       IUserBLL userBLL = resolver.Resolve<IUserBLL>();
     9       IPeopleBLL peopleBLL = resolver.Resolve<IPeopleBLL>();
    10       var r1 = userBLL.GetUserInfor();
    11       var r2 = peopleBLL.Introduce();
    12 
    13       Console.WriteLine(r1);
    14       Console.WriteLine(r2);
    15 }

      评价:彻底摆脱了实现类的身影,与Ypf.BLL层进行了解耦,只需要添加对Ypf.IBLL层的引用,但需要把Ypf.BLL的程序集拷贝到AutoFacTest项目下。

    小小的升级一下:

       把反射那个程序集类写到配置文件中,然后在代码中通过读取配置文件进行进一步的反射,代码如下:

    1  <appSettings>
    2     <add key="DllName" value="Ypf.BLL"/>
    3   </appSettings>
     1  {
     2      ContainerBuilder builder = new ContainerBuilder();
     3      //加载实现类的程序集
     4     string DllName = ConfigurationManager.AppSettings["DllName"];
     5     Assembly asm = Assembly.Load(DllName);
     6     builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();
     7     IContainer resolver = builder.Build();
     8 
     9     IUserBLL userBLL = resolver.Resolve<IUserBLL>();
    10     IPeopleBLL peopleBLL = resolver.Resolve<IPeopleBLL>();
    11     var r1 = userBLL.GetUserInfor();
    12     var r2 = peopleBLL.Introduce();
    13 
    14     Console.WriteLine(r1);
    15     Console.WriteLine(r2);
    16 }

    4. PropertiesAutowired(属性的自动注入)

      背景:一个实现类中定义了其他类型的接口属性,比如RoleBLL中定义IUserBLL的接口属性,而且要对其进行调用, 这个时候就需要通过PropertiesAutowired实现属性的自动注入了。

      注:只有通过AutoFac创建的对象才能实现属性的自动注入!! 相关的类、接口要是public类型。

     1  public class RoleBLL : IRoleBLL
     2     {
     3 
     4         public IUserBLL userBLL { get; set; }
     5 
     6         /// <summary>
     7         /// 展示角色信息
     8         /// </summary>
     9         /// <returns></returns>
    10         public string ShowRoleInfor()
    11         {
    12             return "我是管理员角色";
    13         }
    14 
    15 
    16         public string ShowDIDemo()
    17         {
    18             return "哈哈:" + userBLL.GetUserInfor();
    19         }
    20 
    21 
    22 
    23     }
    RoleBLL
     1 {
     2      ContainerBuilder builder = new ContainerBuilder();
     3      //加载实现类的程序集
     4      Assembly asm = Assembly.Load("Ypf.BLL");
     5      builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired();
     6      IContainer resolver = builder.Build();
     7 
     8      IRoleBLL iRoleBLL = resolver.Resolve<IRoleBLL>();
     9      var r1 = iRoleBLL.ShowDIDemo();
    10      Console.WriteLine(r1);
    }

      下面测试一下不是AutoFac创建的对象能否实现属性的自动注入,新建TempTest类,在里面声明IUserBLL属性,并且在方法中进行调用,然后new一个TempTest对象,对该showMsg方法进行调用,发现报空指针错误,说明userBLL属性为空,没能自动注入。

    1  public class TempTest
    2     {
    3         public IUserBLL userBLL { get; set; }
    4 
    5         public void showMsg()
    6         {
    7             Console.WriteLine(userBLL.GetUserInfor());
    8         }
    9     }
    1 //测试自己new的对象不能实现属性的自动注入
    2 //下面代码报空指针错误
    3 {
    4      TempTest t = new TempTest();
    5      t.showMsg();
    6 }

    5. 1个接口多个实现类的情况

      背景:1个接口有多个实现类的情况(DogBLL 和 CatBLL 都实现了 IAnimalBLL接口)

      分析:resolver.Resolve<IAnimalBLL>();只会返回其中一个类的对象

      解决方案:如果想返回多个实现类的对象,改成 resolver.Resolve<IEnumerable<IAnimalBLL>>()即可。

     1             {
     2                 ContainerBuilder builder = new ContainerBuilder();
     3                 //加载实现类的程序集
     4                 Assembly asm = Assembly.Load("Ypf.BLL");
     5                 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired();
     6                 IContainer resolver = builder.Build();
     7 
     8                 //返回 CalBLL 和 DogBLL 中的一个
     9                 //{
    10                 //    IAnimalBLL iAnimalBLL = resolver.Resolve<IAnimalBLL>();
    11                 //    var r1 = iAnimalBLL.Introduce();
    12                 //    Console.WriteLine(r1);
    13                 //}
    14 
    15                 //如何获取多个呢?
    16                 {
    17                     IEnumerable<IAnimalBLL> blls = resolver.Resolve<IEnumerable<IAnimalBLL>>();
    18                     foreach (IAnimalBLL animalBLL in blls)
    19                     {
    20                         Console.WriteLine(animalBLL.GetType());
    21                         Console.WriteLine(animalBLL.Introduce());
    22                     }
    23                 }
    24             }

    6. AutoFac的几种常见生命周期

    1. InstancePerDependency:每次请求 Resovle都返回一个新对象。InstancePerDependency()【这也是默认的创建实例的方式。】

    2. SingleInstance: 单例,只有在第一次请求的时候创建 。SingleInstance()

    3. InstancePerRequest:ASP.Net MVC 专用,每次http请求内一个对象(也可以理解为一个方法内)。InstancePerRequest() 和 CallContext神似

    4. InstancePerLifetimeScope:在一个生命周期域中,每一个依赖或调用创建一个单一的共享的实例,且每一个不同的生命周期域,实例是唯一的,不共享的。

     下面测试一下前两种生命周期

     情况1

     1   {
     2     ContainerBuilder builder = new ContainerBuilder();
     3     //加载实现类的程序集
     4     Assembly asm = Assembly.Load("Ypf.BLL");
     5     builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired().InstancePerDependency();
     6     IContainer resolver = builder.Build();
     7 
     8     IUserBLL u1 = resolver.Resolve<IUserBLL>();
     9     IUserBLL u2 = resolver.Resolve<IUserBLL>();
    10 
    11     Console.WriteLine(object.ReferenceEquals(u1, u2));
    12 
    13  }

    结果:False,证明InstancePerDependency 每次都创建一个新对象

    情况2

     1   {
     2      ContainerBuilder builder = new ContainerBuilder();
     3      //加载实现类的程序集
     4      Assembly asm = Assembly.Load("Ypf.BLL");
     5      builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired().SingleInstance();
     6      IContainer resolver = builder.Build();
     7 
     8      IUserBLL u1 = resolver.Resolve<IUserBLL>();
     9      IUserBLL u2 = resolver.Resolve<IUserBLL>();
    10 
    11      Console.WriteLine(object.ReferenceEquals(u1, u2));
    12 
    13 }

    结果:true,证明SingleInstance 每次都返回同一个对象。

    四. AutoFac与MVC整合

    1. Controller中通过属性注入对象

     步骤1:在Ypf.MVC层中添加对Ypf.IBLL层的引用,并将Ypf.BLL的程序集拷贝到 Ypf.MVC中,或者直接改一下Ypf.BLL输出路径。

     步骤2:通过Nuget安装程序集 Autofac.Mvc5。

     步骤3:在Gloabl 注册 AutoFac代码。

     1  public class MvcApplication : System.Web.HttpApplication
     2     {
     3         protected void Application_Start()
     4         {
     5             AreaRegistration.RegisterAllAreas();
     6             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
     7             RouteConfig.RegisterRoutes(RouteTable.Routes);
     8             BundleConfig.RegisterBundles(BundleTable.Bundles);
     9 
    10             /***********下面是AutoFac的注册*************/
    11             //1. 创建容器
    12             var builder = new ContainerBuilder();
    13             //2. 把当前程序集中的所有Controller都注册进来
    14             builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
    15             //3. 把Ypf.BLL中的所有类注册给它的全部实现接口,并且把实现类中的属性也进行注册
    16             //{ Assembly asmService = Assembly.Load("Ypf.BLL"); }
    17             //PS:这里可以配合配置文件的,将Ypf.BLL写到配置文件中
    18             string DllName = ConfigurationManager.AppSettings["DllName"];
    19             Assembly asmService = Assembly.Load(DllName);
    20             builder.RegisterAssemblyTypes(asmService).Where(t => !t.IsAbstract).AsImplementedInterfaces().PropertiesAutowired();
    21             var container = builder.Build();
    22             //4. 下面这句话表示当mvc创建controller对象的时候,都是由AutoFac为我们创建Controller对象
    23             DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    24 
    25 
    26         }
    27     }

    PS:分享个小技巧

    步骤4:在Controller中进行调用。

     

    2. 普通类中通过代码获取对象

      在一个没有通过AutoFac注册的普通类中如何获取接口对象呢,通过DependencyResolver.Current.GetService<IUserBLL>();来获取。

      代码如下:

    1   public class Utils
    2     {
    3         public static string Test()
    4         {       
    5             IUserBLL userBLL = DependencyResolver.Current.GetService<IUserBLL>();
    6             return userBLL.GetUserInfor();
    7         }
    8     }

    3. 如何在普通类中通过属性的方式注入对象

    需要有两个条件:

      ①: 这个普通类的创建必须在Global中通过AutoFac来进行注册。

      ②: 获取这个类的时候必须通过 DependencyResolver.Current.GetService<IUserBLL>(); 这种方式来获取。

     在Global文件中注册该普通类

     

    该普通类CommonHelp的获取必须通过DependencyResolver.Current.GetService<CommonHelp>();方式来获取。

     

    4. 在单独线程中获取对象

      比如在Quartz.Net 中,需要通过下面代码来获取。

    详细代码如下:

     {
                    //1.创建作业调度池(Scheduler)
                    IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
    
                    //2.创建一个具体的作业即job (具体的job需要单独在一个文件中执行)
                    var job = JobBuilder.Create<HelloJob>().Build();
    
                    //3.创建并配置一个触发器即trigger   1s执行一次
                    var trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(1)
                                                                                   .RepeatForever()).Build();
                    //4.将job和trigger加入到作业调度池中
                    scheduler.ScheduleJob(job, trigger);
    
                    //5.开启调度
                    scheduler.Start();
    }
     1  public class HelloJob:IJob
     2     {
     3         void IJob.Execute(IJobExecutionContext context)
     4         {
     5             IUserBLL userBLL;
     6             var container = AutofacDependencyResolver.Current.ApplicationContainer;
     7             using (container.BeginLifetimeScope())
     8             {
     9                 userBLL = container.Resolve<IUserBLL>();
    10             }
    11             //下面代码只是测试
    12             Console.WriteLine(userBLL.GetUserInfor());
    13         }
    14     }

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    进度条加载
    插件
    倒计时
    阿里云oss教程
    拖拽排序插件 ---- Dragula
    预览图片代码
    Error: spawn xxxx ENOENT原因与解决
    关于环境配置的随笔
    js下载文件
    QuickStart系列:docker部署之redis
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/9479268.html
Copyright © 2011-2022 走看看