项目
版本

Autofac 管道

在 Autofac(从 6.0 版本开始)中,实际根据服务请求创建实例的逻辑被实现为一个 管道 ,由多个 中间件 组成。每个单独的中间件代表构建或定位实例所需过程的一部分,并将其返回给你。

对于高级自定义场景,Autofac 允许你在管道中添加自己的中间件,以拦截、短路或扩展现有的解析行为。

服务管道与注册管道

每个 服务 都有自己的服务管道,而每个 注册 也有自己的注册管道。

让我们看看典型服务的“默认”执行管道:

一个示例解析管道,包含服务管道和注册管道。

服务管道附着在要解决的服务上,即你用来解决问题的东西。无论实际注册提供实例的方式如何,这些对服务的所有解析都是通用的。

注册管道附着在每个单独的注册上,并适用于所有调用该注册的解析,无论使用哪个服务进行解析。

我们可以利用这种分离管道的概念,将行为附加到给定服务的所有调用(装饰器 这样做),或者附加到单个注册(例如,向管道添加 生命周期事件)。

管道阶段

当我们向管道添加中间件时,需要指定中间件应运行的 阶段

通过指定阶段,我们可以允许在管道内部对中间件进行排序,从而不依赖于添加中间件的实际顺序。

以下是可用的管道阶段,分为服务阶段和注册阶段。

服务管道阶段 描述
ResolveRequestStart 解析请求的开始。在这一阶段添加的自定义中间件会在检测循环依赖之前执行。
ScopeSelection 在这一阶段,选择生命周期范围。如果某些中间件需要更改范围来针对其解析,它会在这里发生(但请注意,注册的 Autofac 生命周期仍然有效)。
Decoration 在这个阶段,将对实例进行装饰(在管道的输出方向)。
Sharing 在这一阶段结束时,如果共享实例满足请求,管道将停止执行并退出。在此阶段添加自定义中间件以选择您自己的共享实例。
ServicePipelineEnd 此阶段发生在服务管道结束(即将开始注册管道)之前。
注册管道阶段 描述
RegistrationPipelineStart 在注册管道开始时发生。
ParameterSelection 在激活之前运行此阶段,是推荐的参数替换点,如果需要的话。
Activation 激活阶段是管道的最后一个阶段,在此阶段创建组件的新实例。

注意
如果您尝试在添加注册中间件时指定服务管道阶段(反之亦然),您将收到错误。您需要根据要添加到的管道使用适当的阶段。

添加注册中间件

让我们看看如何在创建注册时向注册管道插入我们自己的中间件,使用一个简单的 “Hello World” lambda 中间件,它将在控制台打印一些信息:

var builder = new ContainerBuilder();

builder.RegisterType<MyImplementation>().As<IMyService>().ConfigurePipeline(p =>
{
    // 在注册管道的开始处添加中间件。
    p.Use(PipelinePhase.RegistrationPipelineStart, (context, next) =>
    {
        Console.WriteLine("Before Activation - 请求 {0}", context.Service);

        // 调用管道中的下一个中间件。
        next(context);

        Console.WriteLine("After Activation - 实例化 {0}", context.Instance);
    });
});

您可以看到,我们通过提供给 next 回调的方法调用管道中的下一个中间件,从而允许解析操作继续。

next 返回后,您可以访问创建的实例。这是因为在调用 next 时,它会调用管道中的下一个中间件,然后再次调用 next,直到管道结束,实例被激活。

如果不调用 next 回调,管道将结束,我们将返回到调用者。

定义中间件类

除了通过 lambda 函数提供中间件外,您还可以定义自己的中间件类,并将这些类的实例添加到管道中:

class MyCustomMiddleware : IResolveMiddleware
{
    public PipelinePhase Phase => PipelinePhase.RegistrationPipelineStart;

    public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
    {
        Console.WriteLine("Before Activation - 请求 {0}", context.Service);

        // 调用管道中的下一个中间件。
        next(context);

        Console.WriteLine("After Activation - 实例化 {0}", context.Instance);
    }
}

// ...

builder.RegisterType<MyImplementation>().As<IMyService>().ConfigurePipeline(p =>
{
    p.Use(new MyCustomMiddleware());
});

两种添加中间件的方式行为相同,但对于复杂的中间件,定义一个类可能会有所帮助。

将中间件添加到所有注册

如果您想为所有注册添加一段中间件,可以像添加其他共享注册行为一样使用 Registered 事件:

// 将 MyCustomMiddleware 添加到每个注册。
builder.ComponentRegistryBuilder.Registered += (sender, args) =>
{
    // PipelineBuilding 事件在管道构建之前触发,可以在其中添加中间件。
    args.ComponentRegistration.PipelineBuilding += (sender2, pipeline) =>
    {
        pipeline.Use(new MyCustomMiddleware());
    };
};

解析请求上下文

传递给所有中间件的上下文对象是 ResolveRequestContext 的一个实例。这个对象存储了解析请求的初始属性,以及解析执行过程中更新的任何属性。

您可以使用此上下文:

  • 检查正在解析的服务,使用 Service 属性。
  • 检查提供服务的注册。
  • 使用 Instance 属性获取或设置解析操作的结果。
  • 使用 Parameters 属性访问请求参数,并使用 ChangeParameters 方法更改参数。
  • 使用任何正常的解析方法解析另一个服务。

注意
ResolveRequestContext 是一个抽象基类。如果您想为中间件编写单元测试,可以创建一个模拟对象并将模拟对象传递给中间件实现。

添加服务中间件

服务中间件附着在服务上,而不是特定的注册。因此,当我们添加服务中间件时,我们可以为服务的所有解析添加行为,而不关心提供实例的注册是什么。

直接将服务中间件添加到 ContainerBuilder 上:

var builder = new ContainerBuilder();

// 在管道的最开始运行一些中间件,早于任何核心 Autofac 行为。
builder.RegisterServiceMiddleware<IMyService>(PipelinePhase.ResolveRequestStart, (context, next) =>
{
    Console.WriteLine("请求服务:{0}", context.Service);

    next(context);
});

与注册中间件类似,您可以注册中间件类而不是 lambda 函数:

builder.RegisterServiceMiddleware<IMyService>(new MyServiceMiddleware());

服务中间件源

类似于 注册源 <registration-sources>,如果您想在运行时动态地在运行时添加服务中间件,可以添加一个 服务中间件源

这对于像开放泛型服务这样的情况特别有用,其中我们不知道实际的服务类型直到运行时。

通过实现 IServiceMiddlewareSource 并将其注册到 ContainerBuilder 来定义服务中间件源。

class MyServiceMiddlewareSource : IServiceMiddlewareSource
{
    public void ProvideMiddleware(Service service, IComponentRegistryServices availableServices, IResolvePipelineBuilder pipelineBuilder)
    {
        // 在每个服务的 Sharing 阶段添加一些中间件。
        pipelineBuilder.Use(PipelinePhase.Sharing, (context, next) =>
        {
            Console.WriteLine("我出现在每个服务上!");

            next(context);
        });
    }
}

// ...

builder.RegisterServiceMiddlewareSource(new MyServiceMiddlewareSource());
在本文档中