Reputation: 35106
A few times when I copy-paste *.cshtml
files, Visual Studio for some reason sets the 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.
Is there a way to automatically detect such occurrences and prevent?
Upvotes: 38
Views: 8630
Reputation: 5193
jessehouwing's answer worked for me.
NPM alternative:
I would like to mention the NPM module check-vs-includes I created for this. It is especially convenient for devs that have Node installed.
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:
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
Upvotes: 3
Reputation: 2634
I had a little trouble with jessehouwing's answer 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.
Upvotes: 6
Reputation: 114481
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:
Upvotes: 37
Reputation: 9
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.
Upvotes: 0