注册概念:
我们通过创建 ContainerBuilder 来注册 组件 并且告诉容器哪些 组件 暴露了哪些 服务.组件 可以通过 反射 创建; 通过提供现成的 实例创建; 或者通过 lambda 表达式 来创建.

ContainerBuilder 包含一组 Register() 方法来帮你实现以上操作.每个组件暴露一个或多个 服务 ,他们使用 ContainerBuilder 上的 As() 方法连接起来.

 //注册类
var builder = new ContainerBuilder(); builder.RegisterType<ConsoleLogger>().As<ILogger>(); //注册实例 var output = new StringWriter(); builder.RegisterInstance(output).As<TextWriter>(); //注册lambda表达式 builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>(); var container = builder.Build(); using(var scope = container.BeginLifetimeScope())
{
var reader = scope.Resolve<IConfigReader>();
}

通过类型注册

builder.RegisterType<ConsoleLogger>();

builder.RegisterType(typeof(ConfigReader));

当使用基于反射的组件时, Autofac 自动为你的类从容器中寻找匹配拥有最多参数的构造方法.

指定构造函数
你可以使用 UsingConstructor 方法和构造方法中一系列代表参数类型的类型来 手动指定一个构造函数

builder.RegisterType<MyComponent>().UsingConstructor(typeof(ILogger), typeof(IConfigReader));

通过实例注册:

有时候你也许会希望提前生成一个对象的实例并将它加入容器以供注册组件时使用:

var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

当你这样做时你需要考虑一些事情, Autofac 自动处理已注册组件的释放 ,你也许想要自己来控制生命周期而不是让Autofac来帮你调用 Dispose 释放你的对象. 在这种情况下, 你需要通过 ExternallyOwned 方法来注册实例:

var output = new StringWriter();
builder.RegisterInstance(output)
.As<TextWriter>().ExternallyOwned();

通过Lambda表达式注册:
当创建实例不再是简单的调用构造方法, 就需要使用Lambda表达式

使用Lambda表达式注册,在解析时才会创建对象

builder.Register(c => new A(c.Resolve<B>()));

表达式提供的参数 c 是 组件上下文 (一个 IComponentContext 对象) , 使用该上下文参数可以满足其他依赖的成功引入

builder.Register(c =>
{
return new Session(c.Resolve<IDBConfig>())
.SetQueryFilter(new Data.DeleteableQueryFilter())
.SetQueryFilter(new Data.OwnUserQueryFilter(c.Resolve<IGlobalValueProvider>().User.OwnUserID))
.SetInterceptor(new Data.SqlTracerDataInterceptor())
.SetInterceptor(new GlobalDataInterceptor(c.Resolve<IServiceLocator>()))
;
})
.InstancePerLifetimeScope();

参数注入,你可以使用表达式和属性初始化来填充参数。ResolveOptional 方法会尝试解析, 但如果服务没有注册, 不会抛出错误.,但是如果服务成功注册但是无法成功解析, 依然会抛出错误.

builder.Register(c => new A(){ MyB = c.ResolveOptional<B>() });

在大多数情况下不推荐使用参数注入. 使用其他的例如 空对象模式, 重载构造方法或参数默认值这些方式, 用构造方法注入可以创建出可选依赖的更清爽, "不可变" 组件.

通过参数值选择具体的实现,一大好处是具体的类型可以是多种多样的. 指定具体的类型通常可以在运行时完成, 而不仅仅是配置时期:

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 - 哪个类会被实例化取决于运行时期间提供的account ID.

示例中 创建方法的参数 通过第二个可选的参数 p 传入.
解析时可以这样:

var card = container.Resolve<CreditCard>(new NamedParameter("accountId", ""));

注册泛型组件

Autofac支持开放泛型. 使用 RegisterGeneric() 方法:

builder.RegisterGeneric(typeof(NHibernateRepository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();

当容器请求一个匹配的服务类型时, Autofac将会找到对应的封闭类型的具体实现:

// return an NHibernateRepository<Task>
var tasks = container.Resolve<IRepository<Task>>();

暴露服务:
 默认地, 类型注册时大部分情况下暴露它们自身:

builder.RegisterType<CallLogger>();

你可以让一个组件暴露任意数量的服务:

builder.RegisterType<CallLogger>()
.As<ILogger>()
.As<ICallInterceptor>();

暴露服务后, 你就可以解析基于该服务的组件了. 但请注意, 一旦你将组件暴露为一个特定的服务, 默认的服务 (组件类型) 将被覆盖:

scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>(); // This WON'T WORK anymore because we specified
// service overrides on the component:
scope.Resolve<CallLogger>();

如果你既想组件暴露一系列特定的服务, 又想让它暴露默认的服务, 可以使用 AsSelf 方法:

builder.RegisterType<CallLogger>()
.AsSelf()
.As<ILogger>()
.As<ICallInterceptor>();

这样所有的解析就都能成功了:

scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();
scope.Resolve<CallLogger>();

默认注册,如果不止一个组件暴露了相同的服务, Autofac将使用最后注册的组件作为服务的提供方:

builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<FileLogger>().As<ILogger>();

上例中, FileLogger 将会作为 ILogger 默认的服务提供方因为它是最后被注册的.
想要覆盖这种行为, 使用 PreserveExistingDefaults() 方法修改:

builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<FileLogger>().As<ILogger>().PreserveExistingDefaults();

有条件的注册

注解
有条件的注册自Autofac 4.4.0 引入
大多数情况下, 像上面那样覆盖默认的注册其实已经足够让我们在运行时成功地解析正确的组件了. 我们可以使用 PreserveExistingDefaults() 保证组件以正确的顺序被注册; 对于复杂的条件和行为我们也可以利用 lambda表达式/委托 注册处理的很不错了.
但依然有些场景应该是你不想碰到的:
你不想在程序中有些功能在正常运作的情况下某个组件还会出现. 例如, 如果你解析了 IEnumerable<T> 的服务(一堆服务), 所有实现了这些服务的已注册组件都将被返回, 不管你是否使用了 PreserveExistingDefaults(). 大多数情况下这样也行, 但在某些极端情况下你不希望如此.
你只想要在其他一些组件 未被 注册的情况下才注册组件; 或者只想在其他一些组件 已被 注册的情况下. 你不会从容器中解析出你不想要的东西, 并且你也不用修改已经创建的容器. 能够基于其他的注册情况来进行有条件的组件注册非常好用.
这边有两种好用的注册扩展方法:
OnlyIf() - 提供一个表达式, 使用一个 IComponentRegistry 来决定注册是否发生.
IfNotRegistered() - 有其他服务已被注册的情况下阻止注册发生的快捷方法.
这些方法在 ContainerBuilder.Build() 时执行并且以实际组件注册的顺序执行. 下面是一些展示它们如何工作的示例:
var builder = new ContainerBuilder();

builder.RegisterType<ServiceA>()
.As<IService>();
builder.RegisterType<ServiceB>()
.As<IService>()
.IfNotRegistered(typeof(IService));

builder.RegisterType<HandlerA>()
.AsSelf()
.As<IHandler>()
.IfNotRegistered(typeof(HandlerB));
builder.RegisterType<HandlerB>()
.AsSelf()
.As<IHandler>();
builder.RegisterType<HandlerC>()
.AsSelf()
.As<IHandler>()
.IfNotRegistered(typeof(HandlerB));

builder.RegisterType<Manager>()
.As<IManager>()
.OnlyIf(reg =>
reg.IsRegistered(new TypedService(typeof(IService))) &&
reg.IsRegistered(new TypedService(typeof(HandlerB))));

var container = builder.Build();

注册的配置
你可以 使用 XML 或or 编程式配置 ("模块") 来提供注册的群组或者在运行时改变注册. 对于一些动态的注册的生成或者有条件的注册逻辑, 你可以 使用 Autofac 模块 .
动态提供的注册
Autofac模块 是引入动态注册逻辑或简单切面功能的最简单的方法. 例如, 你可以使用一个模块 动态地在被解析的服务上附加一个log4net logger实例.
如果想要完成更加动态的操作, 例如添加对新的 隐式关系类型 的支持, 你可以 在高级概念章节查看注册源模块.

 

属性注入

尽管构造方法参数注入是一种传值给组件的首选的方法, 但你同样可以使用属性或方法注入来传值.

如果组件是一个 lambda表达式组件, 使用对象构造器:
builder.Register(c => new A { B = c.Resolve<B>() });

激活后事件:
builder.Register(c => new A()).OnActivated(e => e.Instance.B = e.Context.Resolve<B>());

自动绑定属性:
builder.RegisterType<A>().PropertiesAutowired();

绑定一个属性:
builder.RegisterType<A>().WithProperty("PropertyName", propertyValue);

如果你没法使用注册lambda表达式, 你可以添加一个激活时事件

builder
.Register<MyObjectType>()
.OnActivating(e => {
var dep = e.Context.Resolve<TheDependency>();
e.Instance.SetTheDependency(dep);
});

Autofac注册组件详解的更多相关文章

  1. Android笔记——四大组件详解与总结

     android四大组件分别为activity.service.content provider.broadcast receiver. ------------------------------- ...

  2. vue.js基础知识篇(6):组件详解

    第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...

  3. admin组件详解

    admin组件详解 先根据admin组件启动流程复习下django项目启动至请求过来发生的事 1将admin组件注册进app 2django项目启动 3在运行到定制的admin时执行其下面的apps文 ...

  4. SpringCloud及其组件详解

    SpringCloud及其组件详解 1.Spring Cloud 1.1 Spring Cloud和Dubbo的区别图解 1.2 微服务的技术栈 2.Spring Cloud 概述 2.1 Sprin ...

  5. 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解

    本文转自:http://www.topeetboard.com 视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.c ...

  6. Android中Intent组件详解

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...

  7. Echars 6大公共组件详解

    Echars 六大组件详解 : title  tooltip toolbox legend  dataZoom visualMap 一.title标题详解 myTitleStyle = { color ...

  8. Angular6 学习笔记——组件详解之组件通讯

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

  9. Angular6 学习笔记——组件详解之模板语法

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...

随机推荐

  1. 【转】struts2的ActionInvocation分析(action调度者)

    一个ActionInvocation实例代表一个action的执行状态,持有拦截器和将要执行的action的实例. defaultActionInvocation是其默认实现.下面是定义在该类中的部分 ...

  2. NOIP 2011 提高组初赛错题简析

    Preface 好久没做初赛题了,据说今年的审核会更加严苛,作为一名去年未PY时只有\(92\)分的蒟蒻,我今年看来是\(90\)分都莫得了 然而今年也没怎么看重初赛,结果现在才来做,翻车到了\(84 ...

  3. AGC037C Numbers on a Circle(神奇思路)

    Atcoder 全是神仙题-- 先变成能不能从 \(b\) 到 \(a\).操作变成一个数减掉旁边两个数. 考虑里面最大的且不和 \(a\) 中相等的那个数.它两边的数此时都不能操作,否则就减到非正数 ...

  4. UAC简介

    用户帐户控制 (User Account Control) 是Windows Vista(及更高版本操作系统)中一组新的基础结构技术,可以帮助阻止恶意程序(有时也称为“恶意软件”)损坏系统,同时也可以 ...

  5. ​LeetCode 26:删除排序数组中的重复项 Remove Duplicates from Sorted Array

    给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成. Give ...

  6. jquery 属性名修改

    jquery   attr <script> // 获取class 名 $(".box").attr("class") // 修改或添加class名 ...

  7. Preface_英语

    这是一本游戏指南.没错,你没有 看错,这就是一本游戏指南.当然,这 本指南针对的只是名为"英文"的游戏. 把英文和电子游戏比较一下,我们 会发现,这两者有惊人的相似之处. 第一,它 ...

  8. python threading ThreadPoolExecutor

    线程池,为什么要使用线程池:1. 线程中可以获取某一个线程的状态或者某一个任务的状态,以及返回值2. 当一个线程完成的时候我们主线程能立即知道3. futures可以让多线程和多进程编码接口一致 获取 ...

  9. Kubernetes service 三种类型/NodePort端口固定

    Kubernetes service 三种类型 • ClusterIP:默认,分配一个集群内部可以访问的虚拟IP(VIP)• NodePort:在每个Node上分配一个端口作为外部访问入口• Load ...

  10. MySQL for OPS 07:主从复制

    写在前面的话 对于企业而言,在互联网这一块其实最重要的是数据.保证数据的安全性,稳定性是作为运维人的基本工作职责.于是为了数据安全性,引进了数据备份,bin log 等.但这并不意味着有这些就足够了. ...