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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    0309. Best Time to Buy and Sell Stock with Cooldown (M)
    0621. Task Scheduler (M)
    0106. Construct Binary Tree from Inorder and Postorder Traversal (M)
    0258. Add Digits (E)
    0154. Find Minimum in Rotated Sorted Array II (H)
    0797. All Paths From Source to Target (M)
    0260. Single Number III (M)
    0072. Edit Distance (H)
    0103. Binary Tree Zigzag Level Order Traversal (M)
    0312. Burst Balloons (H)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/9479268.html
Copyright © 2011-2022 走看看