A few times when I copy-paste *.cshtml
files, Visual Studio for some reason sets Build Action
on these files to be "None":
This is impossible to detect when you work locally, because the files are present. But when you deploy through WebDeploy, files marked as "None" on Build Action are not packaged. As a result I get non-working application on the server.
Question: is there a way to automatically detect such occurrences and prevent?
You could extend the .csproj
with a small snippet that will generate a warning when an item in the "None" group has the extension .cshtml
. The snippet would be:
<Target Name="EnsureContentOnViews" BeforeTargets="BeforeBuild">
<ItemGroup>
<Filtered Include="@(None)" Condition="'%(Extension)' == '.cshtml'" />
</ItemGroup>
<Warning
Condition="'@(Filtered)'!=''"
Code="CSHTML"
File="$(MSBuildProjectDirectory)\%(Filtered.Identity)"
Text="View is not set to [BuildAction:Content]"
/>
</Target>
If you see other build actions (like EmbeddedResource
), you can add them to the Filtered item definition.
If you want more advanced detection you need to actually parse the project files for any item that fits this Xpath //ItemGroup/*[not(self::Content)]/@Include
<Target Name="EnsureContentOnViewsXML" BeforeTargets="BeforeBuild">
<XmlPeek XmlInputPath="$(MSBuildProjectFile)" Namespaces="<Namespace Prefix='msb' Uri='schemas.microsoft.com/developer/msbuild/2003'/>"; Query="/msb:Project/msb:ItemGroup/*[not(self::msb:EmbeddedResource)]/@Include">
<Output TaskParameter="Result" ItemName="AllItems" />
</XmlPeek>
<!-- MsBuild uses XPath 1.0 which doesn't have the 'ends-with' or 'matches' function. -->
<ItemGroup>
<Filtered Include="@(AllItems)" Condition="'%(Extension)' == '.cshtml'" />
</ItemGroup>
<Warning
Code="CSHTML"
File="$(MSBuildProjectDirectory)\%(Filtered.Identity)"
Text="View is not set to [BuildAction:Content]"
Condition="'@(Filtered)'!=''"
/>
</Target>
Instead of <Warning ...>
you can also use <Error ...>
You'll need to manually put one of these snippets in your project file:
Thank you jessehouwing for geting me started! I liked your solution and voted for it. I had a little trouble with it and ended up with the following:
<Target Name="EnsureContentOnViews" BeforeTargets="BeforeBuild">
<ItemGroup>
<Filtered Include="@(None)" Condition="'%(Extension)' == '.cshtml'" />
</ItemGroup>
<Error Condition="'@(Filtered)'!=''" Code="CSHTML" File="%(Filtered.Filename)" Text="Not set to [BuildAction:Content]: Identity: %(Filtered.Identity)" />
</Target>
Right before my </Project>
tag in the csproj file.
Well, @jessehouwing 's certainly worked for me! So that fix stays nicely in my .csproj
file :).... Only, I just double-checked and it looks as if that nice snippet disappeared somewhere in the last month due to a merge of another developer or something.. (true story)
NPM alternative
Anyway, I would like to mention the NPM module check-vs-includes I created for this. It is especially convenient for dev's that have Node installed (and if you don't then you probably should :) ).
I run the check manually before each deploy, instead of on every build. But it is less likely to disappear (unnoticed) from your .csproj file due to a merge.
The main advantage however of using this NPM package, is that besides checking for Build Action NONE it also checks for files that are NOT included in your project at all. Because that can also lead to (subtle but nasty) bugs when you use Visual Studio's Web Deploy. And missing includes are actually pretty likely if:
1) you have 1 non VS developers in your team, or
2) due to merges of the .csproj file (which often has merge issues).
Check the NPM page for instructions. But below a simple example, which assumes you use Node and Gulp:
Simple example
Install the Node module
npm i check-vs-includes
Add a task to your gulpfile.js
:
var checkVSIncludes = require('check-vs-includes');
...
gulp.task('checkVSIncludes', function() {
checkVSIncludes(['/Views/**/*.cshtml', '/app/**/*.js']);
});
- Run the check in your project folder (e.g. where your
.csproj
file is)
gulp checkVSIncludes
I suggest to implement a custom build task. Call it on prebuild in your msbuild/tfs script.
Custom build task should just check .scproj file if there is an include file of cshtml with build action none. Of so just exit returning a non zero int value.