[翻译] Autofac 中注册的概念
原文链接:http://docs.autofac.org/en/latest/register/registration.html
所谓注册组件,是指创建 ContainerBuilder 的实例,并告诉它哪些组件暴露哪些服务。
组件可以用反射创建,可以提供已经创建好的对象的实例,还可以用拉姆达表达式创建。ContainerBuilder 有一组 Register 方法来进行装配。
每个组件暴露一到多个服务,这些服务用生成器的 As 方法连接起来。
// 创建生成器,生成器用来注册组件和服务
var builder = new ContainerBuilder(); // 注册暴露接口的类型
builder.RegisterType<ConsoleLogger>().As<ILogger>(); // 注册已存在的对象实例
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>(); // 注册创建对象的表达式
builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>(); // 生成容器,完成注册,准备解析对象
var container = builder.Build(); // 现在可以用 Autofac 解析服务,例如,
// 这行代码将执行拉姆达表达式解析 IConfigReader 服务
using(var scope = container.BeginLifetimeScope())
{
var reader = container.Resolve<IConfigReader>();
}
反射组件
用类型注册
由反射生成的组件通常按类型注册:
var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>();
builder.RegisterType(typeof(ConfigReader));
使用基于反射的组件时,Autofac 选取可用参数最多的构造函数。比如,一个类有三个构造函数:
public class MyComponent
{
public MyComponent() { /* ... */ }
public MyComponent(ILogger logger) { /* ... */ }
public MyComponent(ILogger logger, IConfigReader reader) { /* ... */ }
}
并使用以下代码注册:
var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
var container = builder.Build(); using(var scope = container.BeginLifetimeScope())
{
var component = container.Resolve<MyComponent>();
}
解析时,Autofac 发现 ILogger 已注册,但 IConfigReader 未注册。于是选取第二个构造函数,因为它是可从容器获取参数最多的构造函数。
重要说明: 通过 RegisterType 注册的组件必须是具体类型。组件可以将抽象类和接口暴露为服务,但不能把抽象类和接口注册为组件。Autofac 要创建组件的实例,抽象类和接口不能实例化。
指定构造函数
注册组件时,通过使用 UsingConstructor 方法并指定构造函数的参数类型列表,可以选择特定的构造函数,这将覆盖自动选择:
builder.RegisterType<MyComponent>()
.UsingConstructor(typeof(ILogger), typeof(IConfigReader));
注意,解析时必须保证参数可用,否则将出现错误。参数既可以在注册时传递,也可以在解析时传递。
实例组件
可使用 RegisterInstance 方法把预先生成的实例注册到容器:
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();
由于 Autofac 会自动清理对象,如果想自己控制对象的生命周期,而不是由 Autofac 调用 Dispose,就需要用 ExternallyOwned 方法来注册实例:
var output = new StringWriter();
builder.RegisterInstance(output)
.As<TextWriter>()
.ExternallyOwned();
将Autofac 集成到现有程序时,注册实例是一个技巧。组件可能会用到单例模式提供的服务,与其直接引用单件,不如将单件注册到容器:
builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();
这样就消除了组件对单件的引用,改由容器提供。
实例暴露的默认服务是实例的具体类型,参考“服务和组件”一节。
拉姆达表达式组件
反射是创建组件的好方式。但是,如果创建逻辑超出简单的调用构造函数时,反射就不够用了。
Autofac 可以接受一个创建组件的委托或拉姆达表达式:
builder.Register(c => new A(c.Resolve<B>()));
参数 c 是组件上下文(IComponentContext),组件在此上下文中注册。可以用它从容器解析出其他值,来辅助创建组件。应使用组件上下文,而不是闭包来访问容器,这很要紧,只有这样资源清理和嵌套容器才不会出问题。
额外的依赖可以用此上下文参数来满足,例如, A的构造函数需要B类型的参数,并且 B 可能有其他依赖项。
表达式创建的组件暴露的默认服务是从表达式推断出的返回类型。
以下是反射方式不能胜任,但拉姆达表达式工作良好的例子。
复杂参数
构造函数参数不能总是声明为简单常量。与其为使用 XML 配置语法创建特定类型的值而大伤脑筋,不如使用类似的代码:
builder.Register(c => new UserSession(DateTime.Now.AddMinutes()));
(当然, session 过期时间在配置文件中更好 – 这里只是说明个大概)
属性注入
尽管有更好的属性注入方式,仍然可以使用表达式和属性初始化器来组装属性:
builder.Register(c => new A(){ MyB = c.ResolveOptional<B>() });
ResolveOptional 方法尝试解析值,即使服务没有注册,也不会抛出异常。(如果服务已注册但不能正确解析仍然会抛出异常) 这是解析服务的选项之一。
多数情况下不推荐属性注入。如果组件有可选的依赖项,通过空对象模式, 重载构造函数,或构造函数参数默认值等替代方案,就可以用构造函数注入的方式来创建整洁的,“稳定的(immutable)”组件。
通过参数值选择实现类
把组件的创建动作隔离出来后,依赖项的具体类型可以变换,这是一个很大的好处。变换通常在运行时完成,而不单是配置时:
builder.Register<CreditCard>(
(c, p) =>
{
var accountId = p.Named<string>("accountId");
if (accountId.StartsWith(""))
{
return new GoldCard(accountId);
}
else
{
return new StandardCard(accountId);
}
});
本例,CreditCard 有两个实现类,GoldCard 和 StandardCard,使用哪个类是在运行时由 accountId 决定的。
例子里的第二个参数名为 p,这是可选参数。
解析组件:
var card = container.Resolve<CreditCard>(new NamedParameter("accountId", ""));
声明创建CreditCard 实例的委托,使用委托工厂,可以得到更整洁,类型安全的语法。
开放式泛型组件
Autofac 支持开放式泛型类型。使用 RegisterGeneric 生成器方法进行注册:
builder.RegisterGeneric(typeof(NHibernateRepository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
从容器请求匹配的服务类型时,Autofac 映射到等价的闭合版本:
// Autofac 返回 NHibernateRepository<Task>
var tasks = container.Resolve<IRepository<Task>>();
注册的特定服务类型 (如IRepository<Person>)会覆盖开放式泛型版本。
服务和组件
注册组件时必须告诉 Autofac 它暴露哪些服务。默认情况下,暴露的服务是组件自身的类型:
// 服务是 "CallLogger"
builder.RegisterType<CallLogger>();
组件仅可通过它暴露的服务来解析。对于本例来说:
// 没问题
scope.Resolve<CallLogger>(); // 有问题,因为注册时没有将 ILogger 接口设置为组件的服务
scope.Resolve<ILogger>();
可以让组件暴露多个服务:
builder.RegisterType<CallLogger>()
.As<ILogger>()
.As<ICallInterceptor>();
暴露服务后,就可以通过它解析组件。注意,将组件暴露为特定的服务后,默认服务(组件类型)会被覆盖:
// 以下均可工作:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>(); // 但这一行不再有用,因为指定的服务覆盖了组件类型
scope.Resolve<CallLogger>();
使用AsSelf 方法,可在暴露其他服务的同时也将自身类型暴露为服务:
builder.RegisterType<CallLogger>()
.AsSelf()
.As<ILogger>()
.As<ICallInterceptor>();
这样全部代码都可工作:
// 注册时暴露了合适的服务,因此都起作用
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();
scope.Resolve<CallLogger>();
默认注册
如果多个组件暴露相同的服务,Autofac 将使用最后注册的组件作为服务的默认提供程序:
builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();
在此场景中,FileLogger 是 ILogger 的默认组件,因为它是最后注册的。
使用 PreserveExistingDefaults 方法可以覆盖这个行为:
builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>().PreserveExistingDefaults();
这时,ConsoleLogger 将是默认的 ILogger 因为后面注册的 FileLogger 使用了 PreserveExistingDefaults()。
配置
可使用XML 配置或编程配置(“modules”)成组提供注册信息,或在运行时更改注册信息。 Autofac modules 还可实现注册信息动态生成,或实现条件注册逻辑。
动态注册
Autofac modules 是引入动态注册逻辑或简单正交特性的最简单方式。例如,将 log4net logger 实例动态附加到正在解析的服务。
如果需要更加动态的行为,比如添加隐式关联类型支持,请参考check out the registration sources section in the advanced concepts area.
[翻译] Autofac 中注册的概念的更多相关文章
- [翻译]Component Registration in Script System 在脚本系统中注册组件
Component Registration in Script System 在脚本系统中注册组件 To refer to our component from a script, the cl ...
- [翻译] Autofac 入门文档
原文链接:http://docs.autofac.org/en/latest/getting-started/index.html 在程序中使用Autofac的基本模式是: 用控制反转(IoC)的思想 ...
- [翻译] Autofac 控制范围和生命周期
原文链接:http://docs.autofac.org/en/latest/lifetime/index.html Lifetime 是指服务的实例在程序中存活多久 – 从最初的实例化到清理(dis ...
- 【翻译Autofac的帮助文档】1.入门指南
[写在前面]尝试做完一件工作之外自我觉得有意义的一件事,那就从翻译Autofac的帮助文档吧. 入门指南 将Autofac集成你的应用程序的步骤通常很简单,一般是: 时刻以IOC(控制反转)的思想来规 ...
- PowerShell_零基础自学课程_5_自定义PowerShell环境及Powershell中的基本概念
PowerShell_零基础自学课程_5_自定义PowerShell环境及Powershell中的基本概念 据我个人所知,windows下的cmd shell除了能够通过修改系统参数来对其中的环境变量 ...
- Unity编程标准导引-Unity中的基本概念-2.1界面概览
Unity中的基本概念 本文我们介绍Unity中的基本概念,包括:场景.游戏对象.组件.预制件.资源等. 2.1.界面概览 打开Unity之后,我们大概可以看到以上画面,以上画面中即显示了我们最常用到 ...
- 初学者Web介绍一些前端开发中的基本概念用到的技术
Web开发是比较费神的,需要掌握很多很多的东西,特别是从事前端开发的朋友,需要通十行才行.今天,本文向初学者介绍一些Web开发中的基本概念和用到的技术,从A到Z总共26项,每项对应一个概念或者技术. ...
- EF6CodeFirst+MVC5+Autofac泛型注册 入门实例
贴一个EF6 CodeFirst模式结合MVC5和Autofac(泛型注册)的一个入门实例 网上类似的例子实在太少,最近自己也有用到这一块的知识,总结了一下,不要让后人踩了自己踩过的坑. 1:新建三个 ...
- [翻译]PYTHON中如何使用*ARGS和**KWARGS
[翻译]Python中如何使用*args和**kwargs 函数定义 函数调用 不知道有没有人翻译了,看到了,很短,顺手一翻 原文地址 入口 或者可以叫做,在Python中如何使用可变长参数列表 函数 ...
随机推荐
- SAP Business One系统功能介绍
SAP Business One(简称SAP B One)是一套价格合理.易于实施的综合业务管理解决方案.该解决方案专为中小型企业量身打造,可确保实现公司发展.提高可盈利性和控制力度以及实现业务流程的 ...
- 安卓中的数据存储方式以及ContentProvider的简单介绍
1.介绍android的数据存储方式 File存储 sharedPrefrence存储方式 conmtentprovider sqlitedatabase 网络存储 2.请介绍下ContentPr ...
- CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
最近在测试的过程中, 发现了SpringBoar的一个问题: SpringBoard[53] <Error>: CGContextTranslateCTM: invalid context ...
- javase基础复习攻略《七》
容器是什么?通俗的讲容器指可以装其它东西的器皿,前面我们提到的数组便是容器的一种,容器的概念在JAVA中便可以理解为用来存储其它对象的器皿.本篇就让我们一起来认识一下JAVA为我们提供的容器类. 1. ...
- poj 2594Treasure Exploration(有向图路径可相交的最小路径覆盖)
1 #include<iostream> #include<cstring> #include<algorithm> #include<cstdio> ...
- 接触Matlab10年后的一个总结,随时使用Matlab要掌握的一些要点
不记得上一次写超过20行的matlab程序是什么时候了,大概是2013年吧,那个时候写过2篇文章,实际用到了 一些matlab的内容,超过200行的matlab程序应该要追溯到2011年了,最近为了帮 ...
- 清空文件下的SVN控制文件
代码如下,复制代码为txt文件,更改后缀为“.bat”,把文件放到,需要删除的文件的顶端文件夹内,点击执行. @echo on color 2f mode con: cols= lines= @REM ...
- Java之HashMap在多线程情况下导致死循环的问题
PS:不得不说Java编程思想这本书是真心强大.. 学习内容: 1.HashMap<K,V>在多线程的情况下出现的死循环现象 当初学Java的时候只是知道HashMap<K,V& ...
- ssl证书生成:cer&jks文件生成摘录
一.生成.jks文件 1.keystore的生成: 分阶段生成: keytool -genkey -alias yushan(别名) -keypass yushan(别名密码) -keyalg ...
- html/css基础篇——html代码编写过程中的几个警惕点
本文想说的警惕点与浏览器兼容无关,主要是几个本人在项目中遇到的几个小问题的总结,问题虽小,但是却有时很困扰人,在此记录一下,如果后期有此类问题会持续添加到这里. 1.内联标签之间的空格 正常情况下书写 ...