I have a performance issue with a fairly simple ASP.MVC view.
It's a log-on page that should be almost instant, but is taking about half a second.
After a lot of digging it looks like the problem is the first call the Url.Action
- it's taking around 450ms (according to MiniProfiler) but that seems insanely slow.
Subsequent calls to Url.Action
are taking <1ms, which is more in line with what I would expect.
This is consistent whether I use Url.Action("action", "controller")
or Url.Action("action")
, but doesn't seem to happen if I use Url.Content("~/controller/action")
. This also happens when I call Html.BeginForm("action")
.
Does anyone have any idea what's causing this?
A dig into the source suggests that RouteCollection.GetVirtualPath
might be the culprit, as that's common to both Url.Action
and Html.BeginForm
. However, surely that's used all over the place? I mean, ½ a second is far too slow.
I have 20 or so custom routes (it's a fairly large app with some legacy WebForms pages) but even then the times seem far too slow.
Any ideas how to fix it?
Problem found, and it is with the routing tables (cheers Kirill).
Basically we have lots of routes that look something like this:
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) + ")" });
It turns out that the Regex check is very slow, painfully slow. So I replaced it with an implementation of IRouteConstraint
that just checks against a HashSet
instead.
Then I changed the map route call:
routes.MapRoute(
name: GetRouteName(),
url: subfolder + "/{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional },
constraints: new { controller = new HashSetConstraint(controllers) });
I also used the RegexConstraint mentioned in that linked article for anything more complicated - including lots of calls like this (because we have legacy WebForm pages):
routes.IgnoreRoute(
url: "{*allaspx}",
constraints: new { allaspx = new RegexConstraint( @".*\.as[pmh]x(/.*)?") });
Those two simple changes completely fix the problem; Url.Action
and Html.BeginForm
now take a negligible amount of time (even with lots of routes).
It seems to me that your problem is compiling of views. You need to precompile views on build and this problem will gone. details here
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 + ")";
}
}
来源:https://stackoverflow.com/questions/11900134/first-call-to-url-action-on-a-page-is-slow