首页 > 解决方案 > 将数据库上下文注入自定义属性 .NET Core

问题描述

我正在创建 ASP.NET Core 3.1 应用程序,使用 SPA 作为前端。所以我决定创建自定义身份验证和授权。所以我创建了自定义属性来分发和验证 JWT。

假设它看起来像这样:

[AttributeUsage(AttributeTargets.Method)]
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
    public async void OnAuthorization(AuthorizationFilterContext filterContext)
    {
        //Checking Headers..            
            
        using (var EF = new DatabaseContext)
        {
            user = EF.User.Where(p => (p.Email == username)).FirstOrDefault();
        }
        filterContext.HttpContext.Response.Headers.Add(
            "AccessToken",
            AccessToken.CreateAccessToken(user));
    }
}

一切都很好,但我的DatabaseContext,看起来像这样:

public class DatabaseContext : DbContext
{        
    public DbSet<User> User { get; set; }      
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseMySQL("ConnectionString");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //....
    }
}

我想从 Appsettings.json 中获取连接字符串,并可能使用依赖注入。我将 Startup.cs 更改为如下所示:

//...
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{            
    services.AddControllersWithViews();
    services.AddDbContext<DatabaseContext>(
        options => options.UseMySQL(Configuration["ConnectionStrings:ConnectionString"]));
    services.Add(new ServiceDescriptor(
        typeof(HMACSHA256_Algo), new HMACSHA256_Algo(Configuration)));
    services.AddSpaStaticFiles(configuration =>
    {
        configuration.RootPath = "ClientApp/build";
    });
}
//...

将数据库上下文类更改为:

public class DatabaseContext : DbContext
{
    public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
    public DbSet<User> User { get; set; } 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ///..
    }
}

在控制器中,我注入了数据库上下文,一切正常。它看起来像这样:

[ApiController]
[Route("API")]
public class APIController : ControllerBase
{
    private DatabaseContext EF;
    public WeatherForecastController(DatabaseContext ef)
    {
        EF = ef;            
    }
    
    [HttpGet]
    [Route("/API/GetSomething")]
    public async Task<IEnumerable<Something>> GetSomething()
    {
        using(EF){
           //.. this works 
        }
    }
} 

但是我的自定义属性不再起作用。我无法声明新的数据库上下文,因为它需要DatabaseContextOptions<DatabaseContext>声明对象,那么如何将 DBContext 注入到属性中,就像对控制器所做的那样?

这不起作用:

public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
    private DatabaseContext EF;
    public AuthLoginAttribute(DatabaseContext ef)
    {
        EF = ef;
    } 

    public async void OnAuthorization(AuthorizationFilterContext filterContext)
    {
        using(EF){
           
        }
    }
}

这适用于控制器,但属性抱怨没有具有 0 个参数的构造函数。

标签: asp.net-coredependency-injectionattributesentity-framework-core

解决方案


您可以做的是利用 RequestServices:

[AttributeUsage(AttributeTargets.Method)]
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var dbContext = context.HttpContext
            .RequestServices
            .GetService(typeof(DatabaseContext)) as DatabaseContext;
        
        // your code
    }
}

如果您允许我在您的代码中添加两条注释:

  1. 尽量不要使用async void,因为一旦发生异常,你会很困惑发生了什么。
  2. 没有必要将注入的 DbContext 包装在这样的 using 语句中using(EF) { .. }。您将提前处理它,这将导致稍后在请求中出现错误。DI 容器正在为您管理生命周期,相信它。

推荐阅读