Dependency injection 如何根据最终用户配置值解析类型?

Dependency injection 如何根据最终用户配置值解析类型?,dependency-injection,unity-container,Dependency Injection,Unity Container,我有一个名为IAcmeService的接口,它有多个实现 FileSystemAcmeService DatabaseAcmeService NetworkAcmeService 最终用户需要能够选择要使用的实现,并保存该选择 目前,我正在配置IOC容器Unity,以便用一个名称注册所有已知的实现 container.RegisterType(of IAcmeService, FileSystemAcmeService)("FileSystemAcmeService") container.R

我有一个名为IAcmeService的接口,它有多个实现

FileSystemAcmeService
DatabaseAcmeService
NetworkAcmeService
最终用户需要能够选择要使用的实现,并保存该选择

目前,我正在配置IOC容器Unity,以便用一个名称注册所有已知的实现

container.RegisterType(of IAcmeService, FileSystemAcmeService)("FileSystemAcmeService")
container.RegisterType(of IAcmeService, DatabaseAcmeService)("DatabaseAcmeService")
container.RegisterType(of IAcmeService, NetworkAcmeService)("NetworkAcmeService")
为了允许用户保存他们的选择,我有一个app.config配置节文件,其中存储了要使用的所选服务名称

为了解析所选的实现,我正在使用该服务的类的Initialize方法中进行手动解析

Private _service as IAcmeService
Public Sub Initialize()
    _service = container.Resolve(of IAcmeService)(_config.AcmeServiceName)
End Sub
这似乎不对,因为我的类必须了解容器。但我想不出别的办法


是否有其他方法允许最终用户在不让类知道容器的情况下进行选择?

定义和实现,这是此类问题的标准解决方案。如果您不介意我使用C,您可以像这样定义IAcmeServiceFactory接口:

public interface IAcmeServiceFactory
{
    IAcmeService Create(string serviceName);
}
现在,您可以编写如下具体实现:

public class AcmeServiceFactory : IAcmeServiceFactory
{
    private readonly IAcmeService fsService;
    private readonly IAcmeService dbService;
    private readonly IAcmeService nwService;

    public AcmeServiceFactory(IAcmeService fsService,
        IAcmeService dbService, IAcmeService nwService)
    {
        if (fsService == null)
        {
            throw new ArgumentNullException("fsService");
        }
        if (dbService == null)
        {
            throw new ArgumentNullException("dbService");
        }
        if (nwService == null)
        {
            throw new ArgumentNullException("nwService");
        }

        this.fsService = fsService;
        this.dbService = dbService;
        this.nwService = nwService;
    }

    public IAcmeService Create(string serviceName)
    {
        switch case serviceName
        {
            case "fs":
                return this.fsService;
            case "db":
                return this.dbService;
            case "nw":
                return this.nwService;
            case default:
                throw new ArgumentException("serviceName");
        }
    } 
}
如果您希望能够创建任意数量的IAcmeService实例,可以使其更通用,但我将把它作为练习留给读者:


这将要求您向Unity注册工厂。在任何需要基于名称的IAcmeService的地方,您都依赖于IAcmeService工厂,而不是IAcmeService本身。

啊……我几乎忽略了工厂构造函数拥有每个具体服务的事实。它允许容器构造实例。美好的我看到这种模式的一个缺点是在只需要一个实例的情况下创建所有实例的开销。我要试一试,看看它的表现如何。感谢Mark给出的书面回答。如果有性能问题,您可以使用此处所述的延迟加载服务来解决:但是,通常情况下,服务将/应该是使用单例生存期样式的长寿命对象,在这种情况下,这一点都不重要。好的。我还认为我的工厂可能依赖于配置对象。这将消除使用该服务的类对配置对象的需要。向unity注册工厂时,仍然无法解析IACMEServcie类型,因此此代码失败?!。是这样吗?正如这里所给出的,您还需要向Unity注册完全不同的IACMEService实现,因为它们被注入工厂。