ASP.NET MVC 5 publish precompile problems - asp.net

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.

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)

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.

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

Related

Partial ASP.net deployment issue - "Could not load assembly "App_Web_xxx.dll", make sure that it is compiled before accessing the page"

I am using Visual Studio 2017, and the application target .net framework is 4.5.2.
This application ABC (to which I am providing an enhancement) is already deployed and functioning pretty well.
Now that I have to only perform changes on 1 page (a simple condition change), I am planning to deploy only App_Web_xxxx in the ".\bin" folder.
After publishing the application using the "Precompiled" configuration, I was able to identify the respective App_web_xxx.dll file and copied in App. Server bin folder. Renamed it according to existing requestEdit.aspx file. All looks good till now, and interestingly, this App_Web_xxx.dll is common for multiple aspx files available under a common folder (users/request).
Once I try executing this code by accessing the website (respective aspx.cs UI file), it gives the error:
"Could not load assembly "App_Web_xxx.dll", make sure that it is compiled before accessing the page".
But the issue is This dll file doesn't break for any other aspx page available under the User/request folder, it is failing only for feature I modified.
What I have done apart from above mentioned way (considering usual way of partial deployment process):
Replaced requestEdit.aspx page and added new dll, by keeping old dll as is (which can be used by other aspx pages in deployment folder)
Error changed to:
Could not load assembly 'App_Web_xxx.dll, version=0.0.0.0, Culture=neutral, Publickey Token=null' or one of its dependencies.The system cannot find the file specified.
Replaced requestEdit.aspx page and added new dll, by removing old dll and renaming App_Web_xxx.dll in "Imports = Default.aspx, App_Web_xxx.dll" in all required aspx files (for which the old dll was removed)
If I am replacing the whole solution (except web.config) the solution works fine with no issues, the only problem comes when I am replacing only 1 .dll file.
All the solutions I found are providing information of IIS version or dll copying from Server to Dev, but my problem is I'm publishing on dev env (local machine) and partially deploying on the server.
Need help to resolve this issue as this is really inconvenient to deploy the whole solution just for 1 condition change!
Additional Info:
Page Properties --> MSBuild Options --> Allow this Precompiled site to be updatable (checked)

Relocating config files of depending assemblies

My application uses NLog as a logging framework hidden behind a facade in MyLogging project. The project is referenced by a number of web sites to not make them directly dependent on NLog. After compilation NLog.config from the logging project ends up in bin folders of each site and NLog manages to find it automatically during startup. That is understandable and pretty much OK. However, editing any file within bin folder forces ASP.NET to restart the working process so I'd like to have NLog.config one folder up beside regular web.config.
Things I'm trying to achieve\avoid:
Keep NLog configuration in a single file `cause it's the same for all of my web sites
Not to embed it into sites web.config (to not trigger ASP.NET auto-restart)
Just moving the file after building projects with msbuild command is not an option because the sites are deployed to Azure (moved files won't be packaged during publishing process)
I'd like to avoid messing with post-deployment scripts and hard coding things like E:\siteroot\0
The issue is not actually NLog specific as I'd have same trouble with standard app.config files. What are my options here?
For ASP.NET applications, it's recommend to set:
"Build Action" to "content". Otherwise it won't be copied to the root folder (folder with web.config)
"copy to Output Directory" to "none". Otherwise it will be copied to your bin folder.
See:

Trimming the output of publishing

I've got a basic ASP.NET Web Application with the following publish settings:
Publish to File System.
Delete all existing files prior to publish - Ticked
Precompile during publishing - Unticked
Exclude files from the App_Data folder - Ticked
While the publish does work as expected, it seems to publish quite a bit of extra baggage.
\bin
Web.config
Web.Debug.config
Web.Release.config
WebServer.dll
\Properties
AssemblyInfo.cs
\PublishProfiles
Release.pubxml
MyWebForm.aspx
MyWebForm.aspx.cs
MyWebForm.aspx.designer.cs
Web.config
Web.Debug.config
Web.Release.config
WebServer.csproj
WebServer.csproj.user
Out of all this, I'm able to remove everything but the following:
\bin
WebServer.dll
MyWebForm.aspx
MyWebForm.aspx.cs
MyWebForm.aspx.designer.cs
Web.config
So, my question is two-fold:
Firstly, why does the publish option, publish various things such as *.csproj files, or the *.config files in the bin directory etc. as the site appears to function perfectly well without this baggage. What is the purpose of these files being made public?
Secondly, is there a way to configure the publish operation to just publish the minimum required files for the project?
I'm not sure as to the reason behind the publish option is publishing the unrequired files, but it seems there is a very easy way to stop it. In the properties of the project under the option "Package/Publish Web" there is a group "Items to deploy", where I am able to select "Only the files required to run the project".

Run equivalent to "Build Page" command for Visual Studio web site project from command line

As outlined in Tip/Trick: Optimizing ASP.NET 2.0 Web Project Build Performance with VS 2005, the "Build Page" command available within Visual Studio web site projects does the following:
the solution will compile all of the class library projects like before, then compile the /app_code directory and Global.asax file, and then instead of re-verifying all pages within the web-site it will only verify the current page you are working on, and any user controls that the page references.
Is there a way to access this functionality from msbuild and / or the command line?
I am setting up an automated build of a large Visual Studio web site project (based on Kentico CMS), which consists of:
a large number of CMS-related pages and user controls that we do not change
a small number of custom "web part" user controls that we are actively developing, all within a CMSWebParts/Custom directory within the web site
Pre-compiling the entire site using aspnet_compiler takes up to 10 minutes, which is too slow for a commit build. Ideally, I'd like to introduce a step that pre-compiles just our custom code. Note that we don't actually deploy the pre-compiled output (not recommended for Kentico sites), this step is intended only to validate the code in the .ascx files.
The best way I've found to reduce the pre-compile time for small changes to large web sites is to use the ASP.Net Compilation Tool (aspnet_compiler.exe) with in-place compilation.
Our build script runs the tool using the following command:
aspnet_compiler.exe -v / -p C:\path\to\MyWebSite
This command specifies the physical path to the web site but does not set the targetDir option, which results in the application being compiled in-place.
The benefit of in-place compilation is that aspnet_compiler will by default only compile files that have changed since the web site was last compiled (you can force it to recompile everything with the -c option). For example, when I run the above command against the web site for the first time, it takes about 10 mins to run. If I then change a single file and run it again, it only takes 30 seconds or so.
You may be curious as to how the compilation tool "knows" which files have changed. Compiling in-place doesn't modify the application being compiled, i.e., you won't end up with files like App_Web_xdqqvn5q.dll and default.aspx.cdcab7d2.compiled in the bin folder of your web application. The output is actually generated within the "Temporary ASP.NET Files" folder. When you specify a physical path (rather than an IIS metabase), a folder within your profile is used, e.g. C:\Users\your.name\AppData\Local\Temp\Temporary ASP.NET Files. Your web application source code is cross-referenced with data stored in Temporary ASP.NET Files to work out what has changed.
I think this might be of help for what you need to accomplish:
http://msdn.microsoft.com/en-us/library/dd293881.aspx
From what I read you can run the build from Visual Studio Command Prompt or from the windows command prompt.
Update:
I couldn't find anything on the internet relating to building only one page but using aspnet_compiler without -c parameter should speed up the compiling process since it will only recompile what has changed. If only thing that has changed since last build was the content of one .aspx page then running the compiler should act similar to Build Page.
-c
Specifies that the application to be compiled should be fully rebuilt.
Components that have already been compiled are compiled again. If this
option is omitted, the tool builds only those parts of the application
that have been modified since compilation was last performed.
aspnet_compiler usage is explained on this page:
http://msdn.microsoft.com/en-us/library/ms229863.aspx

ASP.NET Web Deployment Projects: getting rid of .compiled files

I'm using a Web Deployment Project in Visual Studio 2008 in order to prepare my ASP.NET application (ASP.NET web application, not ASP.NET web site) for being copied to several servers. I have to copy the files on local staging servers, on different servers via FTP and sometimes I have to fetch them from customers' servers.
So, it would be nice to have all files for deployment in a compact form without the necessity of doing a lot of comparing between source and destination. Web deployment projects have this nice feature: compile all your aspx and ascx files into a single (additional) assembly.
I somehow found out how to get rid of aspx placeholder files on the server, now I'd like to know if there is a (maybe self-made) way to get rid of these .compiled files.
From Rick Strahl's blog:
The .Compiled file is a marker file
for each page and control in the Web
site and identifies the class used
inside of the assembly. These files
are not optional as they map the ASPX
pages to the appropriate precompiled
classes in the precompiled assemblies.
If you remove the .Compiled file, the
page that it maps will not be able to
execute and you get a nasty execution
error.
Anybody out there with a creative idea, maybe using a module/handler which intercepts the check against the .compiled files in the bin folder?
The .compile file comes from pre-compiling on deployment. So you basically have 3 options:
Keep the .compiled file
Don't pre-compile and deploy source code
Turn this in to a Web Application instead of a Web Site and compile as an assembly
I have run in to the same problem myself. I actually choose #1 in most cases when dealing with deployment of Web Sites, but on the rare occasion when I know I am going to have to maintain the site for an extended period of time, I take the time to upgrade it to a Web Application.
I don't like the .compiled files either, but nobody gets hurt if they're there. So why bother?
You might want to take a look at Virtual Path Providers (KB how to here) in ASP.NET.
Credit for this suggestion must go to Cheeso and his self answered question here:
Can I get “WAR file” type deployment with ASP.NET?
I don't know about the .compiled files, but you could set up your servers to update their files with subversion instead of manually copying the files when you compile.
So you would compile the files using the Web deployment project (not into a single assembly), put them in a repository you created for this purpose, and on each server, just do an svn update to fetch and compare the files automatically.
I know it's not what you asked for directly, but it may be a path to explore.
Add "Exclude Filter" to your deployment project:
In the Deployment Project.
Right Click on Content Files.
Click on "Exclude Filter".
Add "*.Compiled"
click OK.
and thats it.
I remember at the days when I cant do Web Application with VWD Express, I use nant script to compile the project into a single dll and deploy, that would work (so I dont need the full VS to do dll deployment too), so if you really don't want to mess your project to Web Application, maybe this is a path to check too.
You can get rid of the .compiled files by using the aspnet_merge tool with the -r option.
Removes the .compiled files for the main code assembly (code in the App_Code folder). Do not use this option if your application contains an explicit type reference to the main code assembly.
If you publish your code as updateable (in publish settings) these files are generated. Uncheck that value and republish. This is an old question I know, but no answers are clearly defined for this here.

Resources