2

我有一个用于收集潜在客户的多页表单。我们称之为活动的同一表单有多个版本。有些活动是 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);
    }
}
4

0 回答 0