我有一个相当简单的ASP.MVC视图的性能问题。
这是一个登录页面,应该几乎是即时的,但需要大约半秒钟。
经过大量的挖掘,看起来问题是第一次调用Url.Action
- 它需要大约450毫秒(根据MiniProfiler)但这似乎非常慢。
对Url.Action
的后续调用时间<1毫秒,这更符合我的预期。
无论我使用Url.Action("action", "controller")
还是Url.Action("action")
,这都是一致的,但如果我使用Url.Content("~/controller/action")
似乎不会发生。当我拨打Html.BeginForm("action")
时也会发生这种情况。
有没有人知道造成这种情况的原因是什么?
深入了解source表明RouteCollection.GetVirtualPath
可能是罪魁祸首,因为Url.Action
和Html.BeginForm
都是共同的。但是,肯定是在整个地方都使用了?我的意思是,½秒太慢了。
我有20个左右的自定义路由(它是一个相当大的应用程序,带有一些遗留的WebForms页面),但即便如此,时间似乎太慢了。
任何想法如何解决?
答案 0 :(得分:5)
发现问题,路由表(欢呼Kirill)。
基本上我们有很多看起来像这样的路线:
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) + ")" });
事实证明,the Regex check is very slow非常缓慢。因此,我将其替换为IRouteConstraint
的实现,而只是检查HashSet
。
然后我改变了地图路线呼叫:
routes.MapRoute(
name: GetRouteName(),
url: subfolder + "/{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional },
constraints: new { controller = new HashSetConstraint(controllers) });
我还使用RegexConstraint mentioned in that linked article来处理更复杂的事情 - 包括很多这样的调用(因为我们有遗留的WebForm页面):
routes.IgnoreRoute(
url: "{*allaspx}",
constraints: new { allaspx = new RegexConstraint( @".*\.as[pmh]x(/.*)?") });
这两个简单的改变完全解决了问题; Url.Action
和Html.BeginForm
现在花费的时间可以忽略不计(即使有很多路线)。
答案 1 :(得分:1)
在我看来,你的问题是编译视图。您需要在构建时预编译视图,这个问题就会消失。 details here
答案 2 :(得分: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 + ")";
}
}
答案 3 :(得分:0)
我已将其剥离为“裸露的骨头” ...将单个文件设置到内存中,然后与从IHttpModule下载它相比,从操作中下载它。由于某种原因(可能是 MVC管道加载,路由),IHttpModule的速度(对于小型文件(例如产品列表图片))要快得多。我没有在路由中使用正则表达式(这会使速度更慢)。在IHttpModule中,我达到的速度与URL指向驱动器上的文件的速度相同(当然,如果文件位于驱动器上,而不是URL指向的驱动器位置)。
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ImagesHandler" type="NsdMupWeb.ImagesHttpModule" />
</modules>
</system.webServer>
//Code is made for testing
public class ImagesHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += Context_BeginRequest;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if (app.Request.CurrentExecutionFilePathExtension.Length > 0)
{
var imagePathFormated = "/image/";
var imagesPath = app.Request.ApplicationPath.TrimEnd('/') + imagePathFormated;
if (app.Request.CurrentExecutionFilePath.StartsWith(imagesPath))
{
var path = app.Request.CurrentExecutionFilePath.Remove(0, imagesPath.Length);
var parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length > 1)
{
var ms = new MemoryStream();
Stream stream;
stream = System.IO.File.OpenRead(@"C:\Programming\Sindikat\Main\NsdMupWeb\Files\Cached\imageFormatProductList\1b1e2671-a365-4a87-97ba-063cf51ac34e.jpg");
var ctx = ((HttpApplication)sender).Context;
ctx.Response.ContentType = MimeMapping.GetMimeMapping(parts[1]);
ctx.Response.Headers.Add("last-modified", new DateTime(2000, 01, 01).ToUniversalTime().ToString("R"));
byte[] buffer = new byte[stream.Length / 2];
stream.Read(buffer, 0, buffer.Length);
ctx.Response.BinaryWrite(buffer);
buffer = new byte[stream.Length - buffer.Length];
stream.Read(buffer, 0, buffer.Length);
ctx.Response.BinaryWrite(buffer);
ctx.Response.End();
}
}
}
}
}