当前位置: 移动技术网 > IT编程>开发语言>.net > 第二节:框架前期准备篇之AutoFac常见用法总结

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

2018年08月21日  | 移动技术网IT编程  | 我要评论

网上培训,张仪天,加盟店

一. 说在前面的话

  凡是大约工作在两年以上的朋友们,或多或少都会接触到一些框架搭建方面的知识,只要一谈到框架搭建这个问题或者最佳用法这个问题,势必会引起一点点小小的风波,我说我的好,他说他的好,非常容易骂架,所以在本节乃至该系列我仅仅是总结了一下自己日常中的一些用法,谈一下自己浅陋的见解,谈不上最佳,只要不误导新手 能有点帮助作用就可以了,如您不喜欢,请“右上角  谢谢”。
  在框架搭建过程中,在层与层的解耦方面,势必会涉及到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/
  ③ 最新版本: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     }

步骤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(姚鹏飞)
  • 博客地址 :
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网