如何在 OnModelCreating() 中使用 Scoped Service 在每次 HTTP 请求后 Dispose()?

How to Dispose() after every HTTP request using Scoped Service in OnModelCreating()?

我有一个多租户应用程序,对于每个 HTTP Request,它应该为 属性 AccountId.

验证 Headers

全局查询过滤器是隔离.NET Core Docs中提到的租户的好方法,但它没有列出在每次请求后如何处理状态。

这是一个例子:

ApplicationDbContext.cs 使用由 FooHeaderService

注入的 AccountIdAccount 实体有一个全局查询过滤器
private readonly IFooHeader _fooHeaders;

public ApplicationDbContext(IFooHeader fooHeaders) : base(options)
{
    this._fooHeaders = fooHeaders;
}

protected override void OnModelCreating() {
    FooHeaders foo = this._fooHeaders.GetFooHeaders().AccountId;

    // or using Microsoft.EntityFrameworkCore.Infrastructure
    FooHeaders foo = this.Database.GetService<IFooHeaders>();
    Guid fooId = foo.AccountId;     // MUST DISPOSE BETWEEN REQUESTS!

    // Global Query Filter
    modelBuilder.Entity<Account>()
        .HasQueryFilter(filter => filter.AccountId = fooId)
}

FooHeaderServiceAccountId

获取 Headers
public class FooHeaderService : IFooHeaders
{
    private readonly FooHeaders _headers;

    public FooHeaderService(IHttpContextAccessor contextAccessor)
    {
        _headers.AccountId = contextAccessor.HttpContext?.Request
             .Headers["accountId"].ToString();
    }

    public FooHeaders GetFooHeaders() => _headers;
}

Startup.csFooHeaderService 注册为 ScopedService

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddScoped<IFooHeader, FooHeaderService>();

    services.AddDbContext<ApplicationDbContext>();
}

问题

如果没有妥善处理,OnModelCreating() 中的变量 fooId = AccountId 会在 HTTP Requests 之间持续存在(非常危险的东西!)。

如何将 FooHeaderService 依赖注入 OnModelCreating() 并为每个 HTTP Request 周期处理状态?

您可以使用HttpResponse.RegisterForDisposal方法:

HttpContext.Response.RegisterForDisposal(fooId);

查看示例: https://github.com/dotnet/EntityFramework.Docs/blob/master/samples/core/Querying/QueryFilters/BloggingContext.cs

OnModelCreating 只运行一次,因此您不能在那里解析特定的 AccountId。而是将服务传递到允许您访问 AccountId 的 DbContext 构造函数。您实际上已经这样做了,只是没有在 OnModelCreating 中使用它。

所以这应该是

public ApplicationDbContext(IFooHeader fooHeaders) : base(options)
{
    this._fooHeaders = fooHeaders;
}

protected override void OnModelCreating() {

    // Global Query Filter
    modelBuilder.Entity<Account>()
        .HasQueryFilter(filter => filter.AccountId = this._fooHeaders.AccountId)
}

这样每个请求都会得到一个新的 DbContext(因为它是有范围的),并且每个 DbContext 都有不同的 IFooHeaders 以插入全局查询过滤器。