我有一个用于收集潜在客户的多页表单。我们称之为广告系列的同一表单有多个版本。有些广告系列是3页的形式,有些则是2页,有些是1页。它们共享相同的主要模型和活动控制器等。有1个用于控制活动流程的操作,以及用于将所有主要信息提交到数据库的单独操作。
我无法在本地重现此内容,并且已进行检查以确保用户无法跳过页面。会话模式是InProc。
在每个POST操作之后运行,该操作将值存储在会话中:
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;
}
}
ParentStore类:
public static class ParentStore
{
internal static Lead Lead
{
get { return SessionStore.Get<Lead>(Constants.Session.Lead, new Lead()); }
set { SessionStore.Set(Constants.Session.Lead, value); }
}
Campaign POST操作:
[HttpPost]
public virtual ActionResult Campaign(Lead lead, string campaign, int page)
{
if (this.Session.IsNewSession)
return RedirectToAction("Campaign", new { campaign = campaign, page = 0 });
if (ModelState.IsValid == false)
return View(GetCampaignView(campaign, page), this.Lead);
TrackLead(this.Lead, campaign, page, LeadType.Shared);
return RedirectToAction("Campaign", new { campaign = campaign, page = ++page });
}
上述操作之间以及执行以下提交操作之前出现问题:
[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。但是当发生这些错误时,Campaign POST和Submit POST之间会有一个不同的visitorID。因为我们在广告系列和提交操作期间通过TrackLead()方法跟踪每个表单提交,我可以看到会话在调用之间丢失,尽管每次POST后都会触发OnActionExecuted并将表单存储在会话中。
因此,当出现错误时,我们会在一个visitorID下获得一半表单,并在另一个visitorID下获取表单的其余部分。幸运的是,我们使用第三方服务,每次表单值更改时都会发送一个API调用,该服务使用自己的ID。这些ID在表单的前半部分和表单的其余部分之间是一致的,也是我可以从丢失的会话问题中保存潜在客户的唯一方法。
我还应该注意到99%的时间都可以正常工作。
修改 我修改了我的代码,以便在RTData中显式存储我的主要对象,并使用TempData.Keep()方法在后续请求之间保留对象。我只是将这种行为部署到我的3个站点中的一个,但到目前为止还是那么好。
我还尝试直接在Session中将我的主要对象存储在控制器动作中,即
Session.Add("lead", this._Lead);
使用HTTPSessionStateBase,试图绕过包装类,而不是使用HTTPSessionState的HttpContext.Current.Session。正如预期的那样,这一修改在这个问题上没有任何区别。
编辑#2: 今天早上检查我的电子邮件,我注意到两个会话错有些事情导致会话清除,很可能是某种类型的应用程序池回收,但我还没有弄明白。我可能会开始在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);
}
}