2012-08-10 10 views
6

Ich habe ein Leistungsproblem mit einer relativ einfachen ASP.MVC-Ansicht.Der erste Aufruf von Url.Action auf einer Seite ist langsam

Es ist eine Anmeldeseite, die fast augenblicklich sein sollte, dauert aber etwa eine halbe Sekunde.

Nach viel graben sieht es aus wie das Problem ist der erste Anruf der Url.Action - es dauert rund 450ms (nach MiniProfiler), aber das scheint wahnsinnig langsam.

Nachfolgende Aufrufe an Url.Action nehmen < 1ms, was mehr im Einklang mit dem ist, was ich erwarten würde.

Dies ist konsistent, ob ich Url.Action("action", "controller") oder Url.Action("action") verwende, scheint aber nicht zu passieren, wenn ich Url.Content("~/controller/action") verwende. Dies passiert auch, wenn ich Html.BeginForm("action") rufe.

Hat jemand eine Idee, was das verursacht?

Ein dig in die source legt nahe, dass RouteCollection.GetVirtualPath könnte die Ursache sein, als dass sowohl auf Url.Action und Html.BeginForm üblich ist. Aber sicher, dass das überall benutzt wird? Ich meine, eine halbe Sekunde ist viel zu langsam.

Ich habe 20 oder so benutzerdefinierte Routen (es ist eine ziemlich große App mit einigen alten WebForms Seiten), aber selbst dann scheinen die Zeiten viel zu langsam.

Irgendwelche Ideen, wie man es repariert?

Antwort

5

Problem gefunden, und es ist mit den Routing-Tabellen (Prost Kirill).

Grundsätzlich haben wir viele Routen, die wie folgt aussehen:

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

Es dass the Regex check is very slow stellt sich heraus, quälend langsam. Also ersetzte ich es durch eine Implementierung von IRouteConstraint, die nur gegen eine HashSet stattdessen prüft.

Dann änderte ich den Anruf Karte Route:

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

habe ich auch die RegexConstraint mentioned in that linked article für etwas komplizierter - darunter viele Anrufe wie folgt aus (weil wir Vermächtnis Webformular Seiten haben):

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

Diese zwei einfachen Änderungen beheben das Problem vollständig; Url.Action und Html.BeginForm nehmen Sie jetzt eine vernachlässigbare Zeit (sogar mit vielen Routen).

1

Es scheint mir, dass Ihr Problem das Erstellen von Ansichten ist. Sie müssen Ansichten auf Build vorkompilieren, und dieses Problem ist verschwunden. details here

+0

Das kompiliert Ansichten nicht vor, die sie nur nach einem Build kompilieren, so dass Sie Buildfehler statt Laufzeitfehler erhalten. Es machte auch keinen Unterschied - ich sehe immer noch 450ms beim ersten "Url.Action" -Ruf. – Keith

+0

Korrekte Vorkompilierung ist möglich mit ASPNet_Compiler.exe (siehe http://msdn.microsoft.com/en-us/library/ms229863(v=vs.80).aspx) aber auch ohne die Zeit sehe ich für ' Url.Action' ist verrückt - es ist fast so, als würde man die volle Reflektion machen, um die Controller-Aktionen jedes Mal zu finden, wenn die Seite läuft. – Keith

+0

Können Sie Ihre RegisterRoutes (von global.asax) anzeigen? Es kann sich auf Ihre Zeit auswirken. –

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

Das ist eine andere nützliche Implementierung, ich benutzte die von http://samsaffron.com/archive/2011/10/13/optimizing-asp-net-mvc3-routing – Keith