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. Mac 上超好用的代码对比工具 beyond compare,对比json差异

    导读 昨天下午,公司业务跑不通,然后开发组长让架构师联系我,给我发一个json和部署到dev上的微服务url,让我去测试下,将发来的json放到json.cn上愣是解析不出来,我就用之前的json请求 ...

  2. 安装Linux软件时遇到这个问题,如何解决?

    提示 Could not get lock /var/lib/dpkg/lock 报错? 有些小伙伴在使用 apt 包管理器更新或安装软件时,可能会遇到过诸如以下的错误提示: E: Could not ...

  3. LaTeX中的浮动体

    浮动体代码及注释: 显示效果:

  4. 超稳攻略!Rancher 2.3手动轮换证书,保护集群安全!

    本文转自Rancher Labs 前 言 Rancher 2.3正式发布已经一年,第一批使用Rancher 2.3的用户可能会遇到Rancher Server证书过期,但是没有自动轮换的情况.这会导致 ...

  5. 基于gin的golang web开发:Gin技术拾遗

    本文是对前几篇文章的一些补充,主要包含两部分:单元测试和实际项目中使用路由的小问题. 拾遗1:单元测试 Golang单元测试要求代码文件以_test结尾,单元测试方法以Test开头,参数为*testi ...

  6. charles 常用功能(八)重定向

    1.点击鼠标右键 点击保存就保存到桌面上了 效果图 在123.txt中修改 然后另存为 点击红圈处 然后再次发送请求

  7. AlanShan数据库课程设计报告

    目    录 1.绪论.... 2 1.1前言... 2 1.2社会背景... 2 1.3超市背景... 3 2.系统可行性研究.... 4 2.1 技术可行性研究... 4 2.2 经济可行性研究. ...

  8. 极简python教程02:基础变量,删繁就简

    python极简教程已经开赛,如果错过说明可以回翻: 极简python教程:赛前说明 借这个机会,我再讲讲我的教程和其他网上的教程的区别: 1 我分享的内容,是我在工作中会高频使用的语法,是精华内容 ...

  9. uniapp 微信授权登陆

    准备工作: 1.微信开发者账号 2.AppId .AppSecret (这些可以在开放平台申请到) 第一步 添加移动应用,仔细添加上述信息: 审批通过后,即刻 第二步 打开uniapp,开启OAuth ...

  10. LeetCode初级算法之数组:36 有效数独

    有效数独 题目地址:https://leetcode-cn.com/problems/valid-sudoku/ 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. ...