HttpContext.Current.Session 在使方法异步后为空

本文关键字:异步 方法 Current Session HttpContext | 更新日期: 2023-09-27 18:13:57

我有一个像下面这样的方法

book.Bindbook();

我让它异步如下

new Task(book.Bindbook).Start();

现在此方法使用 HttpContext.Current.Session 现在返回 null。这是返回 null 的代码

public static Bookmanager CartManager
{
    //Gets the value from the session variable.
    get
    {
        try
        {
            if (HttpContext.Current.Session["BookData"] == null)
            {
                Bookmanager bookmgr= new Bookmanager ();
                Book book = new Book(SessionManager.CurrentUser);
                bookmgr.SetCurrentCart(book);                       
                HttpContext.Current.Session["BookData"] = bookmgr;
            }
            else if (((Bookmanager)HttpContext.Current.Session["BookData"]).GetCurrentCart() == null)
            {
                Book book = new Book(SessionManager.CurrentUser);
                ((Bookmanager)HttpContext.Current.Session["BookData"]).SetCurrentCart(book);
            }
        }
        catch(Exception ex)
        {
            //throw ex;
        }
        return ((Bookmanager)HttpContext.Current.Session["BookData"]);
    }
    //Sets the value of the session variable.
    set
    {
        HttpContext.Current.Session["BookData"] = value;
    }
}

HttpContext.Current.Session 在使方法异步后为空

您的解决方案存在许多导致此问题的潜在问题。我将尝试将其分解为碎片以解释正在发生的事情。

new Task(book.Bindbook).Start()并不总是在你认为的地方运行

这种创建异步操作的方法非常危险,因为要知道如何执行任务并不容易。调用此构造函数时,Task将捕获TaskScheduler.Current值作为它用于计划自己的执行的机制。这意味着您的任务的执行与它所处的上下文无形地联系在一起。

通常,您希望使用Task.Run(Action)而不是创建新的Task实例,然后调用Start,因为这始终在 TaskScheduler.Default 的值上运行,这通常是 .NET 线程池,通常是运行后台任务时要执行的操作。

HttpContext不是线程安全的

HttpContext类从未打算安全地从多个线程调用。它是Current值绑定到正在处理请求的线程,在其他线程上不可用。不应将其传递给其他线程。一般来说,您应该将应用中HttpContext的表面积减少到最低限度。出于测试目的几乎不可能进行模拟,并且有几个微妙的限制(例如您发现的(,这使得使用起来具有挑战性。

相反,应尽早在代码中显示Current值,并保留对实际需要使用的对象(如会话(的引用。

静态特性通常是有害的

在对象上具有静态属性意味着整个 AppDomain 中只有这些东西之一(例如 TaskScheduler.Default (,它们表示一些可以配置的横切关注点,或者存在一些隐藏的上下文在幕后操纵值。前一种情况很少见,但在某些情况下是可以接受的,但第二种情况非常有害。 HttpContext.Current是一个不应该是静态的值的示例(ASP.NET 的未来版本完全取消它(。它使代码难以推理,几乎无法测试,并引入了不容易处理的微妙错误(如这个(。

从根本上说,这是这里最大的问题,也是你痛苦的根本原因。如果此属性作为实例属性公开,并且实例的范围限定为请求上下文,则不会遇到任何问题。一旦您使用的对象的生存期与请求相同,您的所有临界状态都将变为本地状态且易于推理。

使用 ConfigureAwait(true( 允许在原始上下文上继续。

var task = new Task(() => book.Bindbook()).ConfigureAwait(true);
task.Start();

HttpContext必然thread,这就是为什么它null。我认为更好的解决方案是通过参数将所有需要的数据传递给其他线程,而不是共享HttpContext