通过创建ContainerBuilder并配置暴露的service(接口或者类型)来使用Autofac注册我们的组件。
组件(Components) 可以通过反射, 对象实例,或者lambda表达式来创建. ContainerBuilder有一系列的Register()方法来实现组件的注册。
ContainerBuilder中每个组件都能通过As()方法来暴露他们一个或多个service.
// Create the builder with which components/services are registered. var builder = new ContainerBuilder(); // Register types that expose interfaces... builder.RegisterType<ConsoleLogger>().As<ILogger>(); // Register instances of objects you create... var output = new StringWriter(); builder.RegisterInstance(output).As<TextWriter>(); // Register expressions that execute to create objects... builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>(); // Build the container to finalize registrations // and prepare for object resolution. var container = builder.Build(); // Now you can resolve services using Autofac. For example, // this line will execute the lambda expression registered // to the IConfigReader service. using(var scope = container.BeginLifetimeScope()) { var reader = container.Resolve<IConfigReader>(); }
反射组件 Reflection Components
通过类型注册
通过反射生成组件最典型的方法是通过类型注册:
var builder = new ContainerBuilder(); builder.RegisterType<ConsoleLogger>(); builder.RegisterType(typeof(ConfigReader));
当使用基于反射的组件时,Autofac会自动使用容器中最可能获取到的参数来构造实例你的类。
例如,你的类有3个构造函数
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。在这个例子中,第二个构造函数会被执行,因为容器中具有最符合的参数。
注意:任何你注册的Type必须都是实类型,这意味着你不能直接注册抽象类或者接口。应为在解析组件时,对象可能会被new实例化,而接口或者抽象类是无法直接实例化的。
指定构造函数 Specifying a Constructor
你可以通过方法UsingConstructor手动指定使用哪个构造函数,其参数为指定构造函数的参数类型。
var output = new StringWriter(); builder.RegisterInstance(output).As<TextWriter>();
注意你提供的参数必须能在容器中获取,不然会出现错误。
实例组件 Instance Components
在很多情况下,你可能想要预先生成对象实例并将它注册到容器中。你可以通过使用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到现有项目中时,可能会存在容器中一些组件用到的对象的单例写法。RegisterInstance()也用来处理这种情况,你可以通过容器来注册它们,而不是直接使用它们:
builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();
这确保了单例最后会被排除,并使用容器托管的对象来替代他。
表达式组件 Lambda Expression Components
反射是创建组件相当好的默认选择,尽管当组件创建逻辑超出简单构造函数调用后,事情可能会变混乱。
Autofac支持通过委托或者Lambda表达式来创建组件:
builder.Register(c => new A(c.Resolve<B>()));
提供的参数c是一个组件上下文对象(IComponentContext),在上下文中创建组件。你可以通过它从容器中解析出其他的值来辅助创建你的组件。使用它而不是使用闭包去访问容器是很重要的,为了层叠容器能够被正确支持。
使用此上下文参数能够满足额外的依赖项。在这个例子中,类型A构造函数要求的B类型参数可能还会依赖其他类型参数。表达式创建的组件暴露的默认服务是通过表达式返回类型来推断的。
下面会给出一些例子:
复杂参数
builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));
属性注入
builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));
通过参数值选择实现方式
builder.Register<CreditCard>( (c, p) => { var accountId = p.Named<string>("accountId"); if (accountId.StartsWith("9")) { return new GoldCard(accountId); } else { return new StandardCard(accountId); } });
在这个例子中,CreditCard被两个类实现,分别是GoldenCard和StandardCard,哪种类型被实例取决于输入的卡号。
参数通过可选构造参数p来传入,注册方式可能像这样:
var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));
范型组件
Autofac支持范型,通过使用RegisterGeneric()方法:
builder.RegisterGeneric(typeof(NHibernateRepository<>)) .As(typeof(IRepository<>)) .InstancePerLifetimeScope();
当一个匹配的Service类型被请求时,容器会自动映射它到一个相近的实现类型:
// Autofac将会返回NHibernateRepository<Task>类型 var tasks = container.Resolve<IRepository<Task>>();
指定类型服务的注册将会覆盖掉范型版本。
服务 vs 组件 Services vs. Components
当注册组件时,我们需要告诉Autofac组件暴露了哪种服务。大多数情况下,会暴露组件自身的类型。
// This exposes the service "CallLogger" builder.RegisterType<CallLogger>();
组件仅能被其暴露的服务来解析出实例,这就意味着如下:
// This will work because the component // exposes the type by default: scope.Resolve<CallLogger>(); // 这个将会失败 // tell the registration to also expose // the ILogger interface on CallLogger: scope.Resolve<ILogger>();
你也可以使用任何数量的服务来暴露你的组件:
builder.RegisterType<CallLogger>() .As<ILogger>() .As<ICallInterceptor>();
你可以通过你暴露的服务来解析出组件实例,但这同时意味着默认服务(组件自身类型)将被覆盖:
// These will both work because we exposed // the appropriate services in the registration: scope.Resolve<ILogger>(); scope.Resolve<ICallInterceptor>(); // 失败 // 默认服务被覆盖 scope.Resolve<CallLogger>();
如果你想为组件暴露一系列服务,同时自身类型仍然可用,你需要使用AsSelf方法:
builder.RegisterType<CallLogger>() .AsSelf() .As<ILogger>() .As<ICallInterceptor>();
现在所有服务均可以解析:
// These will all work because we exposed // the appropriate services in the registration: scope.Resolve<ILogger>(); scope.Resolve<ICallInterceptor>(); scope.Resolve<CallLogger>();
默认注册 Default Registrations
如果有多个提供相同service的组件被注册,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将会成为默认组件提供者。
配置文件注册 Configuration of Registrations
你可以使用定义的XML或者代码模块(Module)来实现孕事批量注册或者更改。也可以使用Autofac modules进行一些动态注册生成或者条件注册逻辑。具体请看:http://autofac.readthedocs.org/en/latest/configuration/index.html
动态注册 Dynamically-Provided Registrations
Autofac modules是最简单的方式来引入动态注册逻辑或简单交叉特性。例如,你可以使用module动态地附加一个Log4net实例到解析出的一个service上。请看:http://autofac.readthedocs.org/en/latest/examples/log4net.html