[翻译] 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中如何使用可变长参数列表 函数 ...
随机推荐
- SpringSide 部署showcase项目出现 JAX-RS (REST Web Services) 2.0 can not be installed错误!
maven+springmvc错误 JAX-RS (REST Web Services) 2.0 can not be installed 项目problem提示错误 JAX-RS (REST Web ...
- Ionic 入门
什么是lonic 简单来说lonic就是一款HTML5移动端应用开发框架,通过配合AngularJS和Cordova/PhoneGap可以开发一款移动端app,值得注意的是它创建的app是混合移动应用 ...
- Deep learning:四十(龙星计划2013深度学习课程小总结)
头脑一热,坐几十个小时的硬座北上去天津大学去听了门4天的深度学习课程,课程预先的计划内容见:http://cs.tju.edu.cn/web/courseIntro.html.上课老师为微软研究院的大 ...
- nginx常见内部参数,错误总结
1.日志简介 nginx日志主要有两种:访问日志和错误日志.访问日志主要记录客户端访问nginx的每一个请求,格式可以自定义:错误日志主要记录客户端访问nginx出错时的日志,格式不支持自定义.两种日 ...
- 加速Web开发的9款知名HTML5框架
与手工编码比起来,HTML5框架在准确性和正确率方面给予了保证.大多数HTML5框架都会有一个组合或者包含一些额外的组件,比如jQuery Scripts.CSS3样式表则以改善多媒体特征的功能性和响 ...
- Sandcastle帮助文档生成器使用介绍
一.软件介绍 Sandcastle是一个管理类库的文档编译器,是用于编译发布组件(Assembly)信息的一个工具,这个工具通过反射和Xslt技术,可以从dll文件及其xml注释(命令行编 ...
- 勤能补挫-简单But易错的JS&CSS问题总结
错误频率较高的JS&CSS问题 勤能补拙,不管是哪门子技术,在实践中多多总结,开发效率慢慢就会提升.本篇介绍几个经常出错的JS&CSS问题,包括事件冒泡.(使用offset.scrol ...
- 过滤eWebeditor等富文本中html标签,获得纯文本信息
/// <summary> /// 过滤html标签 /// </summary> /// <param name="Htmlstring">& ...
- Entity Framework 6 执行Linq to Entities异常"p__linq__1 : String truncation: max=0, len=2, value='测试'"
场景再现 我需要查询公司名称包含给定字符串的公司,于是我写了下面的测试小例子: var condition = "测试"; var query = from b in db.Com ...
- 孙鑫MFC学习笔记14:网络编程
1.OSI 2.TCP/IP与OSI对应关系 3.Socket 4.客户机/服务器模式 5.Windows Sockets 6.套接字类型 7.面向连接的socket编程 8.面向无连接的socket ...