Make sure all *.cshtml files are set to be "Content" for Build Action
Asked Answered
I

4

38

A few times when I copy-paste *.cshtml files, Visual Studio for some reason sets Build Action on these files to be "None": Comments.cshtml is set to be "None" on Build Action

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?

Interflow answered 14/1, 2015 at 23:17 Comment(0)
L
37

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="&lt;Namespace Prefix='msb' Uri='schemas.microsoft.com/developer/msbuild/2003'/&gt;"; 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:

enter image description here

Livraison answered 15/1, 2015 at 18:51 Comment(7)
That worked, thanks for the push in the right direction. Only I had to add Condition="'@(Filtered)'!=''" to <Warning - warning was issued all the time, even with no incorrect filesInterflow
Great answer! I was having the same problem as trailmax. Feedback: the second option (the one with XmlPeek) didn't work for me (doesn't show the warning) but the first one worked perfectly. Thanks!Flyn
The first option worked for me, but the second one did not. I played with the Condition a little, but couldn't get it to work.Xanthus
I really wanted to use the XmlPeek approach as it's much neater, but as others have said, encountered the same problem, no elements were being matched. Found out it was due to the namespace. <XmlPeek XmlInputPath="$(MSBuildProjectFile)" Namespaces="&lt;Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/&gt;" Query="/msb:Project/msb:ItemGroup/*[not(self::EmbeddedResource)]/@Include">Hellebore
Typo in the above code, there needs to be a namespace on the 'EmbeddedResource' also: XmlInputPath="$(MSBuildProjectFile)" Namespaces="&lt;Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/&gt;" Query="/msb:Project/msb:ItemGroup/*[not(self::msb:EmbeddedResource)]/@Include">Hellebore
Great tip! It'll be awesome to have some addon to append this to the project.Detour
Wonderful snippet. Thanks for sharing this golden MSBuild nugget!Supination
R
6

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.

Reel answered 1/3, 2016 at 17:21 Comment(0)
M
3

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

  1. Install the Node module

    npm i check-vs-includes

  2. Add a task to your gulpfile.js:

     var checkVSIncludes = require('check-vs-includes');

     ...

     gulp.task('checkVSIncludes', function() {
        checkVSIncludes(['/Views/**/*.cshtml', '/app/**/*.js']);
     });
  1. Run the check in your project folder (e.g. where your .csproj file is)

    gulp checkVSIncludes

Motte answered 14/7, 2015 at 18:47 Comment(2)
I use Visual Studio's Task Runner Explorer extension to run the check instead of the command in point 3. If you add --save-dev after the install of point 1, then your co-workers can also profit from this automated check. (They have to run npm install once, to get this dev-dependency). And for the readers still on an Express edition of Visual Studio (which doesn't allow installing extensions), upgrade now to Visual Studio 2013 Community Edition (which does and is also free :) ).Motte
upvote for a good write-up for other people. But I'm not in use of node or gulp, so pretty useless for me.Interflow
I
0

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.

Immixture answered 15/1, 2015 at 17:49 Comment(1)
So how exactly is this build action implemented?Interflow

© 2022 - 2024 — McMap. All rights reserved.