見込み客の収集に使用する複数ページのフォームがあります。キャンペーンと呼ばれる同じフォームの複数のバージョンがあります。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 の訪問者 ID を取得します。しかし、これらのエラーが発生すると、Campaign POST と Submit POST の間で訪問者 ID が異なります。キャンペーンおよび送信アクション中に TrackLead() メソッドを介して各フォーム送信を追跡するため、すべての POST 後に OnActionExecuted が起動し、フォームをセッションに保存しているにもかかわらず、呼び出し間でセッションが失われていることがわかります。
したがって、エラーが発生すると、フォームの半分が 1 つの visitorID で取得され、フォームの残りが別の VisitorID で取得されます。幸いなことに、独自の ID を使用するフォーム値が変更されるたびに API 呼び出しを送信するサード パーティ サービスを使用しています。これらの ID は、フォームの前半と残りのフォームの間で一貫しており、セッションの損失の問題からリードを救う唯一の方法です。
また、これは 99% の確率で正常に機能することにも注意してください。
編集: リード オブジェクトを TempData に明示的に格納するようにコードを変更し、TempData.Keep() メソッドを使用して後続のリクエスト間でオブジェクトを永続化しました。この動作を 3 つのサイトのうちの 1 つにしか展開していませんが、これまでのところ問題ありません。
私はまた、コントローラーのアクションで直接セッションにリードオブジェクトを保存しようとしました。つまり、
Session.Add("lead", this._Lead);
HTTPSessionStateBase を使用し、HTTPSessionState を使用する HttpContext.Current.Session の代わりに、ラッパー クラスを回避しようとします。予想どおり、この変更は問題に違いをもたらしませんでした。
編集 #2: 今朝メールをチェックすると、リード モデルを TempData に保存したにもかかわらず、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);
}
}