Sviluppare app ASP.NET Core usando un watcher per file

Di Rick Anderson e Victor Hurdugaci

dotnet watch è uno strumento che esegue un comando dell'interfaccia della riga di comando di .NET quando i file di origine cambiano. Ad esempio, una modifica di file può attivare la compilazione, l'esecuzione di test o la distribuzione.

Questa esercitazione usa un'API Web esistente con due endpoint, di cui uno restituisce una somma e l'altro un prodotto. Il metodo del prodotto ha un bug, che verrà corretto in questa esercitazione.

Scaricare l'app di esempio. Questa è costituita da due progetti: WebApp (un'API Web di ASP.NET Core) e WebAppTests (unit test per l'API Web).

In una shell dei comandi passare alla cartella WebApp. Eseguire il comando riportato di seguito:

dotnet run

Nota

È possibile usare dotnet run --project <PROJECT> per specificare un progetto da eseguire. Ad esempio, l'esecuzione di dotnet run --project WebApp dalla radice dell'app di esempio consentirà di eseguire anche il progetto WebApp.

L'output della console visualizza messaggi simili al seguente che indicano che l'app è in esecuzione e in attesa di richieste:

$ dotnet run
Hosting environment: Development
Content root path: C:/Docs/aspnetcore/tutorials/dotnet-watch/sample/WebApp
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

In un Web browser accedere a http://localhost:<port number>/api/math/sum?a=4&b=5. Dovrebbe essere visualizzato il risultato 9.

Passare all'API del prodotto (http://localhost:<port number>/api/math/product?a=4&b=5). Restituisce 9 e non 20 come previsto. Questo problema verrà corretto più avanti nell'esercitazione.

Aggiungere dotnet watch a un progetto

Lo strumento watcher per file dotnet watch è incluso nella versione 2.1.300 di .NET Core SDK. Se si usa una versione precedente di .NET Core SDK, i passaggi seguenti sono obbligatori.

  1. Aggiungere un Microsoft.DotNet.Watcher.Tools riferimento al pacchetto al .csproj file:

    <ItemGroup>
        <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
    </ItemGroup>
    
  2. Installare il pacchetto Microsoft.DotNet.Watcher.Tools eseguendo il comando seguente:

    dotnet restore
    

Eseguire i comandi dell'interfaccia della riga di comando di .NET usando dotnet watch

Qualsiasi comando dell'interfaccia della riga di comando di .NET può essere eseguito con dotnet watch. Ad esempio:

Comando Comando con watch
dotnet run dotnet watch run
dotnet run -f netcoreapp3.1 dotnet watch run -f netcoreapp3.1
dotnet run -f netcoreapp3.1 -- --arg1 dotnet watch run -f netcoreapp3.1 -- --arg1
dotnet test dotnet watch test

Eseguire dotnet watch run nella cartella WebApp. L'output della console indica che watch è stato avviato.

L'esecuzione dotnet watch run in un'app Web avvia un browser che passa all'URL dell'app una volta pronto. dotnet watch a tale scopo, leggere l'output della console dell'app e attendere il messaggio pronto visualizzato da WebHost.

dotnet watch aggiorna il browser quando rileva le modifiche apportate ai file visualizzati. A tale scopo, il comando watch inserisce un middleware nell'app che modifica le risposte HTML create dall'app. Il middleware aggiunge un blocco di script JavaScript alla pagina che consente dotnet watch di indicare al browser di eseguire l'aggiornamento. Attualmente, le modifiche a tutti i file visualizzati, incluso il contenuto statico, ad .html esempio e .css i file, causano la ricompilazione dell'app.

dotnet watch:

  • Controlla solo i file che influisce sulle compilazioni per impostazione predefinita.
  • Tutti i file di controllo aggiuntivi (tramite configurazione) continuano a verificarsi una compilazione.

Per altre informazioni sulla configurazione, vedere configurazione dotnet-watch in questo documento.

Nota

È possibile usare dotnet watch --project <PROJECT> per specificare un progetto da controllare. Ad esempio, l'esecuzione di dotnet watch --project WebApp run dalla radice dell'app di esempio consentirà di eseguire e controllare anche il progetto WebApp.

Apportare modifiche con dotnet watch

Assicurarsi che dotnet watch sia in esecuzione.

Correggere il bug nel Product metodo di MathController.cs in modo che restituisca il prodotto e non la somma:

public static int Product(int a, int b)
{
    return a * b;
}

Salvare il file. L'output della console indica che dotnet watch ha rilevato una modifica del file e ha riavviato l'app.

Verificare che http://localhost:<port number>/api/math/product?a=4&b=5 restituisca il risultato corretto.

Eseguire test con dotnet watch

  1. Modificare il Product metodo di MathController.cs tornare a restituire la somma. Salvare il file.

  2. In una shell dei comandi passare alla cartella WebAppTests.

  3. Eseguire dotnet restore.

  4. Eseguire dotnet watch test. L'output indica che un test non è stato superato e che il watcher è in attesa di modifiche ai file:

    Total tests: 2. Passed: 1. Failed: 1. Skipped: 0.
    Test Run Failed.
    
  5. Correggere il codice del metodo Product in modo che restituisca il prodotto. Salvare il file.

dotnet watch rileva la modifica ai file ed esegue di nuovo i test. L'output della console indica che i test sono stati superati.

Personalizzare l'elenco dei file da controllare

Per impostazione predefinita, dotnet-watch tiene traccia di tutti i file che soddisfano i criteri GLOB seguenti:

  • **/*.cs
  • *.csproj
  • **/*.resx
  • File di contenuto: wwwroot/**, **/*.config, **/*.json

È possibile aggiungere altri elementi all'elenco espressioni di controllo modificando il .csproj file. Gli elementi possono essere specificati singolarmente o tramite criteri GLOB.

<ItemGroup>
    <!-- extends watching group to include *.js files -->
    <Watch Include="**\*.js" Exclude="node_modules\**\*;**\*.js.map;obj\**\*;bin\**\*" />
</ItemGroup>

Esclusione di file dal controllo

È possibile configurare dotnet-watch in modo che ignori le impostazioni predefinite. Per ignorare file specifici, aggiungere l'attributo Watch="false" alla definizione di un elemento nel .csproj file:

<ItemGroup>
    <!-- exclude Generated.cs from dotnet-watch -->
    <Compile Include="Generated.cs" Watch="false" />

    <!-- exclude Strings.resx from dotnet-watch -->
    <EmbeddedResource Include="Strings.resx" Watch="false" />

    <!-- exclude changes in this referenced project -->
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" Watch="false" />
</ItemGroup>
<ItemGroup>
     <!-- Exclude all Content items from being watched. -->
    <Content Update="@(Content)" Watch="false" />
</ItemGroup>

Progetti di controllo personalizzati

dotnet-watch non è limitato a progetti C#. È possibile creare progetti di controllo personalizzati per gestire scenari diversi. Si consideri il layout di progetto seguente:

  • test/
    • UnitTests/UnitTests.csproj
    • IntegrationTests/IntegrationTests.csproj

Se l'obiettivo consiste nel controllare entrambi i progetti, creare un file di progetto personalizzato configurato per controllarli entrambi:

<Project>
    <ItemGroup>
        <TestProjects Include="**\*.csproj" />
        <Watch Include="**\*.cs" />
    </ItemGroup>

    <Target Name="Test">
        <MSBuild Targets="VSTest" Projects="@(TestProjects)" />
    </Target>

    <Import Project="$(MSBuildExtensionsPath)\Microsoft.Common.targets" />
</Project>

Per avviare il controllo dei file per entrambi i progetti, passare alla cartella test. Eseguire il comando seguente:

dotnet watch msbuild /t:Test

VSTest viene eseguito quando un qualsiasi file viene modificato in uno dei progetti di test.

Configurazione dotnet-watch

Alcune opzioni di configurazione possono essere passate tramite variabili di dotnet watch ambiente. Le variabili disponibili sono:

Impostazione Descrizione
DOTNET_USE_POLLING_FILE_WATCHER Se impostato su "1" o "true", dotnet watch usa un watcher del file di polling anziché corefx.FileSystemWatcher Usato quando si osservano i file nelle condivisioni di rete o nei volumi montati da Docker.
DOTNET_WATCH_SUPPRESS_MSBUILD_INCREMENTALISM Per impostazione predefinita, dotnet watch ottimizza la compilazione evitando determinate operazioni, ad esempio l'esecuzione del ripristino o la rivalutazione del set di file watched in ogni modifica del file. Se impostato su "1" o "true", queste ottimizzazioni sono disabilitate.
DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER dotnet watch run tenta di avviare browser per le app Web con launchBrowser configurate in launchSettings.json. Se impostato su "1" o "true", questo comportamento viene eliminato.
DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH dotnet watch run tenta di aggiornare i browser quando rileva le modifiche apportate ai file. Se impostato su "1" o "true", questo comportamento viene eliminato. Questo comportamento viene eliminato anche se DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER è impostato.

Aggiornamento del browser

dotnet watch inserisce uno script nell'app che consente di aggiornare il browser quando cambia il contenuto. In alcuni scenari, ad esempio quando l'app abilita la compressione delle risposte, dotnet watch potrebbe non essere in grado di inserire lo script. Per questi casi in fase di sviluppo, inserire manualmente lo script nell'app. Ad esempio, per configurare l'app Web per inserire manualmente lo script, aggiornare il file di layout in modo da includere _framework/aspnet-browser-refresh.js:

@* _Layout.cshtml *@
<environment names="Development">
    <script src="/_framework/aspnetcore-browser-refresh.js"></script>
</environment>

Caratteri non ASCII

Visual Studio 17.2 e versioni successive include .NET SDK 6.0.300 e versioni successive. Con .NET SDK e 6.0.300 versioni successive, dotnet-watch genera caratteri non ASCII nella console durante una sessione di ricaricamento rapido. In alcuni host della console, ad esempio il conhost di Windows, questi caratteri potrebbero apparire incomprensibili. Per evitare caratteri incomprensibili, considerare uno degli approcci seguenti:

  • Configurare la variabile di ambiente per eliminare l'emissione DOTNET_WATCH_SUPPRESS_EMOJIS=1 di questi valori.
  • Passare a un terminale diverso, ad esempio https://github.com/microsoft/terminal, che supporta il rendering di caratteri non ASCII.