我有一个用于收集潜在客户的多页表单。我们称之为活动的同一表单有多个版本。有些活动是 3 页表单,有些是 2 页,有些是 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;
}
}
父存储类:
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); }
}
广告系列 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() 方法跟踪每个表单提交并提交操作,所以我可以看到会话在调用之间丢失,尽管 OnActionExecuted 在每次 POST 后触发并将表单存储在会话中。
因此,当出现错误时,我们会在一个visitorID 下获得一半的表单,而在另一个visitorID 下获得表单的其余部分。幸运的是,我们使用第三方服务,每次表单值更改时都会发送一个 API 调用,并使用它自己的 ID。这些 ID 在表格的前半部分和表格的其余部分之间是一致的,这是我可以从丢失的会话问题中保存线索的唯一方法。
我还应该注意,这在 99% 的情况下都可以正常工作。
编辑: 我已经修改了我的代码以将我的主要对象显式存储在 TempData 中,并使用 TempData.Keep() 方法在后续请求之间保留该对象。我只将此行为部署到我的 3 个站点中的 1 个,但到目前为止一切都很好。
我还尝试在控制器操作中直接将我的主要对象存储在 Session 中,即,
Session.Add("lead", this._Lead);
它使用 HTTPSessionStateBase,试图绕过包装类,而不是使用 HTTPSessionState 的 HttpContext.Current.Session。正如预期的那样,此修改对这个问题没有任何影响。
编辑#2: 今天早上检查我的电子邮件,尽管我将我的主要模型保存在 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);
}
}