You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何注册同一接口的多实现(各带不同依赖)并运行时选择

问题描述

我有同一个接口的多个实现类,每个实现都有各自的依赖项。需要在运行时根据条件选择正确的实现,同时设置运行时属性。之前用AutoFac的方案能正常工作,但新项目不再使用该框架,导致UploaderFactory无法正常运行。求可行解决方案,有更优方案也欢迎推荐!

相关代码如下:

// 拥有多个实现的接口
public interface IFileUploader
{
    Task UploadAsync(string filePath);
}

// 某实现类的依赖接口
public interface IDependencyA
{
}

// 另一个实现类的依赖接口
public interface IAnotherDependency
{
}

public class FileUploaderA: IFileUploader
{
    public delegate FileUploaderA Factory(SomeStuff properties);

    private readonly SomeStuff _properties;
    private readonly IDependencyA _dependencyA;

    public FileUploaderA(SomeStuff properties, IDependencyA dependencyA)
    {
        _properties = properties;
        _dependencyA = dependencyA;
    }

    public Task UploadAsync(string filePath)
    {
        // 使用属性和依赖项执行上传逻辑
        throw new NotImplementedException();
    }
}

public class FileUploaderB : IFileUploader
{
    public delegate FileUploaderB Factory(SomeStuff properties);

    private readonly SomeStuff _properties;
    private readonly IAnotherDependency _anotherDependency;

    public FileUploaderB(SomeStuff properties, IAnotherDependency anotherDependency)
    {
        _properties = properties;
        _anotherDependency = anotherDependency;
    }

    public Task UploadAsync(string filePath)
    {
        // 使用属性和依赖项执行上传逻辑
        throw new NotImplementedException();
    }
}

public interface IUploaderFactory
{
    IFileUploader Create(bool condition, SomeStuff someStuff);
}

public class UploaderFactory : IUploaderFactory
{
    private readonly FileUploaderA.Factory _fileUploaderA;
    private readonly FileUploaderB.Factory _fileUploaderB;

    // 此处注入失效!!!
    public UploaderFactory(FileUploaderA.Factory fileUploaderA, FileUploaderB.Factory fileUploaderB)
    {
        _fileUploaderA = fileUploaderA;
        _fileUploaderB = fileUploaderB;
    }

    public IFileUploader Create(bool condition, SomeStuff someStuff)
    {
        return condition ? _fileUploaderA(someStuff) : _fileUploaderB(someStuff);
    }
}

public class SomeStuff
{
    public string A { get; set; }
    public string B { get; set; }
}
解决方案

AutoFac会自动解析自定义的工厂委托,但原生.NET DI(如ASP.NET Core DI)不支持这种自动注入,需要手动处理。以下是几种可行方案:

方案一:手动注册工厂委托(贴近原AutoFac用法)

在DI配置中手动注册FileUploaderA.Factory和FileUploaderB.Factory委托,将服务解析逻辑封装进去:

// 假设已实现IDependencyA和IAnotherDependency的具体类
builder.Services.AddScoped<IDependencyA, DependencyAImpl>();
builder.Services.AddScoped<IAnotherDependency, AnotherDependencyImpl>();

// 注册FileUploaderA的工厂委托
builder.Services.AddScoped<FileUploaderA.Factory>(sp => 
    properties => new FileUploaderA(properties, sp.GetRequiredService<IDependencyA>())
);

// 注册FileUploaderB的工厂委托
builder.Services.AddScoped<FileUploaderB.Factory>(sp => 
    properties => new FileUploaderB(properties, sp.GetRequiredService<IAnotherDependency>())
);

// 注册工厂类
builder.Services.AddScoped<IUploaderFactory, UploaderFactory>();

注册完成后,UploaderFactory的构造函数就能正常注入两个委托,原有代码无需修改。

方案二:直接依赖IServiceProvider(简单但不推荐)

让UploaderFactory依赖IServiceProvider,手动解析依赖并创建实例:

public class UploaderFactory : IUploaderFactory
{
    private readonly IServiceProvider _serviceProvider;

    public UploaderFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IFileUploader Create(bool condition, SomeStuff someStuff)
    {
        if (condition)
        {
            var dependencyA = _serviceProvider.GetRequiredService<IDependencyA>();
            return new FileUploaderA(someStuff, dependencyA);
        }
        else
        {
            var anotherDependency = _serviceProvider.GetRequiredService<IAnotherDependency>();
            return new FileUploaderB(someStuff, anotherDependency);
        }
    }
}

此方式属于服务定位器模式,会降低代码可测试性,仅适合快速临时解决问题。

方案三:策略模式+字典映射(高扩展性)

通过字典映射条件与实例创建逻辑,新增实现时无需修改工厂类:

步骤1:定义上传类型枚举(替代bool条件,提升可读性)

public enum UploadType { TypeA, TypeB }

步骤2:调整工厂接口

public interface IUploaderFactory
{
    IFileUploader Create(UploadType type, SomeStuff someStuff);
}

步骤3:注册DI并构建映射字典

builder.Services.AddScoped<IDependencyA, DependencyAImpl>();
builder.Services.AddScoped<IAnotherDependency, AnotherDependencyImpl>();

// 注册工厂,直接传入映射字典
builder.Services.AddScoped<IUploaderFactory>(sp => 
{
    var factoryMap = new Dictionary<UploadType, Func<SomeStuff, IFileUploader>>
    {
        [UploadType.TypeA] = props => new FileUploaderA(props, sp.GetRequiredService<IDependencyA>()),
        [UploadType.TypeB] = props => new FileUploaderB(props, sp.GetRequiredService<IAnotherDependency>())
    };
    return new UploaderFactory(factoryMap);
});

步骤4:实现工厂类

public class UploaderFactory : IUploaderFactory
{
    private readonly Dictionary<UploadType, Func<SomeStuff, IFileUploader>> _factoryMap;

    public UploaderFactory(Dictionary<UploadType, Func<SomeStuff, IFileUploader>> factoryMap)
    {
        _factoryMap = factoryMap;
    }

    public IFileUploader Create(UploadType type, SomeStuff someStuff)
    {
        if (_factoryMap.TryGetValue(type, out var factory))
        {
            return factory(someStuff);
        }
        throw new ArgumentOutOfRangeException(nameof(type), "不支持的上传类型");
    }
}

此方案扩展性极强,新增IFileUploader实现时仅需在字典中添加新映射即可。

方案四:调整设计,避免构造注入运行时数据(最佳实践)

运行时数据(如SomeStuff)不应通过构造注入,建议将其移至方法参数:

步骤1:修改接口与实现类

public interface IFileUploader
{
    Task UploadAsync(string filePath, SomeStuff properties);
}

public class FileUploaderA: IFileUploader
{
    private readonly IDependencyA _dependencyA;

    public FileUploaderA(IDependencyA dependencyA)
    {
        _dependencyA = dependencyA;
    }

    public Task UploadAsync(string filePath, SomeStuff properties)
    {
        // 使用properties和_dependencyA执行上传逻辑
        throw new NotImplementedException();
    }
}

public class FileUploaderB : IFileUploader
{
    private readonly IAnotherDependency _anotherDependency;

    public FileUploaderB(IAnotherDependency anotherDependency)
    {
        _anotherDependency = anotherDependency;
    }

    public Task UploadAsync(string filePath, SomeStuff properties)
    {
        // 使用properties和_anotherDependency执行上传逻辑
        throw new NotImplementedException();
    }
}

步骤2:简化工厂类

public class UploaderFactory : IUploaderFactory
{
    private readonly IServiceProvider _serviceProvider;

    public UploaderFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IFileUploader Create(bool condition)
    {
        return condition 
            ? _serviceProvider.GetRequiredService<FileUploaderA>() 
            : _serviceProvider.GetRequiredService<FileUploaderB>();
    }
}

步骤3:注册DI

builder.Services.AddScoped<FileUploaderA>();
builder.Services.AddScoped<FileUploaderB>();
builder.Services.AddScoped<IUploaderFactory, UploaderFactory>();

此方案符合DI设计原则,代码更简洁易维护,是优先推荐的优化方案。


内容的提问来源于stack exchange,提问作者LockTar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:35:53