c# - asp.net core (3+) CreateScope() 上的共享上下文

标签 c# asp.net-core

当我尝试运行后台任务时,我总是在该任务内创建一个新范围。随着更新到 3+,似乎在新的创建范围内,存在对原始请求的引用。以下代码将在 Debugger.Break() 语句上中断:

public class TestController : Controller
{
    public readonly IServiceScopeFactory ServiceScopeFactory;

    public TestController(
        IServiceScopeFactory serviceScopeFactory)
    {
        this.ServiceScopeFactory = serviceScopeFactory;
    }

    // GET
    public IActionResult Index()
    {
        Task.Run(() =>
        {
            using (var scope = ServiceScopeFactory.CreateScope())
            {
                var actionContextAccessor = scope.ServiceProvider.GetService<IActionContextAccessor>();
                var actionContext = actionContextAccessor.ActionContext;

                if (actionContext.ActionDescriptor != null)
                    Debugger.Break();
            }
        });

        return Content("Test");
    }
}

启动看起来像这样:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

问题在于 httpContext 与新创建的作用域共享。当其中一个作用域被废弃时,它会影响另一个作用域。例如,使用 IUrlHelper,会导致“IFeatureCollection 已被处理”。

为了测试,我添加了一个测试 httpContext 是否相同。看来确实如此!

public IActionResult Index()
{
    // Just for testing
    var originalContext = this.HttpContext;

    Task.Run(() =>
    {
        using (var scope = ServiceScopeFactory.CreateScope())
        {
            // Make sure the original request was disposed
            Thread.Sleep(1000);

            var actionContextAccessor = scope.ServiceProvider.GetService<IActionContextAccessor>();
            var actionContext = actionContextAccessor.ActionContext;

            if (originalContext == actionContext.HttpContext)
                Debugger.Break();
        }
    });

    return Content("Test");
}

对我来说,这似乎是奇怪的行为,因为我希望新范围不具有相同的 httpContext。它应该是一个新的范围。应该以其他方式创建作用域吗?

找到解决方案

在我的生产代码中,我使用 transient ActionContext 作用域,它尝试检测它是否正在处理请求或后台作用域,如下所示:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>()
    .AddTransient<ActionContext>((s) => {
        var actionContextAccessor = serviceProvider.GetRequiredService<IActionContextAccessor>();
        var actionContext = actionContextAccessor?.ActionContext;

        // Create custom actioncontext
        if (actionContext == null) {
             // create a manual actionContext
        }


        return actionContext;
    }); 

这似乎不再起作用了。如果 httpContext 通过 IHttpContextAccessor 存在,该解决方案似乎过于验证:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>()
    .AddTransient<ActionContext>((s) => {
        var currentContextAccess = serviceProvider.GetService<IHttpContextAccessor>();
        if (currentContextAccess.HttpContext == null) {
             // create a manual actionContext
             ...

             return actionContext;
        }

        var actionContextAccessor = serviceProvider.GetRequiredService<IActionContextAccessor>();
        return actionContextAccessor.ActionContext;
    }); 

最佳答案

For me, this seems like odd behaviour, cause I would except the scope not to be. Should the scope be created in another way?

为什么奇怪? IActionContextAccessor 是一个单例(与 IHttpContextAccessor 相同),因此即使在新创建的范围内也通常会返回相同的实例。

由于您没有等待 Task.Run,因此您的请求将在任务完成之前完成。请求完成后你想如何访问HttpContext?仅在请求期间有效。您必须在启动新任务之前获取所有必需的数据,并将所需的值传递给后台任务。

HttpContext 仅在请求期间有效,并且由于您不等待它,因此请求会提前结束。

您的代码所做的是未定义的行为,请参阅 David 的指南

关于c# - asp.net core (3+) CreateScope() 上的共享上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59985231/

相关文章:

c# - 无法调用服务引用?

asp.net-core - 使用 PropertyBuilder.HasValueGenerator 并连接到数据库上下文

asp.net - Azure Redis 缓存自动清除 PubSub channel

c# - ENC1003 C# 在应用程序运行时不会应用在项目中所做的更改

c# - 如何在 .NET 程序集中找到相应的 .pdb?

c# - 使用异步等待或任务?

c# - 可以在 ASPX 标记中执行内联代码吗?

c# - 如何从容器中运行的 ASP.NET Core 应用程序连接到具有集成安全性的 Windows Server 上的 SQL Server

c# - .NET Core + MSBuild : How to associate a csproj with multiple project. json 文件?

c# - 是否有可能 "intercept"第三方库的 "WriteFile"操作