.NET 7依赖注入:同一接口多实现的差异化配置问询
问题描述
现有ASP.NET Core项目中,Startup.cs的服务配置如下:
services.AddScoped<IFileStorage, DiskFileStorage>(); services.AddScoped<IImageHandler, ImageHandler>(); public class ImageHandler { public ImageHandler(IFileStorage fileStorage, ...) { } }
需新增OtherHandler类,它依赖IFileStorage接口,但要使用NetworkFileStorage实现:
public class OtherHandler : IOtherHandler { public OtherHandler(IFileStorage fileStorage) { } }
需求:配置服务使OtherHandler使用NetworkFileStorage,其余组件默认使用DiskFileStorage。
补充:已通过以下方式实现,但希望找到基于接口的实现方案:
services.AddScoped<IOtherHandler>(provider => new OtherHandler(provider.GetRequiredService<NetworkFileStorage>()));
基于接口的解决方案
方案1:利用.NET 6+ 键控服务(Keyed Services)
这是官方推荐的接口级区分实现方式,无需直接依赖具体类:
- 注册带键的
IFileStorage实现,并设置默认实现:
// 注册带标记的两个IFileStorage实现 services.AddKeyedScoped<IFileStorage, DiskFileStorage>("Disk"); services.AddKeyedScoped<IFileStorage, NetworkFileStorage>("Network"); // 注册默认的IFileStorage指向Disk版本,供其他组件使用 services.AddScoped<IFileStorage>(sp => sp.GetRequiredKeyedService<IFileStorage>("Disk"));
- 给
OtherHandler的构造器添加特性标记,指定注入的键:
public class OtherHandler : IOtherHandler { public OtherHandler([FromKeyedServices("Network")] IFileStorage fileStorage) { } }
- 直接注册
OtherHandler,框架会自动匹配对应键的实现:
services.AddScoped<IOtherHandler, OtherHandler>();
如果不想修改OtherHandler的构造器,也可以在注册时手动解析:
services.AddScoped<IOtherHandler>(sp => new OtherHandler(sp.GetRequiredKeyedService<IFileStorage>("Network")));
方案2:工厂模式包装(兼容所有.NET版本)
如果项目版本低于.NET 6,可通过工厂接口间接获取指定实现,全程基于接口交互:
- 定义工厂接口:
public interface IFileStorageFactory { IFileStorage GetDiskStorage(); IFileStorage GetNetworkStorage(); }
- 实现工厂类:
public class FileStorageFactory : IFileStorageFactory { private readonly IServiceProvider _serviceProvider; public FileStorageFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IFileStorage GetDiskStorage() => _serviceProvider.GetRequiredService<DiskFileStorage>(); public IFileStorage GetNetworkStorage() => _serviceProvider.GetRequiredService<NetworkFileStorage>(); }
- 注册服务:
// 先注册两个具体实现 services.AddScoped<DiskFileStorage>(); services.AddScoped<NetworkFileStorage>(); // 注册默认IFileStorage为Disk版本 services.AddScoped<IFileStorage>(sp => sp.GetRequiredService<DiskFileStorage>()); // 注册工厂 services.AddScoped<IFileStorageFactory, FileStorageFactory>(); // 注册OtherHandler,通过工厂获取Network版本 services.AddScoped<IOtherHandler>(sp => new OtherHandler(sp.GetRequiredService<IFileStorageFactory>().GetNetworkStorage()));
也可以修改OtherHandler注入工厂,简化注册:
public class OtherHandler : IOtherHandler { public OtherHandler(IFileStorageFactory storageFactory) { IFileStorage fileStorage = storageFactory.GetNetworkStorage(); // 初始化逻辑 } }
此时直接注册即可:
services.AddScoped<IOtherHandler, OtherHandler>();
内容的提问来源于stack exchange,提问作者falowil
相关产品推荐
相关产品推荐

