2012-08-10 16 views
6

Ho un problema di prestazioni con una visualizzazione ASP.MVC piuttosto semplice.Prima chiamata a Url.Azione su una pagina è lenta

È una pagina di accesso che dovrebbe essere quasi istantanea, ma richiede circa mezzo secondo.

Dopo un sacco di scavo sembra che il problema è la prima chiamata il Url.Action - sta prendendo circa 450 ms (secondo MiniProfiler), ma che sembra follemente lento.

Le chiamate successive a Url.Action stanno prendendo < 1 ms, che è più in linea con quello che mi aspetterei.

È coerente se utilizzo Url.Action("action", "controller") o Url.Action("action"), ma non sembra che accada se utilizzo Url.Content("~/controller/action"). Questo succede anche quando chiamo Html.BeginForm("action").

Qualcuno ha idea di cosa sta causando questo?

A scavare nel source suggerisce che RouteCollection.GetVirtualPath potrebbe essere il colpevole, in quanto questo è comune ad entrambi Url.Action e Html.BeginForm. Tuttavia, sicuramente è usato dappertutto? Voglio dire, ½ secondo è troppo lento.

Ho circa 20 percorsi personalizzati (è un'app abbastanza grande con alcune pagine WebForms legacy) ma anche allora i tempi sembrano troppo lenti.

Qualche idea su come risolverlo?

risposta

5

problema riscontrato, ed è con le tabelle di routing (applausi Kirill).

Fondamentalmente abbiamo un sacco di percorsi che simile a questa:

string[] controllers = GetListOfValidControllers(); 

routes.MapRoute(
    name: GetRouteName(), 
    url: subfolder + "/{controller}/{action}/{id}", 
    defaults: new { action = "Index", id = UrlParameter.Optional }, 
    constraints: new { controller = "(" + string.Join("|", controllers) + ")" }); 

Si scopre che the Regex check is very slow, dolorosamente lento. Quindi l'ho sostituito con un'implementazione di IRouteConstraint che controlla invece un HashSet.

Poi ho cambiato la chiamata mappa del percorso:

routes.MapRoute(
    name: GetRouteName(), 
    url: subfolder + "/{controller}/{action}/{id}", 
    defaults: new { action = "Index", id = UrlParameter.Optional }, 
    constraints: new { controller = new HashSetConstraint(controllers) }); 

ho anche usato il RegexConstraint mentioned in that linked article per qualcosa di più complicato - tra cui un sacco di chiamate di questo tipo (perché abbiamo pagine eredità WebForm):

routes.IgnoreRoute(
    url: "{*allaspx}", 
    constraints: new { allaspx = new RegexConstraint(@".*\.as[pmh]x(/.*)?") }); 

Queste due semplici modifiche risolvono completamente il problema; Url.Action e Html.BeginForm ora richiedono un tempo trascurabile (anche con molti percorsi).

1

Mi sembra che il problema sia la compilazione di viste. È necessario precompilare le viste sulla build e questo problema sarà risolto. details here

+0

Ciò non precompilare vista, che appena li compila dopo una compilazione in modo da ottenere errori di generazione, piuttosto che errori runtime. Inoltre non ha fatto alcuna differenza - sto ancora vedendo 450ms circa alla prima chiamata "Url.Action". – Keith

+0

La corretta pre-compilazione è possibile con ASPNet_Compiler.exe (vedere http://msdn.microsoft.com/en-us/library/ms229863(v=vs.80).aspx) ma anche senza che il tempo che sto vedendo è ' url.Le azioni sono pazzesche: è quasi come se stesse riflettendo a fondo per trovare le azioni del controller ogni volta che la pagina scorre. – Keith

+0

Puoi mostrare i tuoi RecordRoute (da global.asax)? Può sfruttare il tuo tempo. –

1
public class RegexConstraint : IRouteConstraint, IEquatable<RegexConstraint> 
    { 
    Regex regex; 
    string pattern; 

    public RegexConstraint(string pattern, RegexOptions options = RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase) 
    { 
     regex = new Regex(pattern, options); 
     this.pattern = pattern; 
    } 

    public bool Match(System.Web.HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) 
    { 
     object val; 
     values.TryGetValue(parameterName, out val); 
     string input = Convert.ToString(val, CultureInfo.InvariantCulture); 
     return regex.IsMatch(input); 
    } 

    public string Pattern 
    { 
     get 
     { 
      return pattern; 
     } 
    } 

    public RegexOptions RegexOptions 
    { 
     get 
     { 
      return regex.Options; 
     } 
    } 

    private string Key 
    { 
     get 
     { 
      return regex.Options.ToString() + " | " + pattern; 
     } 
    } 

    public override int GetHashCode() 
    { 
     return Key.GetHashCode(); 
    } 

    public override bool Equals(object obj) 
    { 
     var other = obj as RegexConstraint; 
     if (other == null) return false; 
     return Key == other.Key; 
    } 

    public bool Equals(RegexConstraint other) 
    { 
     return this.Equals((object)other); 
    } 

    public override string ToString() 
    { 
     return "RegexConstraint (" + Pattern + ")"; 
    } 
} 
+0

Questa è un'altra implementazione utile, stavo usando quella da http://samsaffron.com/archive/2011/10/13/optimising-asp-net-mvc3-routing – Keith

Problemi correlati