Aspnetcore: dotnet watch doesn't react when changing .razor files in referenced projects

Created on 26 May 2020  ยท  4Comments  ยท  Source: dotnet/aspnetcore

dotnet watch is meant to identify files in referenced projects that should trigger a refresh. This does work correctly for .cs files, but not for .razor files.

Repro

Using .NET Core 3.1.300 (on Mac, but probably is the same on other platforms):

  • dotnet new blazorwasm --hosted -o MyApp
  • cd MyApp
  • dotnet watch -p Server --list

Expected result: Lists the files (including .razor files from the Client project)

Actual: Gets the paths wrong. Output is:

path/MyApp/Server/Pages/Error.cshtml
path/MyApp/Server/Pages/Shared/_Layout.cshtml
path/MyApp/Server/Controllers/WeatherForecastController.cs
path/MyApp/Server/Pages/Error.cshtml.cs
path/MyApp/Server/Program.cs
path/MyApp/Server/Startup.cs
path/MyApp/Server/MyApp.Server.csproj
path/MyApp/Server/App.razor
path/MyApp/Server/Pages/Counter.razor
path/MyApp/Server/Pages/FetchData.razor
path/MyApp/Server/Pages/Index.razor
path/MyApp/Server/Shared/MainLayout.razor
path/MyApp/Server/Shared/NavMenu.razor
path/MyApp/Server/Shared/SurveyPrompt.razor
path/MyApp/Server/_Imports.razor
path/MyApp/Client/Program.cs
path/MyApp/Client/MyApp.Client.csproj
path/MyApp/Shared/WeatherForecast.cs
path/MyApp/Shared/MyApp.Shared.csproj

Notice Server/Pages/Counter.razor? This should be Client/Pages/Counter.razor. I'm not sure how it's computing these incorrect paths to non-existent files.

Also if you try editing and saving one of the Client/**/*.razor files, dotnet watch won't respond.

Working area-blazor area-commandlinetools enhancement

Most helpful comment

Another workaround to ensure full paths are used for all Watch items:

  <Target Name="FixDotnetWatch" AfterTargets="_CoreCollectWatchItems">
    <ItemGroup>
      <_WatchRelativePath Include="@(Watch)" Condition="'%(Identity)' != '%(Watch.FullPath)'" />
      <Watch Remove="@(_WatchRelativePath)" />
      <Watch Include="%(_WatchRelativePath.FullPath)" />
    </ItemGroup>
  </Target>

All 4 comments

Is this related to or a replacement for Hot reload for Blazor (dotnet/aspnetcore #5456)?

I ask because I've referred doc readers to that issue for when they ask about hot reload.

@guardrex It's related to, but is not a replacement for, hot reloading. Hot reloading would be a much more sophisticated feature.

dotnet watch just watches for changes to files on disk and re-reruns dotnet build or dotnet run, without really speeding up the build process in any way, without refreshing the browser, and without preserving any state. Despite these limitations it's still really beneficial until a more sophisticated hot reload feature is implemented.

I'm seeing the same behavior with cshtml and a referenced MVC class library. I've posted a repo which reproduces the behavior here.

โฏ dotnet watch -p .\MainProject\ --list
C:\scratch\dotnetwatch-razorrefs\MainProject\Views\Home\Index.cshtml
C:\scratch\dotnetwatch-razorrefs\MainProject\Views\Home\Privacy.cshtml
C:\scratch\dotnetwatch-razorrefs\MainProject\Views\Shared\Error.cshtml
<snip>
C:\scratch\dotnetwatch-razorrefs\MainProject\Views\Home\_ReferencedPartialView.cshtml
C:\scratch\dotnetwatch-razorrefs\RazorClasslib\RazorClasslib.csproj

The issue seems to be that whatever is populating the razor and cshtml into the Watch item group is not using FullPath, so the relative path is being concatenated into the original project's path.

msbuild .\MainProject\MainProject.csproj -t:GenerateWatchList -p:CustomAfterMicrosoftCommonTargets="C:\dev\aspnetcore\src\Tools\dotnet-watch\src\assets\DotNetWatch.targets" -p:CustomAfterMicrosoftCommonCrossTargetingTargets=C:\dev\aspnetcore\src\Tools\dotnet-watch\src\assets\DotNetWatch.targets -p:_DotNetWatchListFile=c:\scratch\watchlist.txt -p:_DotNetWatchTraceOutput=true
Microsoft (R) Build Engine version 16.6.0+5ff7b0c9e for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

Build started 6/25/2020 5:04:33 PM.
Project "C:\scratch\dotnetwatch-razorrefs\MainProject\MainProject.csproj" on node 1 (GenerateWatchList target(s)).
_CoreCollectWatchItems:
  Collecting watch items from 'MainProject'
  Views\Home\Index.cshtml
  Views\Home\Privacy.cshtml
  Views\Shared\Error.cshtml
  Views\Shared\_Layout.cshtml
  Views\Shared\_ValidationScriptsPartial.cshtml
  Views\_ViewImports.cshtml
  Views\_ViewStart.cshtml
  C:\scratch\dotnetwatch-razorrefs\MainProject\Controllers\HomeController.cs
  C:\scratch\dotnetwatch-razorrefs\MainProject\Models\ErrorViewModel.cs
  C:\scratch\dotnetwatch-razorrefs\MainProject\Program.cs
  C:\scratch\dotnetwatch-razorrefs\MainProject\Startup.cs
  C:\scratch\dotnetwatch-razorrefs\MainProject\MainProject.csproj
Project "C:\scratch\dotnetwatch-razorrefs\MainProject\MainProject.csproj" (1) is building "C:\scratch\dotnetwatch-razor
refs\RazorClasslib\RazorClasslib.csproj" (2) on node 1 (_CollectWatchItems target(s)).
_CoreCollectWatchItems:
  Collecting watch items from 'RazorClasslib'
  Views\Home\_ReferencedPartialView.cshtml
  C:\scratch\dotnetwatch-razorrefs\RazorClasslib\RazorClasslib.csproj
Done Building Project "C:\scratch\dotnetwatch-razorrefs\RazorClasslib\RazorClasslib.csproj" (_CollectWatchItems target(
s)).

Done Building Project "C:\scratch\dotnetwatch-razorrefs\MainProject\MainProject.csproj" (GenerateWatchList target(s)).

Workaround is to add them to Watch yourself:

<Project>
  <!-- Workaround for dotnet watch with MVC and Referenced SDK Project -->
  <Target Name="RazorWatch" BeforeTargets="_CoreCollectWatchItems">
    <ItemGroup>
      <RazorWatch Include="**\*.cshtml" />
      <Watch Include="%(RazorWatch.FullPath)" />
    </ItemGroup>
  </Target>
</Project>

Another workaround to ensure full paths are used for all Watch items:

  <Target Name="FixDotnetWatch" AfterTargets="_CoreCollectWatchItems">
    <ItemGroup>
      <_WatchRelativePath Include="@(Watch)" Condition="'%(Identity)' != '%(Watch.FullPath)'" />
      <Watch Remove="@(_WatchRelativePath)" />
      <Watch Include="%(_WatchRelativePath.FullPath)" />
    </ItemGroup>
  </Target>
Was this page helpful?
0 / 5 - 0 ratings