问题
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?
回答1:
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).
回答2:
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
回答3:
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