将数据库上下文注入自定义属性 .NET Core

Emi*_*las 2 attributes dependency-injection entity-framework-core asp.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));
    }
}
Run Code Online (Sandbox Code Playgroud)

一切都很好,但我的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)
    {
        //....
    }
}
Run Code Online (Sandbox Code Playgroud)

我想从 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";
    });
}
//...
Run Code Online (Sandbox Code Playgroud)

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

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

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

[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 
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

但我的自定义属性不再起作用。我无法声明新的数据库上下文,因为它需要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){
           
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

edo*_*o.n 8

您可以做的是利用 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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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