.NET Core 2.1依赖注入:控制器实时注入对应IContextDb实现类
根据控制器类型动态注入对应的IContextDb实现
当然可以实现根据控制器类型动态注入对应的IContextDb实例!下面我会给你两种实用的方案,你可以根据项目规模和扩展性需求选择合适的方式。
方案一:直接根据控制器类型硬编码映射
这种方式简单直接,适合控制器数量不多的场景。核心思路是通过IHttpContextAccessor获取当前请求的控制器类型,然后根据类型返回对应的上下文实例。
步骤1:注册IHttpContextAccessor
在ConfigureServices中先注册IHttpContextAccessor,它能帮我们获取当前请求的上下文信息:
public void ConfigureServices(IServiceCollection services) { // 注册HttpContextAccessor,用于获取当前请求的控制器信息 services.AddHttpContextAccessor(); // 注册IContextDb的动态注入逻辑 services.AddTransient<IContextDb>(serviceProvider => { var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>(); var endpoint = httpContextAccessor.HttpContext?.GetEndpoint(); if (endpoint == null) { throw new InvalidOperationException("无法获取当前请求的端点信息"); } // 获取当前请求对应的控制器描述符 var controllerDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>(); if (controllerDescriptor == null) { throw new InvalidOperationException("当前请求并非控制器请求"); } var controllerType = controllerDescriptor.ControllerTypeInfo.AsType(); // 根据控制器类型返回对应的上下文实例 return controllerType switch { Type t when t == typeof(ShopController) => new ShopContext(), Type t when t == typeof(UserController) => new UserContext(), // 这里可以继续添加更多控制器与上下文的映射 _ => throw new NotSupportedException($"未找到与控制器{controllerType.Name}匹配的IContextDb实现") }; }); // 其他服务注册... services.AddControllers(); }
注意事项
- 如果你的
ShopContext、UserContext本身有依赖(比如DbContextOptions),记得提前把这些依赖注册到服务容器中,或者在实例化时传入所需参数。 - 建议添加异常处理逻辑,避免在非控制器请求(比如中间件请求)时出现崩溃。
方案二:使用自定义属性实现灵活映射
如果你的项目控制器数量较多,或者后续需要频繁新增控制器和上下文,这种方式会更灵活。我们可以给控制器添加自定义属性标记它对应的上下文类型,然后在注入时读取这个属性来动态获取实例。
步骤1:定义自定义属性
首先创建一个用于标记控制器对应上下文的特性:
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class UseContextAttribute : Attribute { public Type ContextType { get; } public UseContextAttribute(Type contextType) { ContextType = contextType; // 验证传入的类型是否实现了IContextDb if (!typeof(IContextDb).IsAssignableFrom(contextType)) { throw new ArgumentException($"{contextType.Name}必须实现IContextDb接口"); } } }
步骤2:给控制器添加属性标记
在需要注入特定上下文的控制器上添加这个属性:
[UseContext(typeof(ShopContext))] public class ShopController : ControllerBase { private readonly IContextDb _context; // 构造函数注入IContextDb,框架会自动根据属性返回对应的实例 public ShopController(IContextDb context) { _context = context; } // 控制器方法... }
步骤3:配置动态注入逻辑
修改ConfigureServices中的注册代码,通过读取自定义属性来获取对应的上下文类型:
public void ConfigureServices(IServiceCollection services) { services.AddHttpContextAccessor(); // 先把所有上下文实现注册到服务容器中 services.AddTransient<ShopContext>(); services.AddTransient<UserContext>(); // 注册IContextDb的动态注入逻辑 services.AddTransient<IContextDb>(serviceProvider => { var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>(); var endpoint = httpContextAccessor.HttpContext?.GetEndpoint(); if (endpoint == null) { throw new InvalidOperationException("无法获取当前请求的端点信息"); } var controllerDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>(); if (controllerDescriptor == null) { throw new InvalidOperationException("当前请求并非控制器请求"); } var controllerType = controllerDescriptor.ControllerTypeInfo.AsType(); var useContextAttr = controllerType.GetCustomAttribute<UseContextAttribute>(); if (useContextAttr == null) { throw new InvalidOperationException($"控制器{controllerType.Name}未标记[UseContext]属性"); } // 从服务容器中获取对应的上下文实例,自动处理其依赖注入 return (IContextDb)serviceProvider.GetRequiredService(useContextAttr.ContextType); }); services.AddControllers(); }
优势
- 扩展性强:新增控制器时,只需给控制器添加
[UseContext]属性,并注册对应的上下文实现即可,无需修改注入逻辑。 - 类型安全:自定义属性会验证传入的上下文类型是否实现了
IContextDb,避免错误配置。
内容的提问来源于stack exchange,提问作者sam
相关产品推荐
相关产品推荐

