BigBox777

爱学习、爱运动

  :: 首页 :: 新随笔 :: 联系 :: :: 管理 ::

C#技术栈入门到精通系列16——IOC容器框架Autofac

阅读目录

1、介绍
2、注册组件
  2.1、反射注册
  2.2、手动指定构造函数【反射注册】
  2.3、传递参数注册
  2.4、实例注册
  2.5、Lambda灵活注册
  2.6、泛型注册
  2.7、程序集注册
3、生命周期
4、Autofac配置文件
  4.1、配置项介绍
  4.2、泛型注入配置
5、Autofac实现AOP
  5.1、类代理拦截
  5.2、接口代理拦截
6、参考

返回系列文章目录 

 

案例代码下载

 1、介绍

   Autofac 是一个非常nice的控制反转IOC容器,它管理类之间的依赖关系,从而使应用在规模及复杂性增长的情况下依然可以轻易地修改,适用于 .NET Core、ASP.NET Core、.NET 4.5.1+、通用 Windows 应用程序等。如果你现在还不懂什么是控制反转?什么是依赖注入?请看我前面的文章:C#技术栈入门到精通系列7——依赖注入 ,本节是对之前这篇文章的进一步延伸。使用Autofac需要Nuget安装开源库  Autofac 6.3.0 。

 

2、注册组件

  Autofac可以通过反射、实例或者Lambda来注册组件,接下来分别介绍一下注册过程。

 

2.1、反射注册

  类Hero依赖接口IWater,类Wahaha实现了接口IWater,本例通过反射注册Hero及其依赖的IWater,创建出Hero类实例。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterComponents
 8 {
 9     public class Hero
10     {
11         private readonly IWater _water;
12         public Hero(IWater water)
13         {
14             this._water = water;
15         }
16         public IWater GetWater()
17         {
18             return _water;
19         }
20     }
21 }
类Hero
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterComponents
 8 {
 9     public interface IWater
10     {
11         string GetWaterName();
12     }
13 }
接口IWater
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterComponents
 8 {
 9     public class Wahaha : IWater
10     {
11         public string GetWaterName()
12         {
13             return "哇哈哈矿泉水";
14         }
15     }
16 }
类Wahaha
 1 using Autofac;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Demo16_Autofac.RegisterComponents
 9 {
10     public class RegisterMain
11     {
12         public static void Test1()
13         {
14             //1、注册Autofac组件
15             var builder = new ContainerBuilder();
16             //2、注册组件及其关联组件
17             builder.RegisterType<Hero>();
18             builder.RegisterType<Wahaha>().As<IWater>();
19             //3、构造Autofac容器
20             var container = builder.Build();
21             //4、创建生命周期作用域
22             using (var scope= container.BeginLifetimeScope())
23             {
24                 //5、创建对象
25                 var hero = scope.Resolve<Hero>();
26                 Console.WriteLine(hero.GetWater().GetWaterName());
27             }
28         }
29     }
30 }
反射注册创建Hero实例

 

2.2、手动指定构造函数【反射注册】

  类Warrior有三个构造函数,我们可以使用 UsingConstructor 来手动选择特定的构造函数,如果你不手动选择,那么Autofac会自动为您的类使用能够从容器中获取的最多参数的构造函数。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterComponents
 8 {
 9     public class Warrior
10     {
11         private readonly IWater _water;
12         private readonly string _name;
13         public Warrior()
14         {
15             Console.WriteLine("构造函数:public Warrior()");
16         }
17         public Warrior(IWater water)
18         {
19             Console.WriteLine("构造函数:public Warrior(IWater water)");
20             this._water = water;
21         }
22         public Warrior(IWater water,string name)
23         {
24             Console.WriteLine("构造函数:public Warrior(IWater water,string name)");
25             this._water = water;
26             this._name = name;
27         }
28         public IWater GetWater()
29         {
30             return _water;
31         }
32     }
33 }
类Warrior
 1 using Autofac;
 2 using Autofac.Core;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 
 9 namespace Demo16_Autofac.RegisterComponents
10 {
11     public class RegisterMain
12     {
13         /// <summary>
14         /// 手动指定构造函数
15         /// </summary>
16         public static void Test2()
17         {
18             //1、注册Autofac组件
19             var builder=new ContainerBuilder();
20             //2、注册组件及其关联组件
21             builder.RegisterType<Warrior>().UsingConstructor(typeof(IWater),typeof(string));
22             builder.RegisterType<Wahaha>().As<IWater>();
23             //3、构造Autofac容器
24             var container = builder.Build();
25             //4、创建生命周期作用域
26             using (var scope = container.BeginLifetimeScope())
27             {
28                 //5、创建对象,这里有用的传递参数的注册,TypedParameter是按类型注册
29                 var warrior = scope.Resolve<Warrior>(new TypedParameter(typeof(string),"BigBox777"));
30                 Console.WriteLine(warrior.GetWater().GetWaterName());
31             }
32         }
33     }
34 }
手动指定构造函数

 

2.3、传递参数注册
  在注册组件时,您可以提供一组参数,这些参数可以在基于该组件的服务解析期间使用。可以在注册时传递,也可以在解析时传递。我们从新写个例子类OPCMessage依赖接口ILog,类ConsoleLog实现了接口ILog。写例子前先看下面Autofac的参数匹配策略:
  • NamedParameter - 按名称匹配目标参数
  • TypedParameter - 按类型匹配目标参数(需要精确类型匹配)
  • ResolvedParameter - 灵活的参数匹配

注意:NamedParameter和TypedParameter只能提供常量值。ResolvedParameter 可以用作提供从容器中动态检索的值的一种方式,例如通过名称解析服务。     

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.PassingParameters
 8 {
 9     public class OPCMessage
10     {
11         private readonly ILog _log;
12         private readonly string _deviceName;
13         private readonly Guid _guid;
14         private readonly int _id;
15         public OPCMessage(ILog log, string deviceName,Guid guid,int id)
16         {
17             Console.WriteLine("构造函数:public OPCMessage(ILog log, string deviceName,Guid guid,int id)");
18             this._log = log;
19             this._deviceName = deviceName;
20             this._guid = guid;
21             this._id = id;
22         }
23         public override string ToString()
24         {
25             return $"deviceName:{_deviceName},guid:{_guid},id:{_id}";
26         }
27     }
28 }
类OPCMessage
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.PassingParameters
 8 {
 9     public interface ILog
10     {
11         void Write(string message);
12         void Write(string message,DateTime dateTime);
13     }
14 }
接口ILog
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.PassingParameters
 8 {
 9     public class ConsoleLog : ILog
10     {
11         public void Write(string message)
12         {
13             Console.WriteLine($"Console[{DateTime.UtcNow}]:{message}");
14         }
15 
16         public void Write(string message, DateTime dateTime)
17         {
18             Console.WriteLine($"Console[{dateTime.ToUniversalTime()}]:{message}");
19         }
20     }
21 }
类ConsoleLog
 1 using Autofac;
 2 using Autofac.Core;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 
 9 namespace Demo16_Autofac.PassingParameters
10 {
11     public class PassingParaMain
12     {
13         /// <summary>
14         /// 1、注册时传递
15         /// </summary>
16         public static void Test1()
17         {
18             //1、注册Autofac组件
19             var builder = new ContainerBuilder();
20             //2、注册组件(传递参数)及其关联组件
21             builder.RegisterType<OPCMessage>()
22                 .WithParameter(new NamedParameter("deviceName", "BigBox'PLC"))  //名称匹配
23                 .WithParameter(new TypedParameter(typeof(Guid), Guid.NewGuid()))    //类型匹配
24                 .WithParameter(new ResolvedParameter(   //传递实例来匹配
25                     (pi, ctx) => pi.ParameterType == typeof(int) && pi.Name == "id",
26                     (pi, ctx) => 1233));
27             builder.RegisterType<ConsoleLog>().As<ILog>();
28             //3、构造Autofac容器
29             var container = builder.Build();
30             //4、创建生命周期作用域
31             using (var scope = container.BeginLifetimeScope())
32             {
33                 //5、创建对象
34                 var opc = scope.Resolve<OPCMessage>();
35                 Console.WriteLine(opc.ToString());
36             }
37         }
38       
39     }
40 }
注册时传递参数
 1 using Autofac;
 2 using Autofac.Core;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 
 9 namespace Demo16_Autofac.PassingParameters
10 {
11     public class PassingParaMain
12     {
13         /// <summary>
14         /// 2、解析时传递
15         /// </summary>
16         public static void Test2()
17         {
18             //1、注册Autofac组件
19             var builder = new ContainerBuilder();
20             //2、注册组件(传递参数)及其关联组件
21             builder.RegisterType<OPCMessage>();
22             builder.RegisterType<ConsoleLog>().As<ILog>();
23             //3、构造Autofac容器
24             var container = builder.Build();
25             //4、创建生命周期作用域
26             using (var scope = container.BeginLifetimeScope())
27             {
28                 //5、创建对象
29                 var opc = scope.Resolve<OPCMessage>(
30                     new NamedParameter("deviceName", "BigBox'PLC32"), //名称匹配
31                     new TypedParameter(typeof(Guid), Guid.NewGuid()),   //类型匹配
32                     new ResolvedParameter(   //传递实例来匹配
33                     (pi, ctx) => pi.ParameterType == typeof(int) && pi.Name == "id",
34                     (pi, ctx) => 778899)
35                     );
36                 Console.WriteLine(opc.ToString());
37             }
38         }
39              
40     }
41 }
解析时传递参数【和上面同样效果】

 

2.4、实例注册
  在某些情况下,您可能希望预先生成对象的实例并将其添加到容器中以供注册组件使用。您可以使用  RegisterInstance  方法执行此操作。这种情况下需要注意,autofac会自动处理我们传入的实例的生命周期,如果我们想自己控制传入实例的生命周期,需要使用 ExternallyOwned 方法。看下面的例子。    
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterInstance
 8 {
 9     public class Student: ILearn
10     {
11         public string Name { get; set; }
12         public int Age { get; set; }
13         public bool Sex { get; set; }
14         private readonly DateTime _createTime;
15         public Student()
16         {
17             this._createTime=DateTime.Now;
18         }
19         public void PlayFootball()
20         {
21             Console.WriteLine($"{Name}[{_createTime.ToUniversalTime()}]:训练,踢足球");
22         }
23 
24         public void Sing()
25         {
26             Console.WriteLine($"{Name}[{_createTime.ToUniversalTime()}]:Sing,同一首歌");
27         }
28     }
29 }
类Student
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterInstance
 8 {
 9     public interface ILearn
10     {
11         void Sing();
12         void PlayFootball();
13     }
14 }
接口ILearn
 1 using Autofac;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Demo16_Autofac.RegisterInstance
 9 {
10     public class RegisterInstanceMain
11     {
12         public static void Test1()
13         {
14             //1、注册Autofac组件
15             var builder = new ContainerBuilder();
16             //2、注册实例
17             var student=new Student { Name="BigBox777",Age=18,Sex=true};
18             builder.RegisterInstance(student).As<Student>().ExternallyOwned(); //ExternallyOwned:自己管理实例生命周期
19             //3、构造Autofac容器
20             var container = builder.Build();
21             //4、创建生命周期作用域
22             using (var scope = container.BeginLifetimeScope())
23             {
24                 //5、创建对象
25                 var stu = scope.Resolve<Student>();
26                 stu.Sing();
27             }
28         }
29     }
30 }
实例注册

 

2.5、Lambda灵活注册

  反射是创建组件的一个很好的默认选择,但是有的时候需要根据传入的参数来决定注册成不同的组件。比如根据传入的courseName参数来决定组成Sing(音乐课)、Mathematics(数学课)、Chemistry(化学课),这样可以隔离组件的创建,看下面例子。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterLambda
 8 {
 9     public interface ICourse
10     {
11         string Name();
12     }
13 }
接口ICourse
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterLambda
 8 {
 9     public class Chemistry : ICourse
10     {
11         public string Name()
12         {
13             return "化学课";
14         }
15     }
16 }
类Chemistry
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterLambda
 8 {
 9     public class Mathematics : ICourse
10     {
11         public string Name()
12         {
13             return "数学课";
14         }
15     }
16 }
类Mathematics
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterLambda
 8 {
 9     public class Sing : ICourse
10     {
11         public string Name()
12         {
13             return "音乐课";
14         }
15     }
16 }
类Sing
 1 using Autofac;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Demo16_Autofac.RegisterLambda
 9 {
10     public class RegisterLambdaMain
11     {
12         public static void Test1()
13         {
14             //1、注册Autofac组件
15             var builder = new ContainerBuilder();
16             //2、注册Lambda组件
17             builder.Register<ICourse>((ctx, p) =>
18             {
19                 string courseName = p.Named<string>("courseName");
20                 if (courseName == "Chemistry")
21                 {
22                     return new Chemistry();
23                 }
24                 else if (courseName == "Mathematics")
25                 {
26                     return new Mathematics();
27                 }
28                 else
29                 {
30                     return new Sing();
31                 }
32             });
33             //3、构造Autofac容器
34             var container = builder.Build();
35             //4、创建生命周期作用域
36             using (var scope = container.BeginLifetimeScope())
37             {
38                 //5、创建对象(根据传入courseName参数的值来决定构建实例的类型)
39                 var stu = scope.Resolve<ICourse>(new NamedParameter("courseName", "Chemistry"));
40                 Console.WriteLine(stu.Name());
41             }
42         }
43     }
44 }
Lambda灵活注册

 

2.6、泛型注册

  autofac还支持泛型组件的注册,看下面例子。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterGeneric
 8 {
 9     public interface IRepository<T>
10     {
11         string Info();
12     }
13 }
泛型接口IRepository<>
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterGeneric
 8 {
 9     public class NHibernateRepository<T> : IRepository<T>
10     {
11         public string Info()
12         {
13             return typeof(T).Name;
14         }
15     }
16 }
泛型实现类NHibernateRepository<>
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.RegisterGeneric
 8 {
 9     public class User
10     {
11         public string Name { get; set; }
12         public string Password { get; set; }
13         public string Email { get; set; }
14     }
15 }
类User
 1 using Autofac;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Demo16_Autofac.RegisterGeneric
 9 {
10     public class RegisterGenericMain
11     {
12         public static void Test1()
13         {
14             //1、注册Autofac组件
15             var builder = new ContainerBuilder();
16             //2、注册泛型组件
17             builder.RegisterGeneric(typeof(NHibernateRepository<>)).As(typeof(IRepository<>));
18             //3、构造Autofac容器
19             var container = builder.Build();
20             //4、创建生命周期作用域
21             using (var scope = container.BeginLifetimeScope())
22             {
23                 //5、创建对象
24                 var userRepo = scope.Resolve<IRepository<User>>();
25                 Console.WriteLine(userRepo.Info());
26             }
27         }
28     }
29 }
泛型注册代码

 

2.7、程序集注册

  程序集级别的依赖注入是指接口和实现的依赖不使用配置文件或硬代码实现  builder.RegisterType<Wahaha>().As<IWater>(); ,而是通过名称约定实现依赖注入。下面列出主要代码,详细下载代码查看。

 1 using Autofac;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Reflection;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 
 9 namespace Demo16_Autofac.AssemblyScanning
10 {
11     public class AssemblyScanningMain
12     {
13         public static void Test1()
14         {
15             //1、创建容器组件
16             var builder = new ContainerBuilder();
17             //2、载入程序集
18             var ISer = Assembly.LoadFrom("Demo16-Autofac.IService.dll");
19             var ser = Assembly.LoadFrom("Demo16-Autofac.Service.dll");
20             var iRepo = Assembly.LoadFrom("Demo16-Autofac.IRepository.dll");
21             var repo = Assembly.LoadFrom("Demo16-Autofac.Repository.dll");
22             //3、根据名称约定(接口和实现均以Service结尾),实现服务接口和服务实现的依赖注入
23             builder.RegisterAssemblyTypes(ISer, ser)
24                 .Where(t => t.Name.EndsWith("Service"))
25                 .AsImplementedInterfaces();
26             //4、根据名称约定注入Repository结尾的接口和实现
27             builder.RegisterAssemblyTypes(iRepo, repo)
28                 .Where(t => t.Name.EndsWith("Repository"))
29                 .AsImplementedInterfaces();
30             //5、构造Autofac容器
31             var container =builder.Build();
32             //6、创建生命周期作用域
33             using (var scope = container.BeginLifetimeScope())
34             {
35                 Type iUserType = iRepo.GetType();
36                 
37                  //7、创建对象
38                  var userService = scope.Resolve(iUserType);
39                 
40             }
41 
42         }
43     }
44 }
程序集注册

 

3、生命周期

  Autofac使用生命周期作用域 using (var scope = container.BeginLifetimeScope()) {} 来控制实例的生命周期,生命周期范围可以嵌套,注册实例的时候可以使用一下七种方法来标记实例的生命周期:

  • InstancePerDependency:在其他容器中也称为“瞬态”或“工厂”。使用每个依赖范围,每个服务请求都会返回一个唯一的实例。如果未指定其他选项,则这是默认设置。
  • SingleInstance:使用单实例作用域,从根和所有嵌套作用域中的所有请求返回一个实例。
  • InstancePerLifetimeScope:此范围适用于嵌套生命周期。具有每个生命周期范围的组件在每个嵌套生命周期范围内最多有一个实例。
  • InstancePerMatchingLifetimeScope:同一个匹配的生命周期域中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;且每个不匹配的生命周期域中的实例是唯一的,不共享的。这类似于上面的“每个生命周期范围内的实例”概念,但允许更精确地控制实例共享。创建嵌套生命周期范围时,您可以“标记”或“命名”范围。具有每个匹配生命周期范围的组件在每个嵌套生命周期范围内最多有一个与给定名称匹配的实例。这允许您创建一种“作用域单例”,其中其他嵌套的生命周期范围可以共享组件的实例,而无需声明全局共享实例。注意:如果在没有正确命名的生命周期范围的情况下尝试解析每个匹配生命周期的组件,则会出现异常。
  • InstancePerRequest:每个请求的实例建立在每个匹配的生命周期范围的实例之上,通过提供众所周知的生命周期范围标签、注册便利方法和常见应用程序类型的集成。然而,在幕后,它仍然只是每个匹配生命周期范围的实例。ASP.NET Core 使用 Instance Per Lifetime Scope 而不是 Instance Per Request。
  • InstancePerOwned:在一个所拥有的实例创建的生命周期中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;(较少使用)
  • InstancePerLifetimeScope:强制绑定到一个线程的对象不会满足绑定到另一个线程的组件的依赖关系。虽然没有方便的方法,但您可以使用生命周期范围来完成。重要提示:考虑到多线程场景,您必须非常小心,不要将父作用域从生成的线程下释放出来。如果您生成线程然后处置父范围,您可能会遇到无法解析组件的糟糕情况。
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.InstanceScope
 8 {
 9     public class Worker
10     {
11         public string Name { get; set; }
12         public int Age { get; set; }
13         public bool Sex { get; set; }
14         private readonly Guid _guid;
15         public Worker()
16         {
17             this._guid = Guid.NewGuid();
18             Console.WriteLine($"创建新的Worker:{_guid}");
19         }
20         public void DoWork()
21         {
22             Console.WriteLine($"愉快的专心工作!GUID:{_guid}");
23         }
24     }
25 }
类Worker
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.InstanceScope
 8 {
 9     internal class ServiceForHandler
10     {
11         public ServiceForHandler()
12         {
13             Console.WriteLine("创建ServiceForHandler");
14         }
15     }
16 }
类ServiceForHandler
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.InstanceScope
 8 {
 9     internal class MessageHandler : IDisposable
10     {
11         public MessageHandler()
12         {
13             Console.WriteLine("创建:MessageHandler");
14         }
15         public void Dispose()
16         {
17 
18         }
19     }
20 }
类MessageHandler
  1 using Autofac;
  2 using Autofac.Features.OwnedInstances;
  3 using System;
  4 using System.Collections.Generic;
  5 using System.Linq;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8 
  9 namespace Demo16_Autofac.InstanceScope
 10 {
 11     public class InstanceScopeMain
 12     {
 13         //InstancePerDependency:默认,每个服务请求都会返回一个唯一的实例
 14         public static void Test1()
 15         {
 16             var builder = new ContainerBuilder();
 17             builder.RegisterType<Worker>().InstancePerDependency();
 18             var container = builder.Build();
 19             using (var scope=container.BeginLifetimeScope())
 20             {
 21                 for (int i = 0; i < 5; i++)
 22                 {
 23                     Worker worker = scope.Resolve<Worker>();
 24                     worker?.DoWork();
 25                 }
 26             }
 27         }
 28 
 29         //SingleInstance:单例
 30         public static void Test2()
 31         {
 32             var builder=new ContainerBuilder();
 33             builder.RegisterType<Worker>().SingleInstance();
 34             var container = builder.Build();
 35             using (var scope1=container.BeginLifetimeScope())
 36             {
 37                 for (int i = 0; i < 5; i++)
 38                 {
 39                     Worker worker1 = scope1.Resolve<Worker>();
 40                     using (var scope2=scope1.BeginLifetimeScope())
 41                     {
 42                         Worker worker2 = scope2.Resolve<Worker>();
 43                         worker1?.DoWork();
 44                         worker2?.DoWork();
 45                     }
 46                 }
 47             }
 48         }
 49 
 50         //InstancePerLifetimeScope:每个生命周期范围的组件在每个嵌套生命周期范围内最多有一个实例
 51         public static void Test3()
 52         {
 53             var builder = new ContainerBuilder();
 54             builder.RegisterType<Worker>().InstancePerLifetimeScope();
 55             var container = builder.Build();
 56             using (var scope1=container.BeginLifetimeScope())
 57             {
 58                 for (int i = 0; i < 5; i++)
 59                 {
 60                     Worker worker = scope1.Resolve<Worker>();
 61                     worker?.DoWork();
 62                 }
 63             }
 64             using (var scope2 = container.BeginLifetimeScope())
 65             {
 66                 for (int i = 0; i < 5; i++)
 67                 {
 68                     Worker worker = scope2.Resolve<Worker>();
 69                     worker?.DoWork();
 70                 }
 71             }
 72             using (var scope3 = container.BeginLifetimeScope())
 73             {
 74                 for (int i = 0; i < 5; i++)
 75                 {
 76                     Worker worker = scope3.Resolve<Worker>();
 77                     worker?.DoWork();
 78                 }
 79             }
 80             using (var scope4 = container.BeginLifetimeScope())
 81             {
 82                 for (int i = 0; i < 5; i++)
 83                 {
 84                     Worker worker = scope4.Resolve<Worker>();
 85                     worker?.DoWork();
 86                 }
 87             }
 88         }
 89 
 90         //InstancePerMatchingLifetimeScope:同一个匹配的生命周期域中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;且每个不匹配的生命周期域中的实例是唯一的,不共享的。
 91         public static void Test4()
 92         {
 93             var builder=new ContainerBuilder();
 94             builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("my-request");
 95             var container=builder.Build();
 96             using (var scope1=container.BeginLifetimeScope("my-request"))
 97             {
 98                 for (int i = 0; i < 5; i++)
 99                 {
100                     var worker1 = scope1.Resolve<Worker>();
101                     using (var scope2=scope1.BeginLifetimeScope())
102                     {
103                         var worker2 = scope2.Resolve<Worker>();
104                         worker1?.DoWork();
105                         worker2?.DoWork();
106                     }
107                 }
108             }
109             using (var scope3 = container.BeginLifetimeScope("my-request"))
110             {
111                 for (int i = 0; i < 5; i++)
112                 {
113                     var worker3 = scope3.Resolve<Worker>();
114                     using (var scope4 = scope3.BeginLifetimeScope())
115                     {
116                         var worker4 = scope4.Resolve<Worker>();
117                         worker3?.DoWork();
118                         worker4?.DoWork();
119                     }
120                 }
121             }
122             using (var scopeNoTag=container.BeginLifetimeScope())
123             {
124                 //注意:如果在没有正确命名的生命周期范围的情况下尝试解析每个匹配生命周期的组件,则会出现异常。
125                 //var fail = scopeNoTag.Resolve<Worker>();
126             }
127         }
128 
129         //InstancePerRequest:每个请求的实例建立在每个匹配的生命周期范围的实例之上
130         public static void Test5()
131         {
132             //ASP.NET Core 使用 Instance Per Lifetime Scope 而不是 Instance Per Request。
133         }
134 
135         //InstancePerOwned:在一个所拥有的实例创建的生命周期中,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;(较少使用)
136         public static void Test6()
137         {
138             var builder = new ContainerBuilder();
139             builder.RegisterType<MessageHandler>();
140             builder.RegisterType<ServiceForHandler>().InstancePerOwned<MessageHandler>();
141             var container=builder.Build();
142             using (var scope=container.BeginLifetimeScope())
143             {
144                 var h1 = scope.Resolve<Owned<MessageHandler>>();
145                 h1.Dispose();
146             }
147         }
148 
149         //强制绑定到一个线程的对象不会满足绑定到另一个线程的组件的依赖关系。虽然没有方便的方法,但您可以使用生命周期范围来完成。
150         public static void Test7()
151         {
152         }
153     }
154 }
生命周期作用域使用代码

 

4、Autofac配置文件

  Autofac提供基于 JSON/XML 文件的配置支持,鼓励通过ContainerBuilder类进行编程配置。使用编程接口是容器设计的核心。当无法在编译时选择或配置具体类时,建议使用 JSON 或 XML。需要Nuget引入模块  Microsoft.Extensions.Configuration 6.0.0 和  Autofac.Configuration 4.0.0  ,使用JSON配置文件时还需要引用  Microsoft.Extensions.Configuration.Json 6.0.0 ,使用XML配置文件时引用  Microsoft.Extensions.Configuration.Xml 6.0.0  。   

4.1、配置项介绍

  下面举例的配置文件是同样的,分别用JSON文件和XML文件来表示。

 1 {
 2   "defaultAssembly": "Autofac.Example.Calculator",
 3   "components": [{
 4     "type": "Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition",
 5     "services": [{
 6       "type": "Autofac.Example.Calculator.Api.IOperation"
 7     }],
 8     "injectProperties": true
 9   }, {
10     "type": "Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division",
11     "services": [{
12       "type": "Autofac.Example.Calculator.Api.IOperation"
13     }],
14     "parameters": {
15       "places": 4
16     }
17   }]
18 }
JSON配置文件autofacConfig.json
 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <autofac defaultAssembly="Autofac.Example.Calculator">
 3     <components name="0">
 4         <type>Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition</type>
 5         <services name="0" type="Autofac.Example.Calculator.Api.IOperation" />
 6         <injectProperties>true</injectProperties>
 7     </components>
 8     <components name="1">
 9         <type>Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division</type>
10         <services name="0" type="Autofac.Example.Calculator.Api.IOperation" />
11         <injectProperties>true</injectProperties>
12         <parameters>
13             <places>4</places>
14         </parameters>
15     </components>
16 </autofac>
XML配置文件autofacXMLConfig.xml
 1 using Autofac;
 2 using Autofac.Configuration;
 3 using Microsoft.Extensions.Configuration;
 4 using Microsoft.Extensions.Configuration.Json;
 5 using System;
 6 using System.Collections.Generic;
 7 using System.Linq;
 8 using System.Text;
 9 using System.Threading.Tasks;
10 
11 namespace Demo16_Autofac.CustomerConfiguration
12 {
13     internal class CustomerConfigurationMain
14     {
15         //读取使用JSON配置文件autofacConfig.json
16         public static void Test1()
17         {
18             var configSource = new JsonConfigurationSource()
19             { Optional = false, ReloadOnChange = true, Path = "CustomerConfiguration/autofacConfig.json" };
20 
21             var config = new ConfigurationBuilder();
22             config.Add(configSource);
23             var configModule = new ConfigurationModule(config.Build());
24             var buidler = new ContainerBuilder();
25             buidler.RegisterModule(configModule);
26             var container = buidler.Build();
27             using (var scope=container.BeginLifetimeScope())
28             {
29                 for (int i = 0; i < 5; i++)
30                 {
31                     var water = scope.Resolve<RegisterComponents.IWater>();
32                     Console.WriteLine(water.GetWaterName());
33                 }
34             }
35         }
36     }
37 }
读取使用JSON配置文件
 1 using Autofac;
 2 using Autofac.Configuration;
 3 using Microsoft.Extensions.Configuration;
 4 using Microsoft.Extensions.Configuration.Json;
 5 using Microsoft.Extensions.Configuration.Xml;
 6 using System;
 7 using System.Collections.Generic;
 8 using System.Linq;
 9 using System.Text;
10 using System.Threading.Tasks;
11 
12 namespace Demo16_Autofac.CustomerConfiguration
13 {
14     internal class CustomerConfigurationMain
15     {
16         //读取使用XML配置文件autofacXMLConfig.xml
17         public static void Test2()
18         {
19             var configSource = new XmlConfigurationSource()
20             { Optional = false, ReloadOnChange = true, Path = "CustomerConfiguration/autofacXMLConfig.xml" };
21 
22             var config = new ConfigurationBuilder();
23             config.Add(configSource);
24             var configModule = new ConfigurationModule(config.Build());
25             var buidler = new ContainerBuilder();
26             buidler.RegisterModule(configModule);
27             var container = buidler.Build();
28             using (var scope = container.BeginLifetimeScope())
29             {
30                 for (int i = 0; i < 5; i++)
31                 {
32                     var water = scope.Resolve<RegisterComponents.IWater>();
33                     Console.WriteLine(water.GetWaterName());
34                 }
35             }
36         }
37     }
38 }
读取使用XML配置文件

注意:您可以在配置中指定“defaultAssembly”选项,以帮助以更短的方式编写类型。如果您未在类型或接口引用中指定程序集限定的类型名称,则会假定它位于默认程序集中。

 

components  是配置文件的顶级元素,里面是您要注册的组件数组,可以在每个组件上指定从生命周期范围到参数的所有内容。下面是一个包含所有选项的组件:

 1 {
 2   "components": [
 3     {
 4       "type": "Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition",
 5       "services": [
 6         {
 7           "type": "Autofac.Example.Calculator.Api.IOperation"
 8         },
 9         {
10           "type": "Autofac.Example.Calculator.Api.IAddOperation",
11           "key": "add"
12         }
13       ],
14       "autoActivate": true,
15       "injectProperties": true,
16       "instanceScope": "per-dependency",
17       "metadata": [
18         {
19           "key": "answer",
20           "value": 42,
21           "type": "System.Int32, mscorlib"
22         }
23       ],
24       "ownership": "external",
25       "parameters": {
26         "places": 4
27       },
28       "properties": {
29         "DictionaryProp": {
30           "key": "value"
31         },
32         "ListProp": [ 1, 2, 3, 4, 5 ]
33       }
34     }
35   ]
36 }
完整components配置示例

元素名称

描述

有效值

type

唯一需要的东西。组件的具体类(如果在非默认程序集中,则为程序集限定)。

可以通过反射创建的任何 .NET 类型名称。

services

组件公开的一系列服务。每个服务必须有一个type并且可以选择指定一个key.

可以通过反射创建的任何 .NET 类型名称。

autoActivate

一个布尔值,指示组件是否应该自动激活。

true, false

injectProperties

一个布尔值,指示是否应启用组件的属性(设置器)注入。

true, false

instanceScope

组件的实例范围。

singleinstance, perlifetimescope, perdependency,perrequest

metadata

与组件关联的元数据值数组。每个项目指定name、type和value。

任何元数据值。

ownership

允许您控制生命周期范围是处理组件还是您的代码处理。

lifetimescope, external

parameters

键值对,其中每个元素的名称是构造函数参数的名称,值是要注入的值。

组件类型的构造函数中的任何参数。

properties

键值对,其中每个元素的名称是属性的名称,值是要注入的值。

组件类型上的任何可设置属性。

 

 1 {
 2   "modules": [{
 3     "type": "Autofac.Example.Calculator.OperationModule, Autofac.Example.Calculator",
 4     "parameters": {
 5       "places": 4
 6     },
 7     "properties": {
 8       "DictionaryProp": {
 9         "key": "value"
10       },
11       "ListProp": [1, 2, 3, 4, 5]
12     }
13   }]
14 }
完整的modules示例

元素名称

描述

有效值

type

唯一需要的东西。模块的具体类(如果在非默认程序集中,则为程序集限定)。

从它派生的任何 .NET 类型名称Autofac.Module都可以通过反射创建。

parameters

键值对,其中每个元素的名称是构造函数参数的名称,值是要注入的值。

模块类型的构造函数中的任何参数。

properties

键值对,其中每个元素的名称是属性的名称,值是要注入的值。

模块类型上的任何可设置属性。

 

4.2、泛型注入配置

  使用泛型的时候注入不清楚类型怎么写的,可以用代码  System.Diagnostics.Debug.WriteLine(typeof(IRepository<string>).AssemblyQualifiedName);  查看。

1 {
2   "components": [{
3     "type": "ConfigWithGenericsDemo.StringRepository, ConfigWithGenericsDemo",
4     "services": [{
5       "type": "ConfigWithGenericsDemo.IRepository`1[[System.String, mscorlib]], ConfigWithGenericsDemo"
6     }]
7   }]
8 }
泛型注入配置文件

 

5、Autofac实现AOP

  AOP面向切面编程是实现程序功能的统一维护的一种技术,主要有两种实现方式:①编译时静态植入,优点是效率高,缺点是缺乏灵活性,代表收费的PostSharp。②动态代理,动态为目标类型创建代理,通过代理调用实现拦截。Autofac的AOP是通过动态代理  Autofac.Extras.DynamicProxy  实现的。实现步骤:

①使用时候需要Nuget安装  Autofac.Extras.DynamicProxy 6.0.0 包

②创建拦截器,需要实现  IInterceptor  接口 

③注册拦截器到Autofac容器

④启动拦截器,两个方法:EnableInterfaceInterceptors()方法会动态创建一个接口代理;EnableClassInterceptors()方法会创建一个目标类的子类代理类,这里需要注意的是只会拦截虚方法,重写方法。

⑤给拦截内容添加特性标注,有两种方法:第一种:给类型加上特性  Intercept  ;第二种:在注册类型到容器的时候动态注入拦截器。

 

5.1、类代理拦截

 1 using Castle.DynamicProxy;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.IO;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8 
 9 namespace Demo16_Autofac.AOP
10 {
11     //拦截器需要实现IInterceptor接口
12     public class CallLogger : IInterceptor
13     {
14         private readonly TextWriter _writer;
15         public CallLogger(TextWriter textWriter)
16         {
17             this._writer = textWriter;
18         }
19         /// <summary>
20         /// 拦截方法 
21         /// </summary>
22         /// <param name="invocation">包含被拦截方法执行前的名称、参数和方法执行后的返回结果</param>
23         public void Intercept(IInvocation invocation)
24         {
25             _writer.WriteLine($"调用的方法是:{invocation.Method.Name},参数是:{string.Join(",",invocation.Arguments.Select(arg=>(arg??"").ToString()).ToArray())}");
26             //在被拦截的方法执行完毕后继续执行
27             invocation.Proceed();
28             _writer.WriteLine($"方法执行完毕:{invocation.ReturnValue}");
29         }
30     }
31 }
创建拦截器CallLogger
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Demo16_Autofac.AOP
 8 {
 9     internal interface IShape
10     {
11         void Area();
12     }
13 }
接口IShape
 1 using Autofac.Extras.DynamicProxy;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Demo16_Autofac.AOP
 9 {
10     [Intercept(typeof(CallLogger))]
11     public class Circle : IShape
12     {
13         //必须是虚方法
14         public virtual void Area()
15         {
16             Console.WriteLine("调用了圆求面积方法");
17         }
18     }
19 }
实现Circle,注意标注的特性和重新的虚方法
 1 using Autofac;
 2 using Autofac.Extras.DynamicProxy;
 3 using Castle.DynamicProxy;
 4 using System;
 5 using System.Collections.Generic;
 6 using System.Linq;
 7 using System.Text;
 8 using System.Threading.Tasks;
 9 
10 namespace Demo16_Autofac.AOP
11 {
12     internal class AOPMain
13     {
14         //1、类代理拦截
15         public static void Test1()
16         {
17             var builder=new ContainerBuilder();
18             //动态注入拦截器CallLogger,启用类代理拦截
19             builder.RegisterType<Circle>().EnableClassInterceptors();
20             //注入拦截器到容器
21             builder.Register(c => new CallLogger(Console.Out));
22             //创建容器
23             var container=builder.Build();
24             using (var scope=container.BeginLifetimeScope())
25             {
26                 Circle circle = scope.Resolve<Circle>();
27                 circle.Area();
28             }
29         }
30     }
31 }
类代理拦截实现

 

6、参考

官网  https://autofac.org/
官方帮助文档  https://autofac.readthedocs.io/en/latest/
在西天取经的路上博客  https://www.cnblogs.com/sylone/p/9497233.html
缥缈的尘埃的博客  https://www.cnblogs.com/atomy/p/12834804.html
晓晨Master的博客  https://www.cnblogs.com/stulzq/p/6880394.html

posted on 2022-01-18 15:06  BigBox777  阅读(1693)  评论(0)    收藏  举报