项目
版本

Autofac 聚合服务

引言

当你需要将一组依赖项视为一个依赖项时,聚合服务非常有用。当一个类依赖于多个构造注入的服务,或者拥有多个属性注入的服务时,将这些服务移动到单独的类中可以简化 API。

例如,在超类和子类中,超类有一个或多个构造注入的依赖项。子类通常必须继承这些依赖项,即使它们对超类可能只具有用。通过使用聚合服务,可以将超类构造函数参数合并为一个参数,从而减少子类中的重复性。另一个重要副作用是,子类现在可以防止超类依赖性的更改,这意味着在超类中引入新的依赖项只需更改聚合服务定义即可。

模式和这个示例都在 这里进行了进一步的阐述

可以通过手动创建一个具有构造注入依赖项的类并公开这些属性来实现聚合服务。然而,编写和维护聚合服务类及其伴随的测试可能会很快变得乏味。Autofac 的 AggregateService 扩展允许您直接从接口定义生成聚合服务,而无需编写任何实现。

必要的引用

您可以使用 Autofac.Extras.AggregateService NuGet 包 或手动添加对以下程序集的引用来向项目添加聚合服务支持:

开始使用

假设我们有一个具有多个构造注入依赖项的类,这些依赖项我们私有存储以供稍后使用:

public class SomeController
{
    private readonly IFirstService _firstService;
    private readonly ISecondService _secondService;
    private readonly IThirdService _thirdService;
    private readonly IFourthService _fourthService;

    public SomeController(
        IFirstService firstService,
        ISecondService secondService,
        IThirdService thirdService,
        IFourthService fourthService)
    {
        _firstService = firstService;
        _secondService = secondService;
        _thirdService = thirdService;
        _fourthService = fourthService;
    }
}

为了聚合这些依赖项,我们将这些依赖项移到单独的接口定义,并改为依赖该接口。

public interface IMyAggregateService
{
    IFirstService FirstService { get; }
    ISecondService SecondService { get; }
    IThirdService ThirdService { get; }
    IFourthService FourthService { get; }
}

public class SomeController
{
    private readonly IMyAggregateService _aggregateService;

    public SomeController(IMyAggregateService aggregateService)
    {
        _aggregateService = aggregateService;
    }
}

最后,我们注册聚合服务接口。

using Autofac;
using Autofac.Extras.AggregateService;
//...

var builder = new ContainerBuilder();
builder.RegisterAggregateService<IMyAggregateService>();
builder.Register(/*...*/).As<IFirstService>();
builder.Register(/*...*/).As<ISecondService>();
builder.Register(/*...*/).As<IThirdService>();
builder.Register(/*...*/).As<IFourthService>();
builder.RegisterType<SomeController>();
var container = builder.Build();

聚合服务的接口将自动为您生成实现,依赖项将按照预期填充。

聚合服务的解析方式

属性

只读属性模拟了常规构造注入依赖项的行为。每个属性的类型将在构建聚合服务实例时解析并缓存。

以下是功能等效的示例:

class MyAggregateServiceImpl : IMyAggregateService
{
    private IMyService _myService;

    public MyAggregateServiceImpl(IComponentContext context)
    {
        _myService = context.Resolve<IMyService>();
    }

    public IMyService MyService
    {
        get { return _myService; }
    }
}

方法

方法将像工厂委托一样工作,并会在每次调用时转化为对每个方法的 Resolve 调用。方法的返回类型将被解析,并将任何参数传递给 Resolve 调用。

这是方法调用的功能等效样本:

class MyAggregateServiceImpl : IMyAggregateService
{
    public ISomeThirdService GetThirdService(string data)
    {
        var dataParam = new TypedParameter(typeof(string), data);
        return _context.Resolve<ISomeThirdService>(dataParam);
    }
}

属性设置器和无返回类型的函数

在聚合服务中,属性设置器和无返回类型的函数没有意义。尽管它们在聚合服务接口中存在,但不会阻止代理生成。然而,调用这样的方法会抛出异常。

工作原理

在内部,AggregateService 使用 城堡项目的 DynamicProxy2 。给定一个接口(将服务聚合到一个接口),将生成一个实现该接口的代理。代理将翻译对属性和方法的调用为对 Autofac 上下文的Resolve调用。

性能考虑

由于聚合服务中的方法调用通过动态代理进行,每次方法调用都会有一些小但非零的开销。关于城堡动态代理框架与其他框架的性能研究可以在 这里 找到。

在本文档中