第二节:框架前期准备篇之AutoFac常见用法总结

匿名 (未验证) 提交于 2019-12-02 22:06:11

一. 说在前面的话

  在框架搭建过程中,在层与层的解耦方面,势必会涉及到IOC框架,.Net 平台下我用过的IOC框架主要是: Spring.Net 、Unity、AutoFac,当然还有Castle(我没用过,就不发表任何评论了), 在用过的IOC框架中,Spring.Net 相对很老了,貌似在2015年就不在更新了,但基本的功能也够用了。 现阶段用的最多的就是Unity和AutoFac了,版本更新也比较快,Unity大约一年前写过两篇文章了,本次在该框架系列也会考虑更新一下Unity,本节主要介绍一下AutoFac的几个基本用法。
  先说一下两个概念IOC和DI,我的理解:
  ① IOC:调用者不再创建(不自己new)被调用者的实例,而是交给容器去创建(AutoFac就充当这里的容器),这就是控制反转。
  ② DI:容器创建好的实例再注入调用者的过程,就是依赖注入(比如:属性注入、构造函数注入等)。
AutoFac的信息:
  ② 官方文档:http://autofac.readthedocs.io/en/latest/index.html
  ③ 最新版本: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 { 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   }

 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中的几个方法,仅此而已,在实际开发没有这么用的,坑比用法,起不到任何解耦的作用。

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()的用法,还是存在实现类的身影,在实际开发中没有这么用的,起不到任何解耦的作用,坑比用法。

  引入反射的背景:前面两种方式都需要添加对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 }

  背景:一个实现类中定义了其他类型的接口属性,比如RoleBLL中定义IUserBLL的接口属性,而且要对其进行调用,

  

 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 }

  背景: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             }

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

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

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

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

 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中通过属性注入对象

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

 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     }

步骤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>(); 这种方式来获取。

该普通类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     }

!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!