如何在解析链的中间传递组件参数?
有时,你可能需要在一个依赖链中某个位置解析一个 服务 ,而这个服务又需要一个 组件 。这个组件需要被传递一个参数 。
假设我们有一个简单的电子邮件通知系统:
// 这个接口允许你向某人发送电子邮件通知。
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,我们将逐步给出答案:如何注入配置、环境或上下文参数?