2012-07-20 5 views
7

Sto avendo un comportamento strano, che non può replicarsi sulla macchina locale e sta iniziando a farmi impazzire.L'azione del controller viene richiamata due volte ei registri IIS non mostrano che

Sembra ASP.NET MVC sta cercando di eseguire l'azione, qualcosa di timeout, non riesce senza alcuna eccezione e comunicazione al cliente ajax, poi cerca di eseguire nuovamente l'azione, ajax client ottiene risposta, ma non dalla chiamata originale.

ho un azione di controllo:

[ValidateAntiForgeryToken] 
[ClientErrorHandler] 
public virtual ActionResult PlaceOrder(CheckoutDto checkoutDto) 
{ 
    LoadDataFromDatabase(); 

    // this may take up to couple minutes 
    var orderConfirmationData = PlaceOrderToExternalWebservice(); 

    SaveConfirmationData(orderConfirmationData); 

    return View(Transform(orderConfirmationData)) 
} 

E mi chiamano utilizzando jQuery Ajax:

$.ajax({ 
       url: placeOrderActionUrl, 
       type: "POST", 
       async: true, 
       dataType: "html", 
       data: $('#checkoutForm').serialize(),      
       success: function (data) { 
        // show confirmation data      
       }, 
       error: function (request, status, error) { 
        // show error message 
       } 
      }); 

E per i piccoli ordini funziona bene, ma per i grandi ordini vengono creati e ragione due ordini sembra essere il tempo di elaborazione, maggiore è l'ordine, maggiore è il tempo impiegato per il webservice esterno.

Ho controllato i registri IIS, per assicurarsi che lo script client non stia chiamando l'azione due volte e che i registri IIS mostrino solo una chiamata a un'azione particolare.

Il servizio esterno non ha esito negativo, non ci sono eccezioni registrate nel registro eventi/log sql.

Per assicurarsi, quel cliente ajax ottiene risposta non dalla chiamata originale che ho fatto tipo di serratura:

[ValidateAntiForgeryToken] 
[ClientErrorHandler] 
public virtual ActionResult PlaceOrder(CheckoutDto checkoutDto) 
{ 
    try 
    { 
     if (OrderingLockedForCurrentUser()) 
     { 
      Log("Locked"); 
      return View("Already placing order"); 
     } 

     LockOrderingForCurrentUser(); 

     LoadDataFromDatabase(); 

     // this may take up to couple minutes 
     var orderConfirmationData = PlaceOrderToExternalWebservice(); 

     SaveConfirmationData(orderConfirmationData); 

     return View(Transform(orderConfirmationData)) 
    } 
    finally 
    { 
     RemoveOrderingLockForCurrentUser(); 
    } 
} 

E invece di tornare informazioni confermate, esso restituisce "già ordinare".

ho pensato che forse azione di esecuzione timeout, ma ho provato solo per amor

<httpRuntime executionTimeout="600" /> 

non ha aiutato.

Qualche idea su dove cercare un motivo, che altro da controllare, per abilitare qualsiasi registrazione aggiuntiva?

Aggiornamento: Interessante è che anche la chiamata originale è stata completata.

Update 2: ho aggiunto filtro azione AjaxOnly per essere sicuri, che viene chiamato solo da javascript:

public class AjaxOnlyAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (!filterContext.HttpContext.Request.IsAjaxRequest()) 
     {     
      throw new Exception("This action is intended to be called from Ajax only."); 
     } 
    } 
} 

E dai registri è chiamato solo da JavaScript, in modo mistero continua ...

Update 3:

ho isolato problema di semplice sonno Discussione in test controller separato:

[ValidateAntiForgeryToken] 
    [AjaxOnly] 
    [ClientErrorHandler] 
    public virtual ActionResult PlaceOrderAction(CheckoutDto checkoutDto) 
    { 
     try 
     { 
      if (CanPlaceOrder(Request.RequestContext.HttpContext)) 
      {     
       Thread.Sleep(TimeSpan.FromSeconds(90)); 

       return Content("First time"); 
      } 

      return Content("Second time"); 
     } 
     finally 
     { 
      HttpContext.Cache.Remove(GetKey(userService.CurrentUser.UserId)); 
     } 
    } 

    public bool CanPlaceOrder(HttpContextBase httpContext) 
    { 
     var userId = userService.CurrentUser.UserId; 
     var key = GetKey(userId); 

     if (httpContext.Cache[key] == null) 
     { 
      httpContext.Cache.Add(key, userId, null, DateTime.Now.AddMinutes(10), new TimeSpan(), CacheItemPriority.High, null); 
      return true; 
     } 

     return false; 
    } 

    private static string GetKey(int userId) 
    { 
     return "PlacingOrder{0}".With(userId); 
    } 

Finché funziona correttamente su due macchine dev indipendenti (win 7) e una macchina di staging in ec2 (win2008sp2), è quasi certamente un problema con le impostazioni IIS del server di produzione (win 2008R2 x64 sp1).

+0

Utilizzare async per chiamate lunghe che possono scadere. Puoi aggiungere un token di annullamento. Vedi il mio tutorial http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4 – RickAndMSFT

risposta

0

trovato.

Come ho iniziato a pensare, che la configurazione di IIS è in qualche modo involded a questo, ho iniziato un nuovo sito di prova sulla produzione IIS, normalmente cercate sito MVC con 90 secondi di thread sleep
E ha funzionato come supposto
Quindi ho copiato l'intero sito di produzione per l'esecuzione su porte diverse, pensando che sarò in grado di testare le idee più velocemente senza interessare il sito di produzione - e quando era in esecuzione su diversi porti non ci sono stati problemi

Si scopre che stavamo usando il bilanciatore Amazon Elastic ed era il problema.

Morale è che avrei sprecato molto meno tempo, se stavo isolando aggressivamente il problema fin dall'inizio.

0

Ecco il suggerimento di una domanda simile:?

"C'è qualche altro markup che potrebbe essere accidentalmente riferimento alla pagina di riferimenti a script, riferimenti di immagini, riferimenti CSS, tutti potrebbero essere erroneamente puntato '' o la pagina corrente."

MVC controller is being called twice

+0

Ho visto questo, ma non è il caso perché un) ii i log mostrerebbero ancora 2 chiamate b) chiamerebbero due volte per tutti gli ordini, non solo quelli grandi c) ho aggiunto il filtro per consentire l'azione da chiamare da ajax – Giedrius