项目
版本

如何在解析链的中间传递组件参数?

有时,你可能需要在一个依赖链中某个位置解析一个 服务 ,而这个服务又需要一个 组件 。这个组件需要被传递一个参数

假设我们有一个简单的电子邮件通知系统:

// 这个接口允许你向某人发送电子邮件通知。
public interface INotifier
{
    void Send(string address, string message);
}

// 这个实现的通知器使用一个后端电子邮件仓库来完成繁重的工作。
public class Notifier : INotifier
{
    private IEmailServer _server;
    public Notifier(IEmailServer server)
    {
        this._server = server;
    }

    public void Send(string address, string message)
    {
        this._server.SendMessage(address, "from@domain.com", message);
    }
}

// 这个电子邮件服务器接口是通知器将用来发送邮件的。
public interface IEmailServer
{
    void SendMessage(string toAddress, string fromAddress, message);
}

// 注意这个实现需要一个字符串参数作为服务器地址 - 这是我们直到运行时才知道的,所以不能显式注册参数值。
public class EmailServer : IEmailServer
{
    private string _serverAddress;
    public EmailServer(string serverAddress)
    {
        this._serverAddress = serverAddress;
    }

    public void SendMessage(string toAddress, string fromAddress, message)
    {
        // ...通过指定的服务器地址发送消息。
    }
}

在 Autofac 注册时,你可能会有如下注册:

var builder = new ContainerBuilder();
builder.RegisterType<Notifier>().As<INotifier>();
builder.RegisterType<EmailServer>().As<IEmailServer>();
var container = builder.Build();

服务器地址通常在运行时才能知道,可能是来自上下文或环境参数,也可能是配置。

如何在解析通知器时将服务器地址作为参数传递给电子邮件服务器?

这是一个设计问题的原因

在回答这个问题之前,考虑一下这在很大程度上表明了一个 设计问题

从技术角度讲,你在解析一个 INotifier - 一个不需要知道运行时参数(即电子邮件服务器地址)的组件。INotifier 的实现可以改变。你可以为测试注册一个占位符,或者更改发送方式,使其不再需要知道地址。

将服务器地址作为参数传递给 INotifier 破坏了基于接口的开发和控制反转提供的解耦,因为它假设你知道整个依赖链是如何被解析的。

解决问题的关键是打破这种“知识”,而不是传递参数。

解决方案

不要试图传递参数,而是 反转问题 - 理解如何在运行时确定参数,并将其包装在提供者或 lambda 表达式注册中

这样,问题就变成了另一个 FAQ,我们将逐步给出答案:如何注入配置、环境或上下文参数?

在本文档中