阅读目录
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、参考
Autofac 是一个非常nice的控制反转IOC容器,它管理类之间的依赖关系,从而使应用在规模及复杂性增长的情况下依然可以轻易地修改,适用于 .NET Core、ASP.NET Core、.NET 4.5.1+、通用 Windows 应用程序等。如果你现在还不懂什么是控制反转?什么是依赖注入?请看我前面的文章:C#技术栈入门到精通系列7——依赖注入 ,本节是对之前这篇文章的进一步延伸。使用Autofac需要Nuget安装开源库 Autofac 6.3.0 。
Autofac可以通过反射、实例或者Lambda来注册组件,接下来分别介绍一下注册过程。
类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 }
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 }
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 }
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 }
类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 }
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 }
- 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 }
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 }
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 }
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 }
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 }
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 }
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 }
反射是创建组件的一个很好的默认选择,但是有的时候需要根据传入的参数来决定注册成不同的组件。比如根据传入的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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
程序集级别的依赖注入是指接口和实现的依赖不使用配置文件或硬代码实现 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 }
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 }
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 }
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 }
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 }
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 。
下面举例的配置文件是同样的,分别用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 }
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>
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 }
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 }
注意:您可以在配置中指定“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 }
元素名称 |
描述 |
有效值 |
---|---|---|
|
唯一需要的东西。组件的具体类(如果在非默认程序集中,则为程序集限定)。 |
可以通过反射创建的任何 .NET 类型名称。 |
|
组件公开的一系列服务。每个服务必须有一个 |
可以通过反射创建的任何 .NET 类型名称。 |
|
一个布尔值,指示组件是否应该自动激活。 |
|
|
一个布尔值,指示是否应启用组件的属性(设置器)注入。 |
|
|
|
|
|
与组件关联的元数据值数组。每个项目指定 |
任何元数据值。 |
|
允许您控制生命周期范围是处理组件还是您的代码处理。 |
|
|
键值对,其中每个元素的名称是构造函数参数的名称,值是要注入的值。 |
组件类型的构造函数中的任何参数。 |
|
键值对,其中每个元素的名称是属性的名称,值是要注入的值。 |
组件类型上的任何可设置属性。 |
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 }
元素名称 |
描述 |
有效值 |
---|---|---|
|
唯一需要的东西。模块的具体类(如果在非默认程序集中,则为程序集限定)。 |
从它派生的任何 .NET 类型名称 |
|
键值对,其中每个元素的名称是构造函数参数的名称,值是要注入的值。 |
模块类型的构造函数中的任何参数。 |
|
键值对,其中每个元素的名称是属性的名称,值是要注入的值。 |
模块类型上的任何可设置属性。 |
使用泛型的时候注入不清楚类型怎么写的,可以用代码 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 }
AOP面向切面编程是实现程序功能的统一维护的一种技术,主要有两种实现方式:①编译时静态植入,优点是效率高,缺点是缺乏灵活性,代表收费的PostSharp。②动态代理,动态为目标类型创建代理,通过代理调用实现拦截。Autofac的AOP是通过动态代理 Autofac.Extras.DynamicProxy 实现的。实现步骤:
①使用时候需要Nuget安装 Autofac.Extras.DynamicProxy 6.0.0 包
②创建拦截器,需要实现 IInterceptor 接口
③注册拦截器到Autofac容器
④启动拦截器,两个方法:EnableInterfaceInterceptors()方法会动态创建一个接口代理;EnableClassInterceptors()方法会创建一个目标类的子类代理类,这里需要注意的是只会拦截虚方法,重写方法。
⑤给拦截内容添加特性标注,有两种方法:第一种:给类型加上特性 Intercept ;第二种:在注册类型到容器的时候动态注入拦截器。
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 }
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 }
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 }
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 }
官网 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