.NET Core中自定义Attribute的依赖注入实现方法咨询
在.NET Core自定义Attribute中实现依赖注入的解决方案
首先得明确一个核心问题:.NET 里的 Attribute 实例是由 CLR 直接创建的,根本不走依赖注入容器的实例化流程。所以你想在 Attribute 的构造函数里直接注入 IConfiguration 这类服务是行不通的——CLR 根本不知道你的 DI 容器存在,自然没法帮你自动解析这些依赖参数。
针对你这个自定义路由Attribute的场景(实现了IRouteTemplateProvider),最贴合ASP.NET Core设计的解决方案是用**自定义路由约定(Route Convention)**来实现,下面是具体步骤:
步骤1:简化你的CustomRouteAttribute,只做标记用
把原来带DI参数的构造函数去掉,让Attribute仅作为标识,不需要承载业务逻辑:
public class CustomRouteAttribute : Attribute { // 保留你需要的路由属性,比如Order和Name public int? Order { get; set; } public string Name { get; set; } }
步骤2:创建自定义路由约定类,注入依赖处理逻辑
路由约定是ASP.NET Core官方提供的扩展路由规则的方式,这类类会由DI容器管理,所以可以正常注入IConfiguration:
using Microsoft.AspNetCore.Mvc.ApplicationModels; public class CustomRouteConvention : IApplicationModelConvention { private readonly string _serverKey; // 这里直接注入IConfiguration,完全没问题 public CustomRouteConvention(IConfiguration configuration) { _serverKey = configuration.GetSection("serverkey").Value ?? string.Empty; } public void Apply(ApplicationModel application) { // 遍历所有控制器,找到带有CustomRouteAttribute的目标 foreach (var controller in application.Controllers) { var customRouteAttr = controller.Attributes.OfType<CustomRouteAttribute>().FirstOrDefault(); if (customRouteAttr == null) continue; // 拼接你的自定义路由模板 var routeTemplate = $"api/{_serverKey}/{controller.ControllerName}"; // 给控制器添加这个自定义路由 controller.Selectors.Add(new SelectorModel { AttributeRouteModel = new AttributeRouteModel { Template = routeTemplate, Order = customRouteAttr.Order, Name = customRouteAttr.Name } }); } } }
步骤3:注册路由约定到DI容器
在Program.cs里配置控制器服务时,把这个约定添加进去:
var builder = WebApplication.CreateBuilder(args); // 添加控制器并注册自定义路由约定 builder.Services.AddControllers(options => { options.Conventions.Add(new CustomRouteConvention(builder.Configuration)); }); var app = builder.Build(); // 其他中间件配置... app.MapControllers(); app.Run();
这样一来,当你在控制器上标记[CustomRoute]时,路由约定会自动为它生成包含serverKey的路由模板,完美解决了依赖注入的问题。
通用场景的解决方案(非路由Attribute)
如果你的自定义Attribute不是路由相关的(比如授权、验证类的Attribute),可以采用「标记Attribute + DI管理的逻辑服务」的模式:
- 定义标记用的Attribute:
public class MyCustomAttribute : Attribute { // 可以存储一些标识参数,供后续逻辑使用 public string OperationKey { get; set; } }
- 创建处理逻辑的服务:
public interface IMyCustomService { void Execute(MyCustomAttribute attr, object target); } public class MyCustomService : IMyCustomService { private readonly IConfiguration _configuration; // 这里正常注入依赖 public MyCustomService(IConfiguration configuration) { _configuration = configuration; } public void Execute(MyCustomAttribute attr, object target) { // 在这里使用_configuration和attr的参数处理业务逻辑 var serverKey = _configuration.GetSection("serverkey").Value; // ...你的逻辑代码 } }
- 在合适的时机触发逻辑:
比如在ActionFilter中,通过反射获取Attribute,再从DI容器中拿到服务执行逻辑:
using Microsoft.AspNetCore.Mvc.Filters; public class MyCustomActionFilter : IAsyncActionFilter { private readonly IServiceProvider _serviceProvider; public MyCustomActionFilter(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 从当前Action的元数据中找到MyCustomAttribute var attr = context.ActionDescriptor.EndpointMetadata.OfType<MyCustomAttribute>().FirstOrDefault(); if (attr != null) { // 创建作用域,获取服务实例 using var scope = _serviceProvider.CreateScope(); var service = scope.ServiceProvider.GetRequiredService<IMyCustomService>(); service.Execute(attr, context.Controller); } await next(); } }
最后注册服务和Filter:
builder.Services.AddScoped<IMyCustomService, MyCustomService>(); builder.Services.AddControllers(options => { options.Filters.Add<MyCustomActionFilter>(); });
内容的提问来源于stack exchange,提问作者Voodoo
相关产品推荐
相关产品推荐

