2012-08-10 13 views
6

Tengo un problema de rendimiento con una vista ASP.MVC bastante simple.Primera llamada a Url. La acción en una página es lenta

Es una página de inicio de sesión que debe ser casi instantánea, pero tarda aproximadamente medio segundo.

Después de una gran cantidad de excavaciones, parece que el problema es la primera llamada al Url.Action - está tomando alrededor de 450ms (de acuerdo con MiniProfiler), pero eso parece increíblemente lento.

Las llamadas posteriores a Url.Action están tomando < 1ms, lo cual está más en línea con lo que esperaría.

Esto es consistente ya sea que use Url.Action("action", "controller") o Url.Action("action"), pero no parece suceder si uso Url.Content("~/controller/action"). Esto también ocurre cuando llamo al Html.BeginForm("action").

¿Alguien tiene alguna idea de lo que está causando esto?

una excavación en el source sugiere que RouteCollection.GetVirtualPath podría ser el culpable, ya que es común a ambos Url.Action y Html.BeginForm. Sin embargo, seguramente eso se usa en todo el lugar? Quiero decir, ½ segundo es demasiado lento.

Tengo 20 o más rutas personalizadas (es una aplicación bastante grande con algunas páginas heredadas de WebForms) pero incluso entonces los tiempos parecen demasiado lentos.

¿Alguna idea de cómo solucionarlo?

Respuesta

5

Problema encontrado, y es con las tablas de enrutamiento (aclamaciones Kirill).

Básicamente tenemos un montón de rutas que ser algo como esto:

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) + ")" }); 

Resulta que the Regex check is very slow, dolorosamente lento. Así que lo reemplacé con una implementación de IRouteConstraint que simplemente compara con un HashSet.

Entonces cambió la llamada hoja de ruta:

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

También se utiliza la RegexConstraint mentioned in that linked article para algo más complicado - incluyendo un montón de llamadas de este tipo (porque tenemos páginas legado WebForm):

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

Esos dos cambios simples solucionan completamente el problema; Url.Action y Html.BeginForm ahora toman una cantidad de tiempo despreciable (incluso con muchas rutas).

1

Me parece que su problema es la compilación de vistas. Necesita precompilar las vistas en la compilación y este problema desaparecerá. details here

+0

Eso no precompila las vistas, solo las compila después de una compilación para que obtenga errores de compilación en lugar de errores de tiempo de ejecución. Tampoco hizo ninguna diferencia: sigo viendo 450ms más o menos en la primera llamada 'Url.Action'. – Keith

+0

La precompilación correcta es posible con ASPNet_Compiler.exe (vea http://msdn.microsoft.com/en-us/library/ms229863(v=vs.80).aspx) pero incluso sin eso, estoy viendo el momento ' Url.La acción es una locura: es casi como si estuvieras haciendo una reflexión completa para encontrar las acciones del controlador cada vez que se ejecuta la página. – Keith

+0

¿Podría mostrar sus RegisterRoutes (desde global.asax)? Puede aprovechar su tiempo. –

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

Esa es otra implementación útil, estaba usando la de http://samsaffron.com/archive/2011/10/13/optimising-asp-net-mvc3-routing – Keith

Cuestiones relacionadas