项目
版本

Autofac 组合体

组合模式 允许将一组对象以与单个实例相同的方式进行处理。

在 Autofac 6.0 中,我们添加了对服务中具有多个实现但应通过单个包装类公开的组合模式的支持。

您可以为服务注册一个 组合包装器 注册项,当消费类解决该服务时,将返回该包装器,并可以接受同一服务的集合作为构造函数参数。

此功能特别有用,如果您发现自己需要为服务添加提供程序,但又不想让原始提供程序的消费代码 a)必须更改,或 b)知道有多个提供程序。

// 这是我的具有多个注册项的服务。
public interface ILogSink
{
    void WriteLog(string log);
}

// 组合包装器只是一个注入相同服务的普通类。
public class CompositeLogSink : ILogSink
{
    private readonly IEnumerable<ILogSink> _implementations;

    public CompositeLogSink(IEnumerable<ILogSink> implementations)
    {
        _implementations = implementations;
    }

    public void WriteLog(string log)
    {
        foreach (var sink in _implementations)
        {
            sink.WriteLog(log);
        }
    }
}

// ...

var builder = new ContainerBuilder();

builder.RegisterType<FileLogSink>().As<ILogSink>();
builder.RegisterType<DbLogSink>().As<ILogSink>();

// 注册ILogSink的服务组合包装器。
builder.RegisterComposite<CompositeLogSink, ILogSink>();

var container = builder.Build();

// 这将返回包含两个注册项的组合包装器。
var logSink = container.Resolve<ILogSink>();

logSink.WriteLog("log message");

如果您不知道类型,而不是使用泛型,您可以手动指定:

builder.RegisterComposite(typeof(CompositeLogSink), typeof(ILogSink));

对于更复杂的组合创建,您还可以为组合注册指定一个 lambda:

builder.RegisterComposite<ILogSink>((context, parameters, implementations) => new CompositeLogSink(implementations));

在 lambda 中,context 是在发生解析的 IComponentContext(因此,如果需要,您可以解析其他内容),parameters 是一个 IEnumerable<Parameter> ,其中包含所有传递的参数, implementations 是该服务的所有实现的 IEnumerable

还可以注册泛型组合:

// 泛型提供者...
builder.RegisterGeneric(typeof(FileLogSink<>)).As(typeof(ILogSink<>));
builder.RegisterGeneric(typeof(DbLogSink<>)).As(typeof(ILogSink<>));

// ...和泛型组合。
builder.RegisterGenericComposite(typeof(CompositeLogSink<>), typeof(ILogSink<>));

var container = builder.Build();

// 将返回FileLogSink<HttpClient>和DbLogSink<HttpClient>的组合。
var sink = container.Resolve<ILogSink<HttpClient>>();

组合包装器还可以拥有自己的附加依赖关系,以及使用任何组合 隐式关系 到传递给它们的实现集:

public class CompositeWrapper : ILogSink
{
    public CompositeWrapper(IEnumerable<ILogSink> implementations, IAnotherService service)
    {
    }
}

public class LazyCompositeWrapper : ILogSink
{
    // 对组合的集合进行懒加载。
    public LazyCompositeWrapper(Lazy<IEnumerable<ILogSink>> implementations)
    {
    }
}

public class MetaCompositeWrapper : ILogSink
{
    // 访问每个实现的元数据。
    public MetaCompositeWrapper(IEnumerable<Meta<ILogSink>> implementations)
    {
    }
}

元数据

组合注册可以有自己的元数据,就像普通的注册一样;但是它们公开它们包裹的个别注册的任何元数据:

// 注册一个ILogSink的组合包装器:
builder.RegisterComposite<CompositeLogSink, ILogSink>()
        .WithMetadata("key", "value");

var container = builder.Build();

// 这将返回组合包装器并暴露元数据。
var logSink = container.Resolve<Meta<ILogSink>>();

生命周期

组合包装器可以有自己的生命周期,就像其他任何注册项一样。但是,您应该考虑使组合注册长时间存在的影响;例如,SingleInstance 组合将忽略在嵌套生命周期范围中为包裹服务所做的任何附加注册(例如)。

装饰器

使用组合模式时,装饰器仅应用于个别实现,而不是组合本身。

因此,如果您为 ILogSink 注册了一个装饰器,并且有一个包含 FileLogSinkDbLogSink 的组合注册,则当您解决 ILogSink 时,FileLogSinkDbLogSink 被装饰,但您的组合包装器不会被装饰。

组合体与集合

组合注册从不在解决实现的集合时返回,即使不在组合包装器之外也是如此:

var builder = new ContainerBuilder();

builder.RegisterType<FileLogSink>().As<ILogSink>();
builder.RegisterType<DbLogSink>().As<ILogSink>();

// 注册ILogSink的服务组合包装器。
builder.RegisterComposite<CompositeLogSink, ILogSink>();

var container = builder.Build();

// 这将只返回2个项(实际的实现)。
var logSinks = container.Resolve<IEnumerable<ILogSink>>();
在本文档中