会话状态异常的意外行为

Spe*_*ine 1 c# asp.net session async-await asp.net-4.5

我正在调查asyncawait在ASP.NET MVC控制器方法的上下文中,并得到一些意想不到的行为.

我有以下控制器类:

[SessionState(System.Web.SessionState.SessionStateBehavior.Required)]
public class HomeController : Controller
{
    // GET: Home
    public ActionResult Index()
    {
        return View();
    }

    public async Task<ActionResult> Check1()
    {
        Session["Test"] = "Check";
        System.Diagnostics.Debug.WriteLine("Session: " + Session["Test"]);
        await Task.Delay(20000);
        return View();
    }

    public ActionResult Check2()
    {
        var test = Session["Test"];
        ViewBag.Test = test;
        return View();
    }
}
Run Code Online (Sandbox Code Playgroud)

每个方法Check1和Check2的简单视图:

检查1

@{
    ViewBag.Title = "Check1";
}

<h2>View with Delay</h2>
Run Code Online (Sandbox Code Playgroud)

CHECK2

@{
    ViewBag.Title = "Check2";
    var check = ViewBag.Test;
}

<h2>Immediate View @check</h2>
Run Code Online (Sandbox Code Playgroud)

现在,在启动应用程序后,当我http://localhost:23131/Home/Check1在一个选项卡和http://localhost:23131/Home/Check2第二个选项卡中打开时,调用Check1按预期在20秒后返回,并Check2立即调用返回,但它没有在会话状态中设置的值,即不明白为什么.它应该立即设置,因为它是在延迟之前.但是,输出窗口上会打印正确的值.

现在Check1返回后,点击Check2选项卡上的刷新会带来会话中的值viewbag并显示它.

在此之后,如果我再次刷新两个标签,Check2则在Check1完成后不会返回(延迟20秒),尽管Check2正在async.

我的问题是:

  1. 第一次打开两个选项卡时,Check2由于Check1存在async而立即返回,并且尽管SessionStateBehavior.Requiredawait声明返回控制直到等待任务完成,但它不会被阻止.为什么Check2Check1返回之前没有获得值?
  2. 第一次之后,尽管被Check2抓住了,Check1但是为什么会这样?重新运行应用程序后,立即返回上一个问题中所述的但仅一次.Check1asyncCheck1
  3. 在我的解释中是否有任何我错过或说错的概念.

将ASP.NET与.NET Framework 4.5一起使用,在Windows 7 Ultimate上运行Visual Studio 2013.

Ben*_*ehn 6

对于你的问题2,如果我理解你正在做的是你刷新两个标签(可能是在check2之前的check1),并观察check2没有完成加载,直到check1结束.

原因是asp.net处理(可能)以串行方式写入同一会话的请求,而不是并行处理.这意味着,只要任何一个请求待处理,第二个请求甚至不会在第一个请求完成之前开始处理.使用任务,异步或手动线程处理将无法解决此问题.原因是访问会话不是线程安全的操作.可能的解决方法是不在不需要它们的页面中使用会话状态,或者在不需要写入会话的页面中使用ReadOnly会话状态.

两者都是通过使用EnableSessionState页面上的属性来完成的,要么设置为False禁用会话状态,要么ReadOnly指示在对此页面的任何请求中都不写入会话.

另请参阅此处的示例答案.

对于问题1,可能是因为会话状态仅在ReleaseRequestState生命周期事件(在请求的最后执行)之后持久化,即使我希望这只是在使用非- inproc会话商店.然而,更可能的原因是check2的请求首先执行(并且甚至阻止check1的处理开始).也许你可以详细说明你是如何测试它以及如何确定首先执行哪个请求.

编辑:

可能会发生的事情是:在第一次尝试时,您实际上并没有使用相同的会话(当您启动时尚未创建会话并为每个请求创建一个新会话.这也解释了为什么第二个请求未被阻止直到第一个完成).然后,在第二次尝试时,两者都将使用相同的会话(因为两个选项卡都使用了最后写入的会话cookie).您可以通过打印asp会话(Session.SessionID)id和/或使用无Cookie会话状态来确认该理论

  • 在这种情况下,可能发生的事情是:在第一次尝试时,您实际上并没有使用相同的会话(当您启动时会创建会话并为每个请求创建一个新会话.这也解释了为什么第二个会话请求在第一次完成之前不会被阻止).然后,在第二次尝试时,两者都将使用相同的会话(因为两个选项卡都使用了最后写入的会话cookie).您可以通过打印asp会话(Session.SessionID)id和/或使用无Cookie会话状态来确认该理论. (2认同)