Autofac系列二、解析服务
一、解析服务
在注册完组件并暴露相应的服务后,可以从创建的容器或其子生命周期中解析服务,让我们使用Resolve()方法来实现:
var builder = new ContainerBuilder(); builder.RegisterType<MyComponent>().As<IService>(); var container = builder.Build(); using(var scope = container.BeginLifetimeScope()) { var service = scope.Resolve<IService>(); }
我们应该注意到示例是从生命周期中解析服务而非直接从容器中,当然,你也应该这么做。
有时在我们的应用中也许可以从根容器中解析组件,然而这么做有可能会导致内存泄漏。推荐你总是从生命周期中解析组件。以确保服务实例被妥善的释放和垃圾回收。
解析服务时,Autofac自动链接起服务所需的整个依赖链上不同层级并解析所有的依赖来完整的构建服务,如果你有处理不当的循环依赖或缺少了必须的依赖,你将得到一个DependencyResolutionException.
如果你不清楚一个服务是否被注册了,你可以通过ResolveOptional()或TryResolve()尝试解析:
// If IService is registered, it will be resolved; if // it isn't registered, the return value will be null. var service = scope.ResolveOptional<IService>(); // If IProvider is registered, the provider variable // will hold the value; otherwise you can take some // other action. IProvider provider = null; if(scope.TryResolve<IProvider>(out provider)) { // Do something with the resolved provider value. }
ResolveOptional()和TryResolve()本质上都只是保证某个特定的服务已成功注册。如果该组件已注册就可以解析成功。如果解析本身失败(例如某些必需的依赖未注册),你依然会得到DependencyResolutionException。如果你不清楚服务解析本身是否会成功并需要在解析成功或失败时进行不同的操作,将Resolve()包裹在try..cath块中。
1、可使用的参数类型
Autofac提供了多种不同的参数匹配机制:
- NamedParameter: 通过名称匹配目标参数
- TypeofParameter: 通过匹配目标参数(需要匹配具体的类型)
- ResolvedParameter: 复杂参数的匹配
NamedParameter和TypeParameter只支持常量值。
ResolvedParameter 可以用于提供不同的值来从容器中动态获取对象, 例如, 通过名字解析服务
2、反射组件的参数
当注册一个基于反射的组件时,类型的构造方法也许会需要一个无法从容器中解析出来的参数,你可以在注册时提供该值。
假如有个configuration reader需要传入一个configuration section name:
public class ConfigReader : IConfigReader { public ConfigReader(string configSectionName) { // Store config section name } // ...read configuration based on the section name. }
你可以使用lambda表达式组件:
builder.Register(c => new ConfigReader("sectionName")).As<IConfigReader>();
或者在反射组件注册时传参:
// Using a NAMED parameter: builder.RegisterType<ConfigReader>() .As<IConfigReader>() .WithParameter("configSectionName", "sectionName"); // Using a TYPED parameter: builder.RegisterType<ConfigReader>() .As<IConfigReader>() .WithParameter(new TypedParameter(typeof(string), "sectionName")); // Using a RESOLVED parameter: builder.RegisterType<ConfigReader>() .As<IConfigReader>() .WithParameter( new ResolvedParameter( (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName", (pi, ctx) => "sectionName"));
3、Lambda表达式组件的参数
使用lambda表达式组件注册,不是在注册是传入参数,而是在服务解析时传入具体的参数。
在组件注册表达式时,你可以通过改变委托签名使用传入参数进行注册,代替接收一个IComponentContext参数,一个IConpontentContext和IEnumerable<Parameter>参数:
// 使用两个参数来注册委托 // c = The current IComponentContext to dynamically resolve dependencies // p = An IEnumerable<Parameter> with the incoming parameter set builder.Register((c, p) => new ConfigReader(p.Named<string>("configSectionName"))) .As<IConfigReader>();
当你解析IConfigReader时,你的lambda将使用这些参数来传入值:
var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "sectionName"));
1.4、不显示调用Resolve传参
Autofac支持两种功能,它们允许你自动生成支持强类型参数列表的服务“工厂”,这些参数列表会在解析时用到,这是一种创建需要参数组件实例的更清晰的方法。
- 委托工厂 允许你定义工厂委托方法。
- Func<T> 隐式关系类型 可以提供自动生成工厂的方法。
2、隐式关系类型
为了支持组件和服务之间的特殊关系,Autofac支持隐式的自动解析特定服务,想要
2.1、支持的关系类型
- 直接依赖(B)
- 延迟实例话(Lazy<B>)
- 可控生命周期(OWned<B>)
- 动态实例化(Func<B>)
- 带参数实例化(Func<X,Y,B>)
- 可遍历型(IEnumerable<B>,IList<B>,ICollection<B>)
- 元数据审查(Metadata Interrogation (Meta<B>,Meta<B,X>))
- 键控服务的查找(Keyed Service Lookup(IIndex<X,B>))
2.2、组合关系类型
2.3、关系类型和容器独立性