Многостраничная форма MVC проигрышная сессия
У меня есть многостраничная форма, которая используется для сбора потенциальных клиентов. Существует несколько версий одной формы, которые мы называем кампаниями. Некоторые кампании представляют собой 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); }
}
Действие кампании 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. Но когда возникает эта ошибка, между Campaign POST и Submit POST существует другой идентификатор посетителя. Поскольку мы отслеживаем каждую отправку формы с помощью метода TrackLead() во время кампании и отправляем действия, я вижу, что сеанс теряется между вызовами, несмотря на то, что OnActionExecuted запускается после каждого POST и сохраняет форму в сеансе.
Таким образом, при возникновении ошибок мы получаем половину формы под одним идентификатором посетителя, а оставшуюся часть формы под другим идентификатором посетителя. К счастью, мы используем стороннюю службу, которая отправляет вызов API каждый раз, когда изменяется значение формы, которая использует свой собственный идентификатор. Эти идентификаторы согласуются между первой половиной формы и остальной частью формы, и единственным способом, которым я могу сохранить отрывки из потерянных проблем сеанса.
Я должен также отметить, что это работает хорошо в 99% случаев.
РЕДАКТИРОВАТЬ: я изменил свой код, чтобы явно сохранить мой ведущий объект в TempData и использовал метод TempData.Keep(), чтобы сохранить объект между последующими запросами. Я только развернул это поведение на 1 из 3 моих сайтов, но пока все хорошо.
Я также попытался сохранить свои ведущие объекты в сеансе непосредственно в действии контроллера, т.е.
Session.Add("lead", this._Lead);
который использует HTTPSessionStateBase, пытаясь обойти класс оболочки, вместо HttpContext.Current.Session, который использует HTTPSessionState. Эта модификация не внесла изменений в вопрос, как и ожидалось.
РЕДАКТИРОВАТЬ #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);
}
}