2.1、注册
你通过创建ContainerBuilder对象来注册组件并且将组件暴露的服务告知builder。
组件可以通过反射来进行创建(通过注册一个特定的.NET类型或者开放的泛型(by registering a specific .NET type or open generic));通过提供现成的实例(你创建的对象的一个实例);或者通过lambda表达式(通过一个匿名方法来执行对象的创建)。你可以通过ContainerBuilder中一系列重载的Register()方法来设置创建组件的方式。
组件通过ContainerBuilder中的As()方法来暴露一个或者多个服务。
//为了注册组件而创建ContainerBuilder对象 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.Builder(); //现在你就可以使用autofac来解析(resolve)服务了。列如 //下面代码将会执行注册IConfigReader服务的lambda表达式 using(var scope = container.BeginLifetimeScope()) { var reader = scope.Resolve<IConfigReader>(); }
2.1.1、反射组件
通过类型进行注册
通过类型注册的组件,是一种典型的以反射组件:
var builder = new ContainerBuilder(); builder.RegisterType<ConsoleLogger>(); builder.RegisterType(typeof(ConsoleLogger));
在使用基于反射进行实例化的组件时,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.Builder(); using(var scope = container.BeginLifetimeScope()) { var component = scope.Resolve<MyComponent>(); }
当解析组件时,Autofac会发现ILogger已经被注册到容器中,但是IConfigReader并未注册。在这种情况下,第二个构造函数将被调用,该构造函数所需的参数与我们向容器中注册的组件最为匹配。
对于反射进行创建的组件,有一个需要注意的地方:任何通过RegisterType方法注册的组件必须是一个具体类型(concrete type)。虽然可以将一个抽象类或者接口暴露为服务,但却不能讲抽象类或者接口注册为组件。这种限制的背后是有道理的:Autofac将实例化你所注册的组件,但不能new一个抽象类或接口,new的必须是抽象类或者接口的实现!
指定构造函数
通过UsingConstructor方法以及提供表示构造函数中的参数类型的列表,你可以指定一个特定的构造函数来取代Autofac自行匹配的构造函数,比如:
builder.RegisterType<MyComponent>().UsingConstructor(typeof(ILogger), typeof(IConfigReader));
注意,现在任然需要提供所选构造函数必须的的参数,否则在解析(Resolve)组件时会产生一个错误。你可以在注册时传递参数,也可以在解析(Resolve)时提供参数。
2.1.2、实例组件
在某些情况下,可能会需要一个预生成的实例并将其添加到注册组件所使用的容器中。你可以通过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能够轻松的处理。它(单例模式的实例)可以作为一个实例注册到容器中,而不是直接将容器中的组件与该单例模式的实例进行绑定。(限于英文水平这段是我自己理解出来的,如果按原文来译的话我实在没法通顺的将其译出来,原文如下:
Registering provided instances is also handy when integrating Autofac into an existing application where a singleton instance already exists and needs to be used by components in the container. Rather than tying those components directly to the singleton, it can be registered with the container as an instance:)
builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();
容器将会取代单例模式对这个实例进行管理。
一个实例默认暴露的服务是其具体实例的类型,参阅下面的“服务 VS 组件”。
2.1.3、表达式组件(占坑,一会继续)