Autofac 隐式关系类型

Autofac 支持自动解析特定类型,隐式支持组件与服务间的特殊关系。要充分利用这些关系,只需正常注册你的组件,但是在使用服务的组件或调用Resolve()进行类型解析时,若要改变构造函数的参数,需要使用特定关系类型。
例如,当Autofac注入一个IEnumerable<ITask>类型的构造函数参数时,它不会寻找提供了IEnumerable<ITask>的组件。相反,容器会发现所有的ITask实现且注入它们。
(别担心,下面有例子显示各种类型的用法和它们的意思)。
注意:对于override这种默认行为,在隐式关系类型中同样存在。

1、支持的关系类型

下面的表格简要说明在Autofac中支持的关系类型和你可以使用的.net类型。每个关系类型有一个详细的描述和用例。
关系 类型 含义
A needs B B Direct Dependency(直接依赖关系)
A needs B at some point in the future Lazy<B> Delayed Instantiation(延迟实例化)
A needs B until some point in the future Owned<B> Controlled Lifetime(控制生命周期)
A needs to create instances of B Func<B> Dynamic Instantiation(动态实例)
A provides parameters of types X and Y to B Func<X,Y,B>

Parameterized  Instantiation(参数化实例化)

A needs all the kinds of B

IEnumerable<B>, IList<B>,

 ICollection<B>

Enumeration(枚举)
A needs to know X about B Meta<B> and Meta<B,X> Metadata Interrogation
A needs to choose B based on X IIndex<X,B> Keyed Service Lookup(键控服务查找)

1.1Direct Dependency (B)--直接依赖

直接依赖关系是最基本的关系--组件A需要服务B。它是通过标准的构造函数和属性注入自动处理的:
public class A
{
public A(B dependency) { ... }
}
注册组件A和B,然后解析:
var builder = new ContainerBuilder();
builder.RegisterType<A>();
builder.RegisterType<B>();
var container = builder.Build(); using(var scope = container.BeginLifetimeScope())
{
// B 自动注入到 A 中
var a = scope.Resolve<A>();
}

1.2Delayed Instantiation (Lazy<B>)--惰性依赖

惰性依赖不会被实例化,直到第一次使用它的时候。它出现在依赖不常用或构造昂贵的地方。要充分利用这一优势,在构造函数 A 中使用一个Lazy<B>:
public class A
{
Lazy<B> _b; public A(Lazy<B> b) { _b = b } public void M()
{
// 在第一次调用 M() 方法时,创建组件 B
_b.Value.DoSomething();
}
}
如果你有一个惰性依赖,而你也需要元数据,可以使用Lazy<B,M>代替繁琐的Meta<Lazy<B>, M>。

1.3 Controlled Lifetime (Owned<B>)

一个 owned 依赖,当不在需要它的时候,可以被所有者释放。Owned 依赖关系通常相当于依赖组件执行的一些工作单元。
当与实现了IDisposable的组件一起使用时,这种关系类型是非常有趣的。Autofac 在生命周期结束时自动处理一次性组件,但是那也许意味着组件会存在很长时间;或者你也许只是想控制处理你自己的对象。在这种情况下,你应该使用Owned依赖。
public class A
{
Owned<B> _b; public A(Owned<B> b) { _b = b; } public void M()
{
// _b is used for some task
_b.Value.DoSomething(); // 这里 _b 不再需要,所以,释放他
_b.Dispose();
}
}

在内部,Autofac创建一个微小的lifetime scope,其中对B服务解析,且当你调用它的Dispose()时,这个lifetime scope 将被处理。这也就意味着,释放B也将释放它的依赖关系,除非这些依赖是共享的。

这也意味着,如果你有一个InstancePerLifetimeScope() 注册和一个Owned<B>解析,在同一lifetime scope 不同的地方使用同一类型,你可能会得到不同的实例,这个例子展示了这个问题:
var builder = new ContainerBuilder();
builder.RegisterType<A>().InstancePerLifetimeScope();
builder.RegisterType<B>().InstancePerLifetimeScope();
var container = builder.Build(); using(var scope = container.BeginLifetimeScope())
{
// 这里解析一个使用了 InstancePerLifetimeScope() 的 类型 B ;
var b1 = scope.Resolve<B>();
b1.DoSomething(); // 这个和上面的 b1 一样。
var b2 = scope.Resolve<B>();
b2.DoSomething(); // 在类型 A 中使用的类型 B 和其他地方的 B 不是同一个
var a = scope.Resolve<A>();
a.M();
}

设计就是这样的,因为你不想让一个组件处理 B 下的一切事情。然而,如果你不知道,它可能会导致一些混乱。

 
如果你想要一直控制B的处理,可以使用ExternallyOwned()方法注册B。

1.4 Dynamic Instantiation (Func<B>)--动态实例化

使用自动生成函数,可以让你有效的调用Resolve<T>,而不会捆绑你的组件到Autofac上。如果你需要创建一个给定服务的多个实例,可以使用这种关系类型,或者你不确定你是否会需要一个服务,或者希望在运行时决定。在WCF 集成的场景下这种关系也非常有用,此处你需要创建一个新的服务代理在断层通道之后。
使用这种关系类型推荐使用Lifetime scopes.如果您注册一个对象为InstancePerDependency(),并多次调用Func<B>,每次你将得到一个新实例。但是,如果你注册一个对象为SingleInstance(),并调用Func<B>来多次解析对象,你每次都会得到同一个对象实例。
这种关系的一个示例如下:
public class A
{
Func<B> _b; public A(Func<B> b) { _b = b; } public void M()
{
var b = _b();
b.DoSomething();
}
}

1.5Parameterized Instantiation (Func<X, Y, B>)

您也可以使用自动生成函数,来传递强类型的参数给解析功能。可以选择是在注册过程中传递参数还是手动解析过程中传递:
public class A
{
Func<int, string, B> _b; public A(Func<int, string, B> b) { _b = b } public void M()
{
var b = _b(42, "http://hel.owr.ld");
b.DoSomething();
}
}

在内部,Autofac将它们视为类型的参数。这也就意味着,自动生成函数工厂,不能在输入参数列表中有重复的类型。例如,你有这样一个类型:

public class DuplicateTypes
{
public DuplicateTypes(int a, int b, string c)
{
// ...
}
}
你可能会想注册这种类型,它有一个自动生成的函数工厂。您将解析此函数,但是你将无法执行它。
var func = scope.Resolve<Func<int, int, string, DuplicateTypes>>();

// Throws a DependencyResolutionException:
var obj = func(1, 2, "three");

在一个松耦合的场景下,其中的参数需与类型相匹配,你不了解特定对象的构造函数的参数顺序。如果你需要做这样的事情,你应该使用自定义的委托类型来代替:

public delegate DuplicateTypes FactoryDelegate(int a, int b, string c);
然后使用RegisterGeneratedFactory()注册委托:
builder.RegisterType<DuplicateTypes>();
builder.RegisterGeneratedFactory<FactoryDelegate>(new TypedService(typeof(DuplicateTypes)));

现在这个函数将工作:

var func = scope.Resolve<FactoryDelegate>();
var obj = func(1, 2, "three");
你有另一种方式,就是使用使用委托工厂。
如果您决定使用内置的自动生成函数行为(Func<X,Y,B>),并且每个类型只解析一个函数,它将可以工作,但同一类的所有构造函数参数你会得到相同的输入。
var func = container.Resolve<Func<int, string, DuplicateTypes>>();

// This works and is the same as calling
// new DuplicateTypes(1, 1, "three")
var obj = func(1, "three");

使用这种关系类型且使用委托工厂时推荐使用Lifetime scopes.如果您注册一个对象为InstancePerDependency(),并多次调用Func<X,Y,B>,每次你将得到一个新实例。但是,如果你注册一个对象为SingleInstance(),并调用Func<X,Y,B>多次解析对象,你每次都会得到同一个对象实例即使你传递不同的参数。仅仅传递不同的参数不能打破lifetime scope。

1.6 Enumeration (IEnumerable<B>, IList<B>, ICollection<B>)

枚举类型的依赖提供相同服务(接口)的多种实现。在类似消息处理程序的场景下是非常有帮助的,其中一个消息进来且注册多个处理程序来处理消息。
例如,有一个依赖接口的定义像这样:
public interface IMessageHandler
{
void HandleMessage(Message m);
}

此外,你有一个用户的依赖关系,你需要有多个注册和用户需要的所有注册的依赖关系:

public class MessageProcessor
{
private IEnumerable<IMessageHandler> _handlers; public MessageProcessor(IEnumerable<IMessageHandler> handlers)
{
this._handlers = handlers;
} public void ProcessMessage(Message m)
{
foreach(var handler in this._handlers)
{
handler.HandleMessage(m);
}
}
}

您可以使用隐枚举关系类型很容易地做到这一点。只需注册所有的依赖和用户,并且当你解析用户,它所匹配的所有依赖将被解析为一个枚举:

var builder = new ContainerBuilder();
builder.RegisterType<FirstHandler>().As<IMessageHandler>();
builder.RegisterType<SecondHandler>().As<IMessageHandler>();
builder.RegisterType<ThirdHandler>().As<IMessageHandler>();
builder.RegisterType<MessageProcessor>();
var container = builder.Build(); using(var scope = container.BeginLifetimeScope())
{
// When processor is resolved, it'll have all of the
// registered handlers passed in to the constructor.
var processor = scope.Resolve<MessageProcessor>();
processor.ProcessMessage(m);
}

如果没有匹配的项目在容器中注册,枚举将返回一个空集。也就是说,使用上面的例子,如果你没有注册任何IMessageHandler 实现时,它将中断:

// This throws an exception - none are registered!
scope.Resolve<IMessageHandler>();
但是,此处将继续工作:
// This returns an empty list, NOT an exception:
scope.Resolve<IEnumerable<IMessageHandler>>();

你可能认为,如果你使用这种关系注入东西,你会得到一个空值。相反,你会得到一个空列表。

1.7Metadata Interrogation (Meta<B>, Meta<B, X>)

Autofac元数据功能允许您将任意数据与您在解析时决定使用的服务关联起来。如果你想在使用组件时做出这些决定,使用Meta<B>关系,它将为您提供所有的对象元数据的字符串/对象 词典:
public class A
{
Meta<B> _b; public A(Meta<B> b) { _b = b; } public void M()
{
if (_b.Metadata["SomeValue"] == "yes")
{
_b.Value.DoSomething();
}
}
}

你也可以使用 strongly-typed 元数据, 通过在 Meta<B,X> 关系中指定元数据类型:

public class A
{
Meta<B, BMetadata> _b; public A(Meta<B, BMetadata> b) { _b = b; } public void M()
{
if (_b.Metadata.SomeValue == "yes")
{
_b.Value.DoSomething();
}
}
}
如果你有一个惰性依赖也需要元数据,你可以使用Lazy<B,M> 代替Meta<Lazy<B>, M>

1.8 Keyed Service Lookup (IIndex<X, B>)

当你有很多的特定项目(如了IEnumerable<B>关系)时,但你想要选择一个基于服务的key,您可以使用IIndex<X,B>关系。首先,使用key注册您的服务:
var builder = new ContainerBuilder();
builder.RegisterType<DerivedB>().Keyed<B>("first");
builder.RegisterType<AnotherDerivedB>().Keyed<B>("second");
builder.RegisterType<A>();
var container = builder.Build();
然后使用IIndex<X,B>得到一个keyd服务字典:
public class A
{
IIndex<string, B> _b; public A(IIndex<string, B> b) { _b = b; } public void M()
{
var b = this._b["first"];
b.DoSomething();
}
}

2、组成关系类型

关系类型可以组成,所以:
IEnumerable<Func<Owned<ITask>>>
正确的解释为:
  • All implementations, of
  • Factories, that return
  • Lifetime-controlled
  • ITask services

3、关系类型和容器独立

  在Autofac中,自定义关系类型是基于标准的.NET类型的,不会强迫你绑定你的应用程序与Autofac紧密结合。他们为你的容器配置一个编程模型,它与你写的其他组件(相对于不必知道很多特定容器扩展点和API还可能集中配置)的方式一致。
  例如,您仍然可以创建一个自定义的ITaskFactory在你的核心模型中,但是需要提供一个基于Func<Owned<ITask>> 的AutofacTaskFactory实现。
  请注意,某些关系是基于Autofac中的类型的(例如,IIndex<X,B>)。利用这些关系类型你至少有一个Autofac的引用,即使您在实际解析服务时选择使用不同的IoC容器。

Autofac官方文档翻译--二、解析服务--2隐式关系类型的更多相关文章

  1. Autofac官方文档翻译--二、解析服务--1解析参数传递

    Autofac 传递解析参数 注册组件公开相应的服务之后,你可以从container构造器和子lifetime scopes 中解析服务.使用Resolve()方法来实现: var builder = ...

  2. Autofac官方文档翻译--一、注册组件--1注册概念

    官方文档:http://docs.autofac.org/en/latest/register/registration.html 一.注册概念 使用Autofac 注册组件,通过创建一个Contai ...

  3. 《前端之路》之二:数据类型转换 && 隐式转换 || 显式转换

    目录 02:数据类型转换 && 隐式转换 || 显式转换 02:数据类型转换 && 隐式转换 || 显式转换 在上一个章节中,我们介绍了 JavaScript 的基本的 ...

  4. 初步swift语言学习笔记2(可选类型?和隐式可选类型!)

    作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/28904115 转载请注明出处 假设认为文章对你有所帮助.请通过留言 ...

  5. Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL

    Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL 1.1. $ 美元字符, php 黑头1 1.2. 默认变量的范围和声明:1 1.3 ...

  6. Autofac官方文档翻译--一、注册组件--2传递注册参数

    官方文档:http://docs.autofac.org/en/latest/register/parameters.html 二.Autofac 传递注册参数 当你注册组件时能够提供一组参数,可以在 ...

  7. C++隐式类类型转化

    隐式类类型转换:可以用 单个形参来调用 的构造函数定义了从 形参类型 到 该类类型 的一个隐式转换 class Person { public: Person(): mName()name, mAge ...

  8. Orchard官方文档翻译(二) 安装 Orchard

    原文地址:http://docs.orchardproject.net/Documentation/Installing-Orchard 想要查看文档目录请用力点击这里 最近想要学习了解orchard ...

  9. android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据

    补充:关于PHP服务端可能出现的问题: 如果你刚好也像我一样,用php实现的服务端程序,采用的是apache服务器,那么虚拟主机的配置可能会影响到android应用的调试!! 在android应用中访 ...

随机推荐

  1. P6631 [ZJOI2020] 序列

    可以将问题用形象的方式来表述.给定一排点,第 \(i\) 个点有它需要的覆盖次数 \(a_i\).有两种线段,一种能覆盖连续的一些点,称其为连续线段:另一种能覆盖相邻间隔为 \(1\) 的一些点,称其 ...

  2. Codeforces Round #672 (Div. 2) B. Rock and Lever题解(思维+位运算)

    题目链接 题目大意 给你一个长为n(n<=1e5)的数组,让你求有多少对a[i]和a[j] (i!=j)满足a[i]&a[j]>a[i]^a[j] 题目思路 这些有关位运算的题目肯 ...

  3. java数组作为函数返回值

    1 //将一个二维数组行和列元素互换.存到另一个二维数组 2 package test; 3 4 public class test1_8 { 5 public static int[][] huhu ...

  4. vue获取微博授权URL

    1.在Vue**页面加载时动态发送请求获取微博授权url 1.1 在 components\common\lab_header.vue 中写oauth动态获取微 博授权URL // 获取微博登录地址 ...

  5. Mysql命令、常用函数

    一.sql命令行 查看数据库 show database : 选择使用的数据库 use 数据库名  : 查看表 show tables ; 查询表 select * from 表名     高版本my ...

  6. CentOS SSH安全和配置无密码登录

    CentOS ssh默认监听端口 22端口,允许root用户ssh登录.server投入生产后很有必要更改默认ssh监听端口和禁止root登录. 步骤1:确认安装有ssh包 [appuser@su17 ...

  7. Window .NetCore Nginx

    1.首先去官网下载Nginx 官网地址:http://nginx.org/en/download.html 2.下载好之后,我的文件是放在D:\InstallFiles\Nginx\nginx-1.1 ...

  8. IEEE754标准浮点数表示与舍入

    原文地址:https://blog.fanscore.cn/p/26/ 友情提示:本文排版不太好,但内容简单,请耐心观看,总会搞懂的. 1. 定点数 对于一个无符号二进制小数,例如101.111,如果 ...

  9. 【2020.11.28提高组模拟】T1染色(color)

    [2020.11.28提高组模拟]T1染色(color) 题目 题目描述 给定 \(n\),你现在需要给整数 \(1\) 到 \(n\) 进行染色,使得对于所有的 \(1\leq i<j\leq ...

  10. Spring Boot 实现看门狗功能 (调用 Shell 脚本)

    需要实现看门狗功能,定时检测另外一个程序是否在运行,使用 crontab 仅可以实现检测程序是否正在运行,无法做到扩展,如:手动重启.程序升级(如果只需要实现自动升级功能可以使用 inotify)等功 ...