ASP.NET MVC 5 publish precompile problems
Asked Answered
M

3

10

I have used the web publishing tool to publish my MVC 5 app in the past without precompiling in the past. As part of my effort to reduce the initial load time for each page, I have modified my publish settings as shown below to precompile the app during the publish process. All of a sudden, the incredibly reliable publishing that I was used to has become a nightmare.

  • My understanding of the "Merge all outputs to a single assembly" would mean that all of my .cshtml pages would get compiled together into Dashboard.Precompiled.dll, which would get deployed to IIS. This has not been the case - when I am able to get publishing to work, it creates a .complied file for each .cshtml file in my project and does not perform any merging.

  • The major issue right now is that the .compiled are only generated some of the time. When I look in the obj\Debug\AspnetCompileMerge\TempBuildDir\bin directory there are no .compiled files nor Dashboard.Precompiled.dll.

I have tried restarting visual studio, cleaning the solution and rebuilding, previewing vs. not previewing the changes before publish, creating a brand new publish profile, and fiddling with the advanced precompile settings time and time again. Usually after 30 minutes or so of mucking with it I can get the .precompiled files to generate and publish successfully, however I have not been able to identify what caused it to work correctly that time. The next time I go and publish without changing any settings, it will stop working again. The issue occurs when using either VS2015 or VS2017RC.

Can anyone please help point me in the right direction here? I've sunk many hours into this already and just feel like I'm going in circles at this point.

Thanks!

EDIT I took a closer look at the build output and found the call to aspnet_compiler.exe was executed with the following parameters:

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v / -p C:\Users\steve\Source\Dashboard\master\src\Agility.Web\obj\Staging\AspnetCompileMerge\Source -d C:\Users\steve\Source\Dashboard\master\src\Agility.Web\obj\Staging\AspnetCompileMerge\TempBuildDir

When I run this command directly from the command line, no .compiled files are being generated in TempBuildDir\bin.

enter image description here

Microcosm answered 7/2, 2017 at 13:55 Comment(12)
have you change solution configuration to the release mode?Calvaria
I have tried that before, although I am currently publishing to a test environment so I am using debug configuration. Would changing to release mode somehow impact the output of the aspnet compiler?Microcosm
Is your web.config is being published?Calvaria
Also Check the web.config files properties and see if Build Action is set to None if yes set it to Content. and run the command again.Calvaria
Also Change DashBoard.Precomiled.dll to DashBoard.Precomiled only.Calvaria
Check the Treat as Library Component check box as well.Calvaria
have you made any progress yet?Calvaria
The web config file was getting published and already set to Content. I changed the name to Dashboard.Precompiled and checked the treat as library component and it generated the .compiled files correctly. Any insight on what these actions might have done to get it working?Microcosm
You might have just missed some options. nothing to worry about if its working. weird things happen in developer world some time something working and other time it stop working. :) Now you have steps here how to solve if this issue arises again :)Calvaria
shall i put these comments as answer ? if you likeCalvaria
Yes, please go ahead so I can give you the bounty. I'm going to test publishing a few more times after restarting VS and changing the build configuration to Release to make sure it will work when I push into production. Just want to be sure this takes care of it once and for all, since I could get it working intermittently before. Will award by EOD if all goes well. Thanks for the help!Microcosm
i have posted above comments as an answer. ThanksCalvaria
C
4

Please try following steps.

1. Change solution configuration to the release mode.

2. Make sure web.config is being published.

3. Check the web.config file's properties and see if Build Action is set to None if yes set it to Content. and run the command again.

4. Change DashBoard.Precomiled.dll to DashBoard.Precomiled only.

5. Check the Treat as Library Component check box as well.

For more detail about the options visit ASP.NET Compilation Tool (Aspnet_compiler.exe)

Thanks

Calvaria answered 10/2, 2017 at 9:19 Comment(0)
S
13

I wanted to find out exactly what the differences were between:

  • "Do not merge" and "Do not merge. Create a separate assembly for each page and control."
    • It sounds like these do the same thing, what's the difference?
  • "Merge all outputs to a single assembly" and "Merge all pages and control outputs to a single assembly"
    • Again, these sound like the same thing (when you don't have an App_Code folder).

And I want to find out why "Allow precompiled site to be updatable" didn't seem to do anything when it was checked (i.e. I thought it would precompile the pages to their own assembly/assemblies while also emitting the original editable *.aspx, .ascx, .master files.

So, today I sat-down and created a spreadsheet and ran every different Publish Profile Precompilation setting with an ASP.NET WebForms *.csproj application - I also wanted to see what was the slowest vs. quickest output.

Background:

  • My ASP.NET project targets .NET Framework 4.7.2
  • It is not a "Website project".
  • Raw C# *.cs files are not published to the production web-server.
  • It is a WebForms project using *.aspx, *.ascx, *.master, *.ashx and Global.asax. It is not an MVC project using the Razor *.cshtml nor WebForms *.aspx view-engines).
  • It does not use the App_Code folder (so the "Treat as library component" option has no effect).

Findings:

Important notes:

  • These findings only apply if you're working with a "traditional" ASP.NET Web Forms Web Application project. These do not apply to ASP.NET Website Projects (where you don't have a *.csproj file), ASP.NET MVC (non-Core) projects, nor ASP.NET Core projects.
  • I use the term "Page files" as a shorthand for *.aspx, *.ascx, *.master, *.asmx, and *.ashx files, but not Global.asax.
  • I use the term "Built normally" to refer to doing a "Build > Rebuild project" in Visual Studio, where you see the output in your %projectdir%\bin directory. This is compared to doing a Publish Build (which will perform a normal build first, then copy the output to another directory to run the Publish MSBuild steps)

Here are my findings for what each option results in:

  • "Precompile during publishing" (in Publish Settings window)

    • If you don't have an App_Code folder and you want to publish editable *.aspx/*.ascx/`*.master files then there is no performance reason to check this box.
      • This is because when this is checked but "Allow precompiled site to be updatable" is unchecked then it will only precompile your Global.asax file (not your Global.asax.cs, which is compiled anyway).
      • i.e. your *.aspx, *.ascx, *.master and *.ashx files will not be precompiled to an assembly, and they'll still need to be compiled on-demand on the web-server.
      • But it will still precompile them to check for compiler-errors and broken <% @-lines in your *.aspx, *.ascx, *.master, *.asax and *.ashx files.
  • "Allow precompiled site to be updatable"

    • When this checked your *.aspx, *.ascx, *.master and *.ashx files will not be precompiled to an assembly, and they'll still need to be compiled on-demand on the web-server.
      • I originally thought that it would precompile those files to an assembly (DLL) and additionally publish the original *.aspx files for editing on the server and only recompile them if they're changed - but I was wrong.
  • Emit debug information

    • This generates *.pdb files for each new assembly generated by the precompilation process. It does not affect any *.pdb files already present when your application is built normally.
    • I think this should always be enabled - PDB files are essential for quickly investigating runtime problems and they don't add too much to the final publish size.
  • Do not merge

    • When you don't have an App_Code folder and "Allow precompiled site to be updatable" is checked then "Do not merge" will only fully precompile Global.asax into App_global.asax.dll. No other DLL files will be added to the final publish output.

    • When "Allow precompiled site to be updatable" is unchecked then all Page files (defined under "Important notes" above) will be compiled into new DLL files App_Web_xxxxxxxx.dll in groups of 10 classes.

      • I cannot see how it decides to group files a pattern for which 10 files it uses - sometimes they're in alphabetical order, other times it's arbitrary.
  • Do not merge. Create a separate assembly for each page and control.

    • This is the same as above, except rather than being in groups of 10 Page files (or classes) per assembly, it's 1-Page file-per-assembly.
    • This is also one of the slowest publish builds when "Allow precompiled site to be updatable" is unchecked.
    • The only advantage to this approach is if you want to individually replace each page's precompiled *.dll on the server - but I don't think that's a good idea as it will often break - it's better to replace all files at once. Only do this if you're on a 56K connection and can only upload less than 100KB at a time - which is just silly.
  • Merge all outputs to a single assembly

    • Really does compile/merge all Page files and Global.asax (App_global.asax.dll) to a single DLL file.
  • Treat as library component

    • This option had zero effect on my project, whether it was checked or unchecked (as my project doesn't have an App_Code folder).
  • Merge each individual folder output to its own assembly

    • This generates intermediate DLLs for each filesystem directory in your project that contains Page files and then merges them into a single DLL for each folder.
    • This option resulted in the second longest Publish build time.
    • I can't think of a good reason why you would need to use this feature today - unless you have a project with thousands of Page files spread over tens of folders and want to do manual incremental updates. (i.e. this isn't an option you'd use in a CI/CD process).
  • Merge all pages and control outputs to a single assembly

    • If you have an App_Code folder:
      • Then the contents of App_Code (and other "Special folders" like App_GlobalResources, App_WebReferences) will be precompiled to this separate assembly from your Page files assembly. This will not include Global.asax (compiled to App_global.asax.dll).
    • If you don't have an App_Code folder then this option results in very similar output to "Merge all outputs to a single assembly" except the final output will precompile Global.asax to its own assembly (App_global.asax.dll).
      • This option resulted in the longest publish build time of all the options - for zero real benefit in my case.
      • So if you don't have an App_Code folder then there is no reason to choose this option.

Restated:

When "Allow precompiled site to be updatable" is checked and "Do not merge" is checked:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

    * Compiled only for error-checking.
    * Not compiled to an assembly DLL in the `bin\` folder.

Global.asax

    * Compiled to `App_global.asax.dll`

App_Code

    * Compiled to `App_Code.dll`

When "Allow precompiled site to be updatable" is not checked and "Do not merge" is checked:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

    * Compiled to `App_Web_abcdefghij.dll` in groups of 10-per-DLL

Global.asax

    * Compiled to `App_global.asax.dll`

App_Code

    * Compiled to `App_Code.dll`

When "Allow precompiled site to be updatable" is not checked and "Merge each individual folder output to its own assembly" is checked:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

    * Each file compiled to its own `App_Web_OriginalFileName.abcdefghij.dll` file.

Global.asax

    * Compiled to `App_global.asax.dll`

App_Code

    * Compiled to `App_Code.dll`

When "Allow precompiled site to be updatable" is not checked and "Merge all outputs to a single assembly (named 'Everything')" is checked:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

    * Compiled and merged into the single Everything.dll

Global.asax

    * Compiled and merged into the single Everything.dll

App_Code

    * Compiled and merged into the single Everything.dll

When "Allow precompiled site to be updatable" is not checked and "Merge each individual folder output to its own assembly" is checked:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

    * Compiled into an assembly for each folder.

Global.asax

    * Compiled to `App_global.asax.dll` (separate from the assembly for the *.aspx files in the root directory)

App_Code

    * Compiled and merged into `App_Code.dll`

When "Merge all pages and control outputs to a single assembly (named 'PagesAndControls')" is checked:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

    * Compiled into PagesAndControls.dll

Global.asax

    * Compiled to `App_global.asax.dll` (separate from PagesAndControls.dll)

App_Code

    * Compiled and merged into `App_Code.dll`

Conclusion:

If you don't need to edit *.aspx/*.ascx,/*.master files once they're deployed, and you don't have an App_Code folder, then choose these settings for the best results:

[ ] Allow precompiled site to be updatable
[X] Emit debug information
[X] Merge all outputs to a single assembly 
[ ] Treat as library component

Methodology:

  • All builds are using Release.
  • A "Folder" publish-profile was used.
    • The target was a folder on the same disk volume (a PCI-Express Optane drive).
    • The folder was cleared after each run.
  • The only change between each test run was to changet the parameters in the
  • git confirmed zero changes to source files and project files before each build and publish.
  • I ran a shell script that completely clears the bin and obj directories between each run, so the web-application project is fully rebuilt between runs instead of just the Publish).
  • I used a Stopwatch program that recorded the exact time I clicked the Publish button, but was manually stopped by a keyboard press when I saw the Publish operation was completed.

Results:

(Screenshot of my spreadsheet)

Screenshot of Excel spreadsheet of precompilation input options and observed output

Schlosser answered 4/8, 2019 at 19:7 Comment(7)
I don't get why was it important for you to measure speed of the build process. It would be much more interesting to know how much faster pages would load, using different settings, as that's one of the main reasons to use precompilation, correct me if I'm wrong.Kuhns
@Kuhns I don’t have to justify myself to you :)Schlosser
of course not :) just being curiousKuhns
@Kuhns FWIW I precompile not for any performance reason but to ensure my page files compile at design-time. No-one wants a runtime .aspx compile error YSOD.Schlosser
Ok, that makes sense, I understand. Good idea actually. The problem with me and my comment is that I was searching for a way to speed up load times at first access (because that part sucks in our app). For some reason I thought everyone else using precompile option is on the same track :) my apologies ;)Kuhns
@Kuhns Design-time precompilation does improve load-times, but not significantly. Personally I wouldn't waste time trying to speed-up any WebForms projects beyond simple precompilation: you should focus on updating your project to ASP.NET Core which is really fast at startup - which makes me wonder how technology designed in 2001 is much slower than ASP.NET Core on ultramodern hardware, it's weird.Schlosser
It's asp.net mvc 5 actually, with framework 4.7.2 in the back, but I get the point. We are using .NET Core in other places, just no resources to migrate this project at the moment (also it's not a very important one). Thanks for confirming that precompilation won't get us far... That's what we observed, too. I agree, spending time to migrate .NET Core is better spent. Thank you !Kuhns
E
7

Visual Studio uses ASP.NET compilation tool and ASP.NET merging tool which are the Aspnet_compiler.exe and Aspnet_merge.exe to compile an ASP.NET application.

Behind the scenes VS uses these 2 tools to compile web applications projects. Alternatively, you can invoke these 2 tools from the command line.

You can locate these 2 files by navigating to this directory: %WINDIR%\Microsoft.NET\Framework\v4.0.30319 (or to whatever framework version you are using). You can use these tools to compile ASP.NET applications. To learn more about all options of these 2 tools please read these links: Aspnet_compiler.exe and Aspnet_merge.exe

My recommendation on fixing the issue:

  • Restart Visual Studio
  • Clean Rebuild your solution
  • You should input just the name of the assembly without the .dll (in your example Dashboard.Precompiled.dll should be just Dashboard.Precompiled)
  • (you may consider) Restarting your machine

Please read more about Advanced Precompile settings on this link, I'm pasting here the options as well:

Allow precompiled site to be updatable - This setting corresponds to the –u option of the aspnet_compiler.exe command. If you select this option, pages and user controls (.aspx, .ascx, and .master files) are copied as-is to the target folder and can be updated as text files without recompiling the project. Otherwise, the HTML markup for pages and user controls is removed and compiled into the assembly output.

Emit debug information - This setting corresponds to the -d option of the aspnet_compiler.exe command.

Do not merge - This setting does not run aspnet_merge.exe and does not use the -fixednames option of the aspnet_compiler.exe command.

Do not merge. Create a separate assembly for each page and control - This setting does not run aspnet_merge.exe. Instead, it uses the -fixednames option of the aspnet_compiler.exe command. This option is useful if you want to make granular updates of your deployed web site. However, compiling with the -fixednames option disables the compiler's batch optimizations and can result in longer compile times for large web sites.

Merge all outputs to a single assembly - This setting is equivalent to the -o assemblyname option of the aspnet_merge.exe command.

Treat as library component (remove the App_Code.compiled file) - This setting corresponds to the -r option of the aspnet_merge.exe command. Selecting this option enables the project's App_Code.dll assembly to be added to the Bin folder of another web site without conflicting with the App_Code.dll assembly in the other web site. This is useful for building a library of .ascx controls

Merge each individual folder output to its own assembly - This setting corresponds to the -prefix prefixname option of the aspnet_merge.exe command. This option enables you to update your web site at the folder level rather than updating the entire application. You can use the Optional assembly prefix box to specify a prefix that will be pre-pended to all generated assembly names. For example, if you specify the prefix MyCompany, the name becomes MyCompany.SubfolderName.

Merge all pages and control outputs to a single assembly - This setting corresponds to the –w assemblyname option of the aspnet_merge.exe command. This option enables you to update UI elements separately from updating other code. Special folders such as App_Code, App_WebReferences, and so on are each compiled into a separate assembly. Specify the target assembly name in the Assembly name box.

Ely answered 9/2, 2017 at 21:40 Comment(6)
please have a look at the comments.Calvaria
oh cool! I just indicated the options for the precompile settings in VS because OP misunderstood the meaning of merge for the dialog box. This is not a competition to your answer these are just additional items to learn. Anyway that's what SO for, right? For "us" to learn?Ely
great, but he knows how these options and settings works. He just lost some where while configuring project to precompile.Calvaria
it's not just about him or you or me, it's about the people who will encounter the same issue in the future.Ely
Copying the pasting contents from a site should not be the answer for people it should consists of your experience of solving the problem, otherwise google provide very good search facility to look for topic. But any how your answer is there. but please link the MSDN site to your answer.i Hope you don't mind. ThanksCalvaria
Useful answer. Yes, MSDN contains the information, but it's helpful to have it right here next to the question it relates to. Thanks @ElyDiversiform
C
4

Please try following steps.

1. Change solution configuration to the release mode.

2. Make sure web.config is being published.

3. Check the web.config file's properties and see if Build Action is set to None if yes set it to Content. and run the command again.

4. Change DashBoard.Precomiled.dll to DashBoard.Precomiled only.

5. Check the Treat as Library Component check box as well.

For more detail about the options visit ASP.NET Compilation Tool (Aspnet_compiler.exe)

Thanks

Calvaria answered 10/2, 2017 at 9:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.