原文链接: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 中注册的概念的更多相关文章

  1. [翻译]Component Registration in Script System 在脚本系统中注册组件

    Component Registration in Script System 在脚本系统中注册组件   To refer to our component from a script, the cl ...

  2. [翻译] Autofac 入门文档

    原文链接:http://docs.autofac.org/en/latest/getting-started/index.html 在程序中使用Autofac的基本模式是: 用控制反转(IoC)的思想 ...

  3. [翻译] Autofac 控制范围和生命周期

    原文链接:http://docs.autofac.org/en/latest/lifetime/index.html Lifetime 是指服务的实例在程序中存活多久 – 从最初的实例化到清理(dis ...

  4. 【翻译Autofac的帮助文档】1.入门指南

    [写在前面]尝试做完一件工作之外自我觉得有意义的一件事,那就从翻译Autofac的帮助文档吧. 入门指南 将Autofac集成你的应用程序的步骤通常很简单,一般是: 时刻以IOC(控制反转)的思想来规 ...

  5. PowerShell_零基础自学课程_5_自定义PowerShell环境及Powershell中的基本概念

    PowerShell_零基础自学课程_5_自定义PowerShell环境及Powershell中的基本概念 据我个人所知,windows下的cmd shell除了能够通过修改系统参数来对其中的环境变量 ...

  6. Unity编程标准导引-Unity中的基本概念-2.1界面概览

    Unity中的基本概念 本文我们介绍Unity中的基本概念,包括:场景.游戏对象.组件.预制件.资源等. 2.1.界面概览 打开Unity之后,我们大概可以看到以上画面,以上画面中即显示了我们最常用到 ...

  7. 初学者Web介绍一些前端开发中的基本概念用到的技术

    Web开发是比较费神的,需要掌握很多很多的东西,特别是从事前端开发的朋友,需要通十行才行.今天,本文向初学者介绍一些Web开发中的基本概念和用到的技术,从A到Z总共26项,每项对应一个概念或者技术. ...

  8. EF6CodeFirst+MVC5+Autofac泛型注册 入门实例

    贴一个EF6 CodeFirst模式结合MVC5和Autofac(泛型注册)的一个入门实例 网上类似的例子实在太少,最近自己也有用到这一块的知识,总结了一下,不要让后人踩了自己踩过的坑. 1:新建三个 ...

  9. [翻译]PYTHON中如何使用*ARGS和**KWARGS

    [翻译]Python中如何使用*args和**kwargs 函数定义 函数调用 不知道有没有人翻译了,看到了,很短,顺手一翻 原文地址 入口 或者可以叫做,在Python中如何使用可变长参数列表 函数 ...

随机推荐

  1. Jquery 选择器 详解

    在线文档地址:http://tool.oschina.net/apidocs/apidoc?api=jquery 各种在线工具地址:http://www.ostools.net/ 一.基本选择器 $( ...

  2. HTML5横竖屏提示

    HTML代码: <div class="screen-prompt"></div> CSS判断代码: /*横竖屏提示*/ @media screen and ...

  3. Python字符串的encode与decode

    首先要搞清楚,字符串在Python内部的表示是unicode编码. 因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unic ...

  4. java线程四种状态

    一个线程可以有四种状态: 1.新(new), 即线程刚刚创建,而并未执行 2.可运行(runnable),意味着一旦时间分片机制有空闲的CPU周期提供给一个线程,那个线程便可立即开始运行.因此,线程可 ...

  5. Java多线程系列--“基础篇”11之 生产消费者问题

    概要 本章,会对“生产/消费者问题”进行讨论.涉及到的内容包括:1. 生产/消费者模型2. 生产/消费者实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p ...

  6. ASProtect注册码使用教程|ASProtect SKE(加壳脱壳工具) 2.56 汉化注册版

    ASProtect 是功能非常完善的加壳.加密保护工具.能够在对软件加壳的同时进行各种保护.如:反调试跟踪.自校验及用密钥加密保护等:还有多种限制使用措施,如:使用天数限制.次数限制及对应的注册提醒信 ...

  7. Anliven - 你的学习为何如此低效?!

    拖延 适时学习的本质就是营造机会,具有强烈的时效性,而拖延能够毁灭所有机会! 一个得不到执行的完美计划,比不上一次仓促的执行! 盲目 缺少有效的策略和方法,没有弄清基本的问题(需求--->性质- ...

  8. Spring应用教程-3 依赖关系配置

    注:组件与组件之间的耦合,采用依赖注入管理,但普通的JavaBean属性值,应直接在代码中设置. 1. 注入其他Bean的属性值 我们分析一下,Bean_A的一个属性要依赖Bean_B的一个属性值.这 ...

  9. java设计模式学习(-)

    在我目前看来博客的作用就是笔记了,所谓的原创也仅仅是复制了别人的东西实践了一下,但为了学习记忆确实要记录下来.在这里感谢网络各处的资源,原谅我对资源的索取. 接下来每天开始学以则设计模式或者几天学一则 ...

  10. js基础篇——call/apply、arguments、undefined/null

    a.call和apply方法详解 call方法: 语法:call([thisObj[,arg1[, arg2[,   [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象 ...