You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自定义AuthorizeAttribute构造函数中传入ServiceProvider获取DbContext?

问题解答:自定义特性构造函数中获取DbContext的可行方案

首先得明确一个核心限制:你没法在自定义Attribute的构造函数里获取ServiceProvider或DbContext。因为Attribute是作为元数据在程序启动早期(甚至编译阶段)就被实例化的,这时候依赖注入容器还没完成初始化,根本拿不到任何注入的服务。你之前硬编码连接字符串的方式确实不适合生产环境,因为没法利用DI的配置和生命周期管理。

下面给你两种符合需求的类型安全方案,分别对应「独立于授权请求时机」和「请求上下文内处理但解耦授权逻辑」的场景:


方案一:程序启动时批量注册模块(推荐,完全独立于授权请求)

如果你的目标是把所有标记了OmsAuthorizeAttribute的模块信息提前写入数据库,不需要等到第一次授权请求,那可以在程序启动时扫描所有控制器/Action,提取Attribute参数后批量处理。

步骤1:修改OmsAuthorizeAttribute,开放模块信息的访问权限

把私有字段改成带公共getter的属性:

public class OmsAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter {
    public enum AccessEnablers { PostLogin, Anytime, Default }
    public string ModuleName { get; }
    public string ModuleDescription { get; }
    public AccessEnablers AllowAccess { get; set; } = AccessEnablers.Default;

    public OmsAuthorizeAttribute(string moduleName, string moduleDescription, AccessEnablers overrideAccess = AccessEnablers.Default) {
        ModuleName = moduleName;
        ModuleDescription = moduleDescription;
        AllowAccess = overrideAccess;
    }

    // 原有的OnAuthorization逻辑保持不变...
}

步骤2:在Program.cs中启动时扫描并注册

构建完WebApplication后,创建作用域获取DbContext,然后扫描所有带该Attribute的Action:

var app = builder.Build();

// 启动时批量注册所有标记的模块
using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<OmsDbContext>();
    // 扫描当前程序集下的所有控制器
    var controllerTypes = typeof(Program).Assembly.GetTypes()
        .Where(t => t.IsAssignableTo(typeof(ControllerBase)));

    foreach (var controllerType in controllerTypes)
    {
        // 提取控制器下所有带OmsAuthorizeAttribute的Action
        var actions = controllerType.GetMethods()
            .Where(m => m.GetCustomAttributes<OmsAuthorizeAttribute>(inherit: true).Any());

        foreach (var action in actions)
        {
            var attribute = action.GetCustomAttribute<OmsAuthorizeAttribute>();
            if (attribute != null && OmsEnvironment.settings.WizardCompleted)
            {
                OmsDbContext.registerModule(attribute.ModuleName, attribute.ModuleDescription, dbContext);
            }
        }
    }
}

// 后续中间件配置...
app.Run();

方案二:用IFilterFactory实现带DI的类型安全特性

如果需要在请求上下文内处理,但不想和授权逻辑强绑定,你可以让OmsAuthorizeAttribute实现IFilterFactory,通过工厂模式创建带DI依赖的Filter实例,既保留类型安全的参数传递,又能获取DbContext。

完整实现代码

public class OmsAuthorizeAttribute : AuthorizeAttribute, IFilterFactory {
    public enum AccessEnablers { PostLogin, Anytime, Default }
    public string ModuleName { get; }
    public string ModuleDescription { get; }
    public AccessEnablers AllowAccess { get; set; } = AccessEnablers.Default;

    public OmsAuthorizeAttribute(string moduleName, string moduleDescription, AccessEnablers overrideAccess = AccessEnablers.Default) {
        ModuleName = moduleName;
        ModuleDescription = moduleDescription;
        AllowAccess = overrideAccess;
    }

    // 实现IFilterFactory,创建带DI的Filter实例
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) {
        var dbContext = serviceProvider.GetRequiredService<OmsDbContext>();
        var memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
        return new OmsAuthorizeFilter(dbContext, memoryCache, ModuleName, ModuleDescription, AllowAccess);
    }

    public bool IsReusable => false;

    // 实际的授权和模块注册逻辑放在内部Filter类中
    private class OmsAuthorizeFilter : IAuthorizationFilter {
        private readonly OmsDbContext _dbContext;
        private readonly IMemoryCache _memoryCache;
        private readonly string _moduleName;
        private readonly string _moduleDescription;
        private readonly AccessEnablers _allowAccess;

        public OmsAuthorizeFilter(OmsDbContext dbContext, IMemoryCache memoryCache, string moduleName, string moduleDescription, AccessEnablers allowAccess) {
            _dbContext = dbContext;
            _memoryCache = memoryCache;
            _moduleName = moduleName;
            _moduleDescription = moduleDescription;
            _allowAccess = allowAccess;
        }

        public void OnAuthorization(AuthorizationFilterContext context) {
            // 模块注册逻辑:用缓存确保只执行一次
            if (OmsEnvironment.settings.WizardCompleted) {
                var cacheKey = $"ModuleRegistered_{_moduleName}";
                if (!_memoryCache.TryGetValue(cacheKey, out _)) {
                    OmsDbContext.registerModule(_moduleName, _moduleDescription, _dbContext);
                    _memoryCache.Set(cacheKey, true, TimeSpan.FromDays(30));
                }
            }

            // 原有的授权逻辑
            Printer.print("Authorization requested for module: " + _moduleName);
            if (!context.HttpContext.User.Identity.IsAuthenticated && _allowAccess != AccessEnablers.Anytime)
                context.Result = new RedirectToActionResult("Login", "Account", context);
            else {
                // 自定义授权逻辑
            }
        }
    }
}

这种方式下,你依然可以像原来一样使用[OmsAuthorize("模块名", "描述")]标记Action,完全保留类型安全,同时还能通过DI获取DbContext和其他服务。


内容的提问来源于stack exchange,提问作者ROMSCore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:27:48