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

ASP.NET Core 3+中CreateScope()共享原请求上下文的问题及解决

Issue: Background Task Scope Inherits Original Request Context in ASP.NET Core 3+

I ran into a tricky problem when working with background tasks in ASP.NET Core 3+: when creating a new service scope inside a background task, the new scope ended up referencing the original request's context. This caused unexpected errors when either scope was disposed—like getting a "IFeatureCollection has been disposed of" exception when using IUrlHelper.

Problem Breakdown

When I spun up a new IServiceScope via ServiceScopeFactory.CreateScope() inside a Task.Run() block, the HttpContext in that new scope was identical to the original request's context. Disposing either scope would invalidate the context for both, leading to runtime errors when accessing request-dependent services.

Reproduction Code

Test Controller

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"); 
    } 
}

Startup Configuration

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?}"); 
        }); 
    } 
}

Failed Original Approach

At first, I tried injecting ActionContext by relying solely on IActionContextAccessor, but this didn't work. The accessor still pulled the original request's context even in the background scope:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>() 
    .AddTransient<ActionContext>((s) => 
    { 
        var actionContextAccessor = serviceProvider.GetRequiredService<IActionContextAccessor>(); 
        var actionContext = actionContextAccessor?.ActionContext; 
        // Create custom actioncontext if null
        if (actionContext == null) 
        { 
            // create a manual actionContext 
        } 
        return actionContext; 
    });

Final Working Solution

The fix was to add a check using IHttpContextAccessor to determine if we're in an active request context. If there's no active HttpContext (like in a background task), we create a manual, isolated ActionContext:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>() 
    .AddTransient<ActionContext>((s) => 
    { 
        var currentContextAccess = serviceProvider.GetService<IHttpContextAccessor>(); 
        if (currentContextAccess.HttpContext == null) 
        { 
            // Example of creating a manual ActionContext:
            // var httpContext = new DefaultHttpContext();
            // var actionContext = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());
            // return actionContext;
        } 
        var actionContextAccessor = serviceProvider.GetRequiredService<IActionContextAccessor>(); 
        return actionContextAccessor.ActionContext; 
    });

This ensures background tasks get their own independent ActionContext instead of inheriting the disposed request context, eliminating the disposal-related errors.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 13:07:43