IoC之AutoFac(一)——简单使用和组件注册
阅读目录
一、AutoFac简单使用
1 namespace AutofacDemo 2 { 3 class Program 4 { 5 //用于存储容器的属性 6 private static IContainer Container { get; set; } 7 static void Main(string[] args) 8 { 9 //获取容器 10 var builder = new ContainerBuilder(); 11 //注册组件(类),并公开服务(接口) 12 builder.RegisterType<ConsoleOutput>().As<IOutput>(); 13 builder.RegisterType<TodayWriter>().As<IDateWriter>(); 14 //存储容器 15 Container = builder.Build(); 16 17 WriteDate();//输出当前的日期 18 } 19 public static void WriteDate() 20 { 21 using (var scope=Container.BeginLifetimeScope()) 22 { 23 //解析组件(功能类似于new一个IDateWriter的实现类) 24 var writer = scope.Resolve<IDateWriter>(); 25 writer.WriteDate(); 26 } 27 } 28 } 29 //输出功能接口 30 public interface IOutput 31 { 32 void Write(string content); 33 } 34 //控制台输出类 35 public class ConsoleOutput : IOutput 36 { 37 public void Write(string content) 38 { 39 Console.WriteLine(content); 40 } 41 } 42 //输出日期的接口 43 public interface IDateWriter 44 { 45 void WriteDate(); 46 } 47 48 //输出今天日期的类 49 public class TodayWriter : IDateWriter 50 { 51 private IOutput _output;//输出日期的类依赖IOutput 52 public TodayWriter(IOutput output) 53 { 54 this._output = output; 55 } 56 public void WriteDate() 57 { 58 this._output.Write(DateTime.Today.ToShortDateString()); 59 } 60 } 61 }
应用程序执行过程:
“WriteDate”方法向Autofac询问IDateWriter。
Autofac看到IDateWriter映射到TodayWriter,所以开始创建一个TodayWriter。
Autofac认为TodayWriter在其构造函数中需要一个IOutput。
Autofac看到IOutput映射到ConsoleOutput,所以创建一个新的ConsoleOutput实例。
Autofac使用新的ConsoleOutput实例来完成TodayWriter的构建。
Autofac将“WriteDate”的完全构建的TodayWriter返回给消费者。
之后,如果您希望应用程序编写不同的日期,则可以实现不同的IDateWriter,然后在应用程序启动时更改注册。你不必改变任何其他类。 这样就实现了反转控制!
二、注册
使用Autofac 注册组件的任务是:通过创建一个ContainerBuilder并且告知builder 哪些组件公开哪些服务。
2.1 注册方式
1 // 创建组件/服务注册的容器 2 var builder = new ContainerBuilder(); 3 5 ////----------------------一、反射组件----------------//// 6 // 反射组件方法1---通过类型注册 7 builder.RegisterType<ConsoleOutput>().As<IOutput>(); 8 // 反射组件方法2---通过构造函数注册 9 builder.RegisterType<TodayWriter>() 10 .UsingConstructor(typeof(IOutput)).As<IDateWriter>(); 11 12 13 14 ////--------------------二、实例组件----------------------//// 15 // 注册创建的对象实例,把该实例作为一个服务 16 var output = new ConsoleOutput(); 17 builder.RegisterInstance(output).As<IOutput>(); 18 //不影响系统中原有的单例 19 builder.RegisterInstance(MySingleton.GetInstance()).ExternallyOwned(); 20 21 22 23 ////------------------三、Lambda表达式组件---------------//// 24 //参数c是组件上下文(一个IComponentContext对象) ,此处该组件被创建。您可以使用它来解析来自于容器的其他组件,从而帮助你创建组件。 25 26 builder.Register(c => new TodayWriter(c.Resolve<IOutput>())).As<IDateWriter>(); 27 28 //分离组件创建最大的好处就是具体类型可以变化 29 //一个组件分离的Demo:注册一个信用卡组件 30 builder.Register<CreditCard>( 31 (c, p) => 32 { 33 var accountId = p.Named<string>("accountId"); 34 if (accountId.StartsWith("9")) 35 { 36 return new GoldCard(accountId); 37 } 38 else 39 { 40 return new StandardCard(accountId); 41 } 42 }); 43 //根据参数不同获取不同种类的信用卡类实例 44 var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345")); 45 46 47 ////-------------------------四、泛型注册------------------//// 48 //泛型注册,可以通过容器返回List<T> 如:List<string>,List<int>等等 49 builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>)).InstancePerLifetimeScope(); 50 using (IContainer container = builder.Build()) 51 { 52 IList<string> ListString = container.Resolve<IList<string>>(); 53 } 54 55 56 ////-----------------------五、服务与组件-------------------//// 57 builder.RegisterType<ConsoleLogger>();//默认把注册类型作为公开服务 58 builder.RegisterType<ConsoleLogger>().As<ILogger>().As<IMyLogger>();//可以公开任意多个服务,但是默认的注册类型会被覆盖 59 builder.RegisterType<ConsoleLogger>().AsSelf().As<ILogger>();//加上AsSelf()默认的注册类型不会被覆盖 60 //当公开的服务重名时,服务使用的默认组件是后注册的那个 61 builder.Register<ConsoleLogger>().As<ILogger>(); 62 builder.Register<FileLogger>().As<ILogger>();//默认使用后注册的FileLogger 63 //阻止该默认行为,使用builder.Register<FileLogger>().As<ILogger>().PreserveExistingDefaults(),这样不会覆盖以前注册的组件 64 65 66 ////-----------六、条件注册(IfNotRegistered,OnlyIf)-------//// 67 //HandlerC不会被注册 68 builder.RegisterType<HandlerA>() 69 .AsSelf() 70 .As<IHandler>() 71 .IfNotRegistered(typeof(HandlerB)); 72 builder.RegisterType<HandlerB>() 73 .AsSelf() 74 .As<IHandler>(); 75 builder.RegisterType<HandlerC>() 76 .AsSelf() 77 .As<IHandler>() 78 .IfNotRegistered(typeof(HandlerB)); 79 80 //只有IService和HandlerB都注册时,才会去注册Manager 81 builder.RegisterType<Manager>() 82 .As<IManager>() 83 .OnlyIf(reg => 84 reg.IsRegistered(new TypedService(typeof(IService))) && 85 reg.IsRegistered(new TypedService(typeof(HandlerB)))); 86 87 88 // 编译容器完成注册且准备对象解析 89 Container = builder.Build(); 90 91 // 现在你可以使用 Autofac 解析服务. 例如,这行将执行注册的lambda表达式对于 IConfigReader 服务. 92 using (var scope = Container.BeginLifetimeScope()) 93 { 94 var writer = Container.Resolve<IOutput>(); 95 var dataWriter = Container.Resolve<IDateWriter>(); 96 writer.Write("111");//输出111 97 dataWriter.WriteDate();//输出当前日期 98 Console.ReadKey(); 99 }
2.2 带参数注册
1、参数类型
Autofac支持的参数类型有三种:
NamedParameter - 通过名称匹配目标参数
TypedParameter - 通过类型匹配目标参数(需要精确匹配类型)
ResolvedParameter - 灵活的参数匹配
例子:一个读取配置文件的类,构造器需要传入参数:节点名称
1 public class ConfigReader : IConfigReader 2 { 3 public ConfigReader(string configSectionName) 4 { 5 // 存储配置的节点名称 6 } 7 8 // ...读取基于节点名称的配置 9 }
2、反射组件参数
Parameters with Reflection Components----注册时传入
1 // 使用一个命名参数: 2 builder.RegisterType<ConfigReader>() 3 .As<IConfigReader>() 4 .WithParameter("configSectionName", "mySectionName"); 5 6 // 使用一个类型参数: 7 builder.RegisterType<ConfigReader>() 8 .As<IConfigReader>() 9 .WithParameter(new TypedParameter(typeof(string), "mySectionName")); 10 11 // 使用一个解析参数: 12 builder.RegisterType<ConfigReader>() 13 .As<IConfigReader>() 14 .WithParameter( 15 new ResolvedParameter( 16 (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName", 17 (pi, ctx) => "mySectionName"));
3、Lambda表达式组件参数
Parameters with Lambda Expression Components----解析时传入
在组件注册表达式中,你可以通过改变委托签名使用传入参数进行注册,代替仅仅接收一个IComponentContext参数,一个IComponentContext 和 IEnumerable<Parameter>参数:
1 builder.Register((c, p) => 2 new ConfigReader(p.Named<string>("configSectionName"))) 3 .As<IConfigReader>();
当你解析参数时,你的lambda将使用这些参数来传入值:
var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "mySectionName"));
参考文章:
1、https://blog.csdn.net/chiyueqi/article/details/52446569
2、http://www.yuanjiaocheng.net/Autofac/register-parameters.html
阅读目录
一 Resolve方法
在您的组件注册了适当的服务后,您可以从内置的容器和子生命周期范围中解析服务。 您可以使用Resolve()方法,还是使用上篇的例子:
1 private static IContainer Container { get; set; } 2 static void Main(string[] args) 3 { 4 var builder = new ContainerBuilder(); 5 //注册服务 6 builder.RegisterType<ConsoleOutput>().As<IOutput>(); 7 Container = builder.Build(); 8 //解析服务 9 using (var scope=Container.BeginLifetimeScope()) 10 { 11 var output= scope.Resolve<IOutput>(); 12 output.Write("outputsomething"); 13 Console.ReadKey(); 14 } 15 }
解析服务时,Autofac将自动链接服务的整个依赖关系层次,并解析完全构建服务所需的任何依赖关系。 如果您的循环依赖关系被错误地处理,或者缺少必需的依赖关系,那么您将得到一个DependencyResolutionException。
二 TryResolve和ResolveOptional方法
如果您有可能注册或可能不被注册的服务,您可以使用ResolveOptional()或TryResolve()来尝试对服务进行有条件解决:
1 //解析服务 2 using (var scope = Container.BeginLifetimeScope()) 3 { 4 //1.ResolveOptional:IOutput注册的话解析,未被注册返回null 5 var service = scope.ResolveOptional<IOutput>(); 6 7 //2.TryResolve:IOutput注册的话解析获取一个类型实例,未注册返回null 8 IOutput output = null; 9 //如果有IOutPut服务,执行输出 10 if (scope.TryResolve<IOutput>(out output)) 11 { 12 output.Write("outputsomething"); 13 } 14 Console.ReadKey(); 15 }
三 解析服务时传参
解析服务的时候,您可能会发现需要将参数传递给Autofac。 (如果您在注册时知道值,则可以在注册中提供它们,详细见上篇。)Resolve()方法使用可变长度的参数列表在注册时接受相同的参数类型。
3.1 可用参数类型
Autofac提供了几种不同的参数匹配策略:
-
NamedParameter - 按名称匹配目标参数
-
TypedParameter - 按类型匹配目标参数(需要精确类型匹配)
-
ResolvedParameter - 灵活的参数匹配
NamedParameter和TypedParameter只能提供常量值。
ResolvedParameter可以用作提供从容器动态检索的值的方法,例如。 通过名称解析服务。
3.2 带反射组件的参数
当您解析基于反射的组件时,类型的构造函数可能需要您需要根据运行时值指定的参数,这在注册时不可用。 您可以在Resolve()方法调用中使用一个参数来提供该值。
假设您有一个配置读取器,需要传递一个配置部分名称:
1 public class ConfigReader : IConfigReader 2 { 3 public ConfigReader(string configSectionName) 4 { 5 // 存储配置的节点名称 6 } 7 8 // 读取基于节点名称的配置 9 }
您可以将参数传递给Resolve()调用,如下所示:
//注册 builder.RegisterType<ConfigReader>().As<IConfigReader>(); //解析 var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "mySectionName"));
如果您有多个参数,只需通过Resolve()方法将它们全部传递:
var service = scope.Resolve<AnotherService>( new NamedParameter("id", "service-identifier"), new TypedParameter(typeof(Guid), Guid.NewGuid()), new ResolvedParameter( (pi, ctx) => pi.ParameterType == typeof(ILog) && pi.Name == "logger", (pi, ctx) => LogManager.GetLogger("service")));
3.3 具有Lambda表达式组件的参数
使用lambda表达式组件注册,您需要在lambda表达式中添加参数处理,因此当Resolve()调用传入时,可以利用它们。
在组件注册表达式中,您可以通过更改用于注册的代理签名来使用传入参数。 而不是仅仅使用IComponentContext参数,而是接收一个IComponentContext和一个IEnumerable <Parameter>:
相关实例:
// c 是当前组件上下文 // p 是IEnumerable<Parameter>参数集合 builder.Register((c, p) =>new ConfigReader(p.Named<string>("configSectionName"))) .As<IConfigReader>();
现在,当您解析IConfigReader时,您的lambda将使用传递的参数:
//解析时传参 var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "sectionName"));
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
2017-06-29 Entity Framework 4.0 recipes 读书笔记2 ExecuteStoreQuery()
2016-06-29 javascript的setTimeout()用法总结,js的setTimeout()方法