C# MVC多页表单丢失会话
我有一个多页的表格用来收集线索。同一表单有多个版本,我们称之为活动。有些活动是3页表单,有些是2页,有些是1页。它们都共享相同的潜在客户模型和活动控制器等。有一个操作用于控制活动流,还有一个单独的操作用于将所有潜在客户信息提交到数据库中 我无法在本地复制此内容,并且有检查确保用户不能跳过页面。会话模式为InProc 这将在每次POST操作之后运行,该操作在会话中存储值:C# MVC多页表单丢失会话,c#,asp.net-mvc-3,forms,session,tempdata,C#,Asp.net Mvc 3,Forms,Session,Tempdata,我有一个多页的表格用来收集线索。同一表单有多个版本,我们称之为活动。有些活动是3页表单,有些是2页,有些是1页。它们都共享相同的潜在客户模型和活动控制器等。有一个操作用于控制活动流,还有一个单独的操作用于将所有潜在客户信息提交到数据库中 我无法在本地复制此内容,并且有检查确保用户不能跳过页面。会话模式为InProc 这将在每次POST操作之后运行,该操作在会话中存储值: protected override void OnActionExecuted(ActionExecutedCont
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
if (this.Request.RequestType == System.Net.WebRequestMethods.Http.Post && this._Lead != null)
ParentStore.Lead = this._Lead;
}
这是控制器中的Lead属性:
private Lead _Lead;
/// <summary>
/// Gets the session stored Lead model.
/// </summary>
/// <value>The Lead model stored in session.</value>
protected Lead Lead
{
get
{
if (this._Lead == null)
this._Lead = ParentStore.Lead;
return this._Lead;
}
}
问题发生在上述操作之间,在执行以下提交操作之前:
[HttpPost]
public virtual ActionResult Submit(Lead lead, string campaign, int page)
{
if (this.Session.IsNewSession || this.Lead.Submitted || !this.LeadExists)
return RedirectToAction("Campaign", new { campaign = campaign, page = 0 });
lead.AddCustomQuestions();
MergeLead(campaign, lead, this.AdditionalQuestionsType, false);
if (ModelState.IsValid == false)
return View(GetCampaignView(campaign, page), this.Lead);
var sharedLead = this.Lead.ToSharedLead(Request.Form.ToQueryString(false)); //Error occurs here and sends me an email with whatever values are in the form collection.
EAUtility.ProcessLeadProxy.SubmitSharedLead(sharedLead);
this.Lead.Submitted = true;
VisitorTracker.DisplayConfirmationPixel = true;
TrackLead(this.Lead, campaign, page, LeadType.Shared);
return RedirectToAction(this.ConfirmationView);
}
我们网站的每位访问者都会获得一个唯一的GUID visitorID。但是,当这些错误发生时,活动帖子和提交帖子之间的visitorID不同。因为我们在活动和提交操作期间通过TrackLead()方法跟踪每个表单提交,所以我可以看到会话在调用之间丢失,尽管每次发布后都会触发OnActionExecuted并将表单存储在会话中
因此,当出现错误时,我们在一个visitorID下得到一半表单,在另一个visitorID下得到表单的其余部分。幸运的是,我们使用了一个第三方服务,该服务在每次表单值更改时发送一个API调用,该表单值使用它自己的ID。这些ID在表单的前半部分和表单的其余部分之间是一致的,这是我从丢失的会话问题中保存线索的唯一方法
我还应该注意到,这在99%的情况下都很好
编辑:
我修改了代码,将lead对象显式存储在TempData中,并使用TempData.Keep()方法在后续请求之间持久化该对象。我只在我的3个站点中的1个站点部署了这种行为,但到目前为止还不错
我还尝试将会话中的lead对象直接存储在控制器操作中,即
Session.Add("lead", this._Lead);
它使用HTTPSessionStateBase,试图绕过包装类,而不是使用HTTPSessionState的HttpContext.Current.Session。正如预期的那样,这一修改在这个问题上没有什么不同
编辑#2:
今天早上查看电子邮件时,我注意到两个会话错误,尽管我将lead模型保存在TempData中。有什么东西导致会话被清除,很可能是某种应用程序池循环,但我还没有弄清楚。我可能要开始在SQL Server中存储会话
SessionStore.cs请求代码:
public static class SessionStore
{
private static HttpSessionState Session
{
get { return HttpContext.Current.Session; }
}
public static T Get<T>(object name, T defaultValue)
{
var key = name.ToString();
return SessionStore.Session.ContainsKey(key) ? (T)Session[key] : defaultValue;
}
public static void Set(object name, object value)
{
SessionStore.Session.Add(name.ToString(), value);
}
}
公共静态类会话存储
{
私有静态HttpSessionState会话
{
获取{return HttpContext.Current.Session;}
}
公共静态T Get(对象名称,T defaultValue)
{
var key=name.ToString();
return SessionStore.Session.ContainsKey(key)-(T)Session[key]:defaultValue;
}
公共静态无效集(对象名称、对象值)
{
SessionStore.Session.Add(name.ToString(),value);
}
}
您能同时包含SessionStore代码吗?整个过程似乎非常脆弱且容易出错。如果用户在同一页面上打开两个选项卡,同时刷新或提交它们,会发生什么情况?看起来他们会覆盖彼此的线索。这就是使用Session的问题所在,它在用户可能打开的所有选项卡之间都会被撕碎。为什么不将lead存储在数据库中呢?这些网站与lead management/call center软件/iframe集成在一起(尽管这不是iframe特有的问题)。每位代表一次只能打开我们网站的一个实例,一次提交一个,但如果他们打开多个选项卡,开始填写表格,提交第一页,打开另一个选项卡,提交另一个第一页,返回第一个选项卡并提交,第一条线索将通过。第二个选项卡,如果试图提交,将重定向到第一个表单页面,它们必须重新开始。这不是一个问题,但我明白你的意思。我也试图通过打开多个选项卡来重现这个错误,就像你提到的,但一切都按预期进行。如果我开始填写表单,并通过浏览器清除缓存/cookies,然后尝试提交表单的第二页,这将重现错误。但代表们并没有这样做。今天,该网站获得了约2600名独立访客,并提交了约180份提交页面。不知道有多少page1表单提交。其中,有3个会话错误,低于平均值。将sessionState模式更改为SQLServer没有任何影响,我继续随机收到会话错误。不过,会话错误线索中的一小部分实际上进入了我们的数据库。
Session.Add("lead", this._Lead);
public static class SessionStore
{
private static HttpSessionState Session
{
get { return HttpContext.Current.Session; }
}
public static T Get<T>(object name, T defaultValue)
{
var key = name.ToString();
return SessionStore.Session.ContainsKey(key) ? (T)Session[key] : defaultValue;
}
public static void Set(object name, object value)
{
SessionStore.Session.Add(name.ToString(), value);
}
}