2014-01-21 110 views
3

我尝试预编译Azure WebApplication,因此第一次点击并不会在页面的每次首次点击时花费几秒钟时间。Azure预编译似乎不起作用

我有下面的代码在我WebRole.Run()

using (var server_manager = new ServerManager()) 
{ 
    var main_site    = server_manager.Sites[RoleEnvironment.CurrentRoleInstance.Id + "_Web"]; 
    var main_application  = main_site.Applications["/"]; 
    var main_application_pool = server_manager.ApplicationPools[main_application.ApplicationPoolName]; 

    string physical_path = main_application.VirtualDirectories["/"].PhysicalPath; 

    main_application["preloadEnabled"] = true; 
    main_application_pool["autoStart"] = true; 
    main_application_pool["startMode"] = "AlwaysRunning"; 

    server_manager.CommitChanges(); 

    Log.Info("Building Razor Pages", "WebRole"); 

    var build_manager = new ClientBuildManager("/", physical_path); 
    build_manager.PrecompileApplication(); 

    Log.Info("Building Razor Pages: Done", "WebRole"); 
} 

它似乎并没有抛出任何异常,当我看着日志大约需要55秒做build_manager.PrecompileApplication()

似乎对我很正确。

除了当我第一次尝试加载页面时仍会受到打击。如果我看看MiniProfiler,我特别看到Find部分需要很长时间。我仍然怀疑这是汇编,因为1.5秒找到一个文件似乎对我来说有点长。

enter image description here

有什么毛病我上面的方法呢?有没有办法检查页面是否真的编译?而且,在编译的情况下,还能有什么?为什么o为什么东西太复杂了......

+0

你能试着从这个问题http://stackoverflow.com/q/10830044/57428的代码,看看它是否有差别? – sharptooth

+0

假设VirtualPath是“/”,问题中的代码似乎是相同的。或者你的意思是添加第三个'null'参数或额外的ClientBuildManagerParameters? –

+0

不,我的意思是枚举应用程序的所有'VirtualDirectories'并使用它们的路径。与直接传递'/'相比,可能会有一些愚蠢的细微差别。 – sharptooth

回答

2

我从来没有得到过它的工作。可能有些奇怪的Web.config/Azure /项目设置似乎关闭了,但我从未找到它。

在您进一步阅读之前,我建议您先为您的项目尝试这种方法:https://stackoverflow.com/a/15902360/647845。 (因为它不依赖于黑客)

我的解决方法是相对简单的,我搜索应用程序中的所有刀片意见,而且比我只是尝试用嘲笑ControllerContext来呈现他们,驳回的结果。由于一个非有效的ViewModel,所有的Views都会抛出一个异常,但只要视图首先被编译,这并不重要。

private void PrecompileViews() 
{ 
    string search_path = GetSearchPath(); 

    // Create a dummy ControllerContext - using the HomeController, but could be any controller 
    var http_context = new HttpContext(new HttpRequest("", "http://dummy", ""), new HttpResponse(new StringWriter())); 
    http_context.Request.RequestContext.RouteData.Values.Add("controller", "Home"); 
    var controller_context = new ControllerContext(new HttpContextWrapper(http_context), http_context.Request.RequestContext.RouteData, new HomeController()); 

    // loop through all views, and try to render them with the dummy ControllerContext 
    foreach (var file in Directory.GetFiles(search_path, "*.cshtml", SearchOption.AllDirectories)) 
    { 
     string relative_view_path = "~" + file.Replace(search_path, "", StringComparison.InvariantCultureIgnoreCase); 

     var view_result = new ViewResult { ViewName = relative_view_path }; 

     try 
     { 
      view_result.ExecuteResult(controller_context); 
     } 
     catch (Exception) 
     { 
      // Almost all views will throw exceptions, because of a non valid ViewModel, 
      // but that doesn't matter because the view stills got compiled 
     } 
    } 
} 

GetSearchPath返回你的应用程序的根目录:

private string GetSearchPath() 
{ 
    string bin_path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase); 
    string local_path = new Uri(bin_path).LocalPath; 
    return Path.GetFullPath(Path.Combine(local_path, "..")); 
} 

我呼吁这一切在我的Global.asax结束:

protected void Application_Start() 
{ 
    [...] 
    PrecompileViews(); 
} 
+0

Application_Start()会在每次应用程序池回收时每运行27小时或什么东西以及类似20分钟不活动之后运行。 – sharptooth

+0

我禁用了超时(请参阅:http://blog.smarx.com/posts/controlling-application-pool-idle-timeouts-in-windows-azure)。应用程序池回收我必须看看,谢谢! –

+0

有一个更好的方法来运行这个代码 - 制作一个控制器 - 动作对,并通过从角色OnStart()内部向自己发送一个HTTP请求来调用它。检查操作中的“HttpRequest.IsLocal”,以便外部人员不能触发它。 – sharptooth