在ASP.NET Core 2.0中如何使用服务定位器(ServiceLocator)
如何在方法中使用服务定位器而非构造函数注入依赖
我目前的代码结构如下:
Startup 类代码
public void ConfigureServices(IServiceCollection services) { // 此处代码 Bootstraper.Setup(services); }
Bootstraper 类代码
public static partial class Bootstraper { // 此处代码 public static IServiceCollection CurrentServiceCollection { get;set;} public static IServiceProvider CurrentServiceProvider { get { return CurrentServiceCollection.BuildServiceProvider(); } } public static void Setup(IServiceCollection serviceCollection) { // 此处代码 SetupLog(); InitializeCulture(); InitializeDbContexts(); RegisterDataModelRepositories(); } }
RegisterDataModelRepositories() 方法代码
CurrentServiceCollection.AddTransient<IDefAccidentGroupRepository>(p => new DefAccidentGroupRepository(ApplicationMainContextId)); CurrentServiceCollection.AddTransient<IDefGenderRepository>(p => new DefGenderRepository(ApplicationMainContextId));
我的需求是:希望能够在方法中使用服务定位器,而无需在类的构造函数中解析依赖,请问有没有可行的解决办法?
当然有办法实现,不过先得提醒你:服务定位器模式虽然能满足你的需求,但它会带来一些潜在问题——比如增加代码耦合度、降低可测试性(依赖被隐藏在方法内部,难以替换模拟实现),所以如果不是迫不得已,构造函数注入仍然是更推荐的方式。
具体实现方案
基于你现有的 Bootstraper 类,我们可以完善它的服务定位能力:
优化 Bootstraper 的服务提供逻辑
别每次调用CurrentServiceProvider都重新构建服务提供者,这会导致性能问题和服务实例不一致。应该在Setup方法中一次性完成服务注册后,再构建并保存服务提供者:public static partial class Bootstraper { // 此处代码 public static IServiceCollection CurrentServiceCollection { get; private set;} public static IServiceProvider CurrentServiceProvider { get; private set; } public static void Setup(IServiceCollection serviceCollection) { CurrentServiceCollection = serviceCollection; // 先完成所有服务注册操作 SetupLog(); InitializeCulture(); InitializeDbContexts(); RegisterDataModelRepositories(); // 最后统一构建服务提供者 CurrentServiceProvider = serviceCollection.BuildServiceProvider(); } }添加通用的服务定位方法
在Bootstraper中新增两个静态方法,让你可以在任意地方便捷获取服务:// 获取服务(找不到返回null) public static T GetService<T>() where T : class { return CurrentServiceProvider.GetService<T>(); } // 获取必需服务(找不到抛出异常) public static T GetRequiredService<T>() where T : class { return CurrentServiceProvider.GetRequiredService<T>(); }在方法中直接使用服务定位器
现在你就可以在任意方法里直接获取依赖,完全不需要构造函数注入了:public void SomeBusinessMethod() { var accidentRepo = Bootstraper.GetRequiredService<IDefAccidentGroupRepository>(); // 使用 accidentRepo 执行业务逻辑 var genderRepo = Bootstraper.GetService<IDefGenderRepository>(); if(genderRepo != null){ // 处理性别相关逻辑 } }
额外注意事项
- 如果你用的是ASP.NET Core,官方其实不推荐使用服务定位器——框架本身已经提供了完善的依赖注入生态,服务定位器会破坏依赖注入的透明性,让代码难以维护。
- 如果你只是需要在非控制器、非服务类中使用服务,也可以考虑通过构造函数注入
IServiceProvider,再在方法中解析,但这本质上还是服务定位,同样要谨慎使用。 - 测试时,你可以在测试初始化阶段调用
Bootstraper.Setup,传入包含模拟服务的IServiceCollection,这样就能替换真实依赖完成测试。
内容的提问来源于stack exchange,提问作者user2568693
相关产品推荐
相关产品推荐

