如何注册同一接口的多实现(各带不同依赖)并运行时选择
问题描述
我有同一个接口的多个实现类,每个实现都有各自的依赖项。需要在运行时根据条件选择正确的实现,同时设置运行时属性。之前用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
相关产品推荐
相关产品推荐

