Autofac 组装扫描
Autofac 可以使用约定来查找和注册组件。您可以扫描并注册单个类型,也可以专门针对 Autofac 模块 进行扫描。
类型扫描
也称为约定驱动注册或扫描,Autofac 可根据用户指定的规则从一个程序集中注册一组类型:
var dataAccess = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(dataAccess)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces();
每次调用 RegisterAssemblyTypes()
都只会应用一套规则 - 如果有多个不同的组件集合需要注册,则需要多次调用 RegisterAssemblyTypes()
。
Autofac 还支持使用 RegisterAssemblyOpenGenericTypes()
进行泛型开放类型的程序集扫描。它使用与 RegisterAssemblyTypes()
类似的通用语义:
var dataAccess = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyOpenGenericTypes(dataAccess)
.Where(t => t.Name.StartsWith("MessageHandler"))
.AsImplementedInterfaces();
过滤类型
RegisterAssemblyTypes()
和 RegisterAssemblyOpenGenericTypes()
分别接受一个或多个程序集的参数数组。默认情况下,将注册程序集中所有具体的类 ,包括内部和私嵌套类。您可以使用提供的 LINQ 样式的谓词来过滤要注册的类型集。
在 4.8.0 中添加了 PublicOnly()
扩展方法,使数据封装更加方便。如果您只想注册公开类,请使用 PublicOnly()
:
builder.RegisterAssemblyTypes(asm)
.PublicOnly();
要应用自定义过滤器到注册的类型,使用 Where()
谓词:
builder.RegisterAssemblyTypes(asm)
.Where(t => t.Name.EndsWith("Repository"));
要排除类型进行扫描,请使用 Except()
谓词:
builder.RegisterAssemblyTypes(asm)
.Except<MyUnwantedType>();
Except()
谓词还允许您为特定排除类型自定义注册:
builder.RegisterAssemblyTypes(asm)
.Except<MyCustomizedType>(ct =>
ct.As<ISpecial>().SingleInstance());
可以使用多个过滤器,此时它们将按逻辑与应用。
builder.RegisterAssemblyTypes(asm)
.PublicOnly()
.Where(t => t.Name.EndsWith("Repository"))
.Except<MyUnwantedRepository>();
指定服务
RegisterAssemblyTypes()
和 RegisterAssemblyOpenGenericTypes()
的注册语法是单个类型注册语法的子集,因此像 As()
这样的方法对程序集也适用:
builder.RegisterAssemblyTypes(asm)
.Where(t => t.Name.EndsWith("Repository"))
.As<IRepository>();
As()
和 Named()
方法的额外重载接受决定类型将提供哪些服务的 lambda 表达式:
builder.RegisterAssemblyTypes(asm)
.As(t => t.GetInterfaces()[0]);
与常规组件注册一样,多次调用 As()
将组合在一起。
为了更容易建立常见的约定,已添加了一些额外的注册方法:
方法 | 描述 | 示例 |
---|---|---|
AsImplementedInterfaces() |
将类型注册为提供其公开接口(排除 IDisposable )作为服务。 |
builder.RegisterAssemblyTypes(asm).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces(); |
AsClosedTypesOf(open) |
注册可以赋值给封闭的泛型类型的类型。 | builder.RegisterAssemblyTypes(asm).AsClosedTypesOf(typeof(IRepository<>)); |
AsSelf() |
默认:将类型注册为自身 - 当与其他服务规范一起覆盖默认行为时很有用。 | builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().AsSelf(); |
模块扫描
模块扫描由 RegisterAssemblyModules()
注册方法执行,顾名思义,它会扫描提供的程序集,创建模块实例,然后将它们注册到当前容器构建器。
例如,如果下面两个简单的模块类位于同一个程序集中,并且每个都注册一个组件:
public class AModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new AComponent()).As<AComponent>();
}
}
public class BModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new BComponent()).As<BComponent>();
}
}
不接受类型参数的 RegisterAssemblyModules()
重载将注册程序集中实现 IModule
的所有类。在下面的例子中,两个模块都会被注册:
var assembly = typeof(AComponent).Assembly;
var builder = new ContainerBuilder();
// 注册两个模块
builder.RegisterAssemblyModules(assembly);
带泛型类型参数的 RegisterAssemblyModules()
重载允许您指定模块必须继承的基类型。在下面的例子中,只注册了一个模块,因为扫描受到了限制:
var assembly = typeof(AComponent).Assembly;
var builder = new ContainerBuilder();
// 注册 AModule 但不注册 BModule
builder.RegisterAssemblyModules<AModule>(assembly);
带类型对象参数的 RegisterAssemblyModules()
重载工作起来像泛型类型参数重载,但它允许您指定可能在运行时确定的类型。在下面的例子中,只注册了一个模块,因为扫描受到了限制:
var assembly = typeof(AComponent).Assembly;
var builder = new ContainerBuilder();
// 注册 AModule 但不注册 BModule
builder.RegisterAssemblyModules(typeof(AModule), assembly);
IIS 托管的 Web 应用程序
在使用 IIS 应用程序进行程序集扫描时,根据您的做法可能会遇到一些问题( 这是我们的常见问题之一 )。
在 IIS 中托管应用程序时,应用程序首次启动时所有程序集都会加载到 AppDomain
中,但当 IIS 重新启动 AppDomain
时,这些程序集将仅按需加载。
要解决此问题,请使用 GetReferencedAssemblies() 方法从 System.Web.Compilation.BuildManager 获取引用的程序集列表:
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
这将强制引用的程序集立即加载到 AppDomain
中,使其可用于模块扫描。