1 注册的概念和方式
使用autofac 的ContainerBuilder 来注册组件(components---通常指实现类),并把它的服务(service---通常指接口,抽象类,类实例)暴露给调用方。
autofac 通过相似的 Register() 方法来进行注册,通过As() 方法来公开服务
1) 创建一个ContainerBuilder 来注册组件和服务
var builder = new ContainerBuilder();
|
2) 通过反射类型来进行注册 。
builder.RegisterType<ConsoleLogger>.As<ILogger>(); |
3) 通过一个实例来进行注册
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();
|
如果用实例来注册,每次解析时,获得结果都是同一个实例。
builder.RegisterInstance(MyInstance.Instance).ExternallyOwned(); |
ExternallyOwned() 方法配置组件不会被释放。
4) 通过Lamda表达式的方式来构建注册
// Register expressions that execute to create objects... builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>(); |
5) 通过RegisterGeneric()方法 支持泛型接口注册
//注册
builder.RegisterGeneric(typeof(List<>)) .As(typeof(IList<>)) .InstancePerLifetimeScope(); //解析代码
var tasks = container.Resolve<IList<String>>();
|
容器会自动根据传递的 T 类型 进行解析
6) 通过程序集进行注册
// 通过程序集进行注册
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
|
以上代码AsImplementedInterfaces() 方法 会把当前运行的程序集中所有实现了接口的组件进行注册,并把接口作为服务对外公开。
例如: 程序集中包含了 2个接口:IOutput 和 IDateWriter
和三个实现类:class ConsoleOutput : IOutput
class CurentDateWriter :IDateWriter
class MyComponent:IOutput,IDateWriter
那么这三个都会被注册。如果程序集中还有其他类型实现了接口,也会被自动注册,除了 IDisposable 接口。
7) 通过链式语法把一个组件注册为多个服务对外公开
// 链式注册 builder.RegisterType<MyComponent>() .AsSelf() // 公开MyComponent 的实例 .As<IOutput>() // 公开IOutput 的 实例 .As<IDataWriter>(); // 公开 IDataWriter 的实例 |
所以如下三种解析都可以正常工作
// 都可以正常执行
scope.Resolve<IOutput>();
scope.Resolve<IDataWriter>();
scope.Resolve<MyComponent>();
|
2 条件注册
1) 注册时添加筛选条件
示例1:在程序集中,通过Where() 筛选名称以“Repository” 结尾的类进行注册,并制定服务进行公开
// asm 为一个程序集变量 builder.RegisterAssemblyTypes(asm) .Where(t => t.Name.EndsWith("Repository")) .As<IRepository>(); |
通过Where 提供Lamda 集合操作,可以对类进行筛选。
示例2 : 通过 Except() 方法排除不需要注册的类
// 排除 MyUnwantedType 不注册
builder.RegisterAssemblyTypes(asm)
.Except<MyUnwantedType>();
|
3 Autofac 构造函数注册
1) 当组件类有多个构造函数,通过RegisterType 方法注册的组件后,解析服务时,会通过反射形式,根据构造函数参数从多到少的方式匹配。
// 多构造函数组件 public class MyComponent { public string Name {get;set;} public MyComponent() { Name = "no parameter"; } public MyComponent(IOutput output) { Name = string.Format("parameter is {0}", output.ToString()); } public MyComponent(IOutput output, IDateWriter writer) { Name = string.Format("parameter is {0} and {1}", output.ToString(),writer.ToString()); } } class Program { static IContainer container { get; set; } static void Main(string[] args) { var builder = new ContainerBuilder(); // 注册类型,通过反射注册 builder.RegisterType<MyComponent>(); builder.RegisterType<ConsoleOutput>().As<IOutput>(); container = builder.Build(); //构建容器 //使用 using (var scope = container.BeginLifetimeScope()) { var component = container.Resolve<MyComponent>(); Console.WriteLine(component.GetType()); Console.WriteLine(component.Name); } Console.ReadKey(); } } |
- Autofac 根据上诉代码,在解析MyComponent 时,会先看容器中,是否有已注册的 ILoger 和IConfigReader 的 服务
- Autofac 发现只有一个ILoger 服务,而没有 IConfigReader 的服务,于是自动选择 带一个ILoger 构造参数的 构造函数实例化 M
运行结果:
2) 通过UsingConstructor()指定构造函数注册
builder.RegisterType<MyComponent>() .UsingConstructor(typeof(ILogger), typeof(IDataWriter)); |
- 当容器中,存在ILoger 和IDataWriter 的 注册时,当Resolve<MyComponent> 时,容器会返回2个构造函数的实例
- 当容器中,只存在Iloger 的注册时,因为是手动用2个构造函数进行祖册,此时Resolve<MyComponent> 会抛出异常,因为IDataWriter 的注册实例不存在。
4 Autofac 自动解析规则
1) 如果有多个组件注册,并以同一个服务对外公开,autofac 会取最后一个最后一个注册的组件,作为服务。
builder.Register<ConsoleLogger>().As<ILogger>(); builder.Register<FileLogger>().As<ILogger>(); |
以上注册代码,FileLogger 组件会被默认公开,因为它是最后一个注册为ILogger 的服务。
5 为注册组件传递参数
下面有一个读取配置节点值的 类
public class ConfigReader : IConfigReader { public ConfigReader(string configSectionName) { // Store config section name } // ...read configuration based on the section name. } |
1 ) 通过Lamda表达式 传递参数
builder.Register(c => new ConfigReader("sectionName")).As<IConfigReader>(); |
“sectionName” 就是传递节点名称的值。
2 ) 通过方法WithParameters 传递参数
// 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")); |