Distribuire app .NET Core con Visual Studio

È possibile distribuire un'app .NET Core come distribuzione dipendente dal framework, che include i file binari dell'applicazione ma dipende dalla presenza di .NET Core nel sistema di destinazione oppure come distribuzione autonoma, che include l'applicazione e i file binari di .NET Core. Per una panoramica della distribuzione di applicazioni .NET Core, vedere Distribuzione di applicazioni .NET Core.

Le sezioni seguenti illustrano l'uso di Microsoft Visual Studio per creare i tipi di distribuzione seguenti:

  • Distribuzione dipendente dal framework
  • Distribuzione dipendente dal framework con dipendenze di terze parti
  • Distribuzione autonoma
  • Distribuzione autonoma con dipendenze di terze parti

Per informazioni sull'uso di Visual Studio per sviluppare applicazioni .NET Core, vedere Dipendenze e requisiti di .NET Core.

Distribuzione dipendente dal framework

Una distribuzione dipendente dal framework senza dipendenze di terze parti richiede la compilazione, il testing e la pubblicazione dell'app. Il processo viene illustrato da un semplice esempio scritto in C#.

  1. Creare il progetto.

    Selezionare File>New (Nuovo) >Project (Progetto). Nella finestra di dialogo Nuovo progetto espandere le categorie di progetti del linguaggio (C# o Visual Basic) nel riquadro dei tipi di progetti Installati, scegliere il modello .NET Core e selezionare il modello Console App (.NET Core) (App console (.NET Core)) nel riquadro centrale. Immettere un nome di progetto, ad esempio "FDD", nella casella di testo Nome. Selezionare il pulsante OK.

  2. Aggiungere il codice sorgente dell'applicazione.

    Aprire il file Program.cs o Program.vb nell'editor e sostituire il codice generato automaticamente con il codice seguente. Questo codice richiede all'utente di immettere del testo e visualizza le singole parole immesse dall'utente. Usa l'espressione regolare \w+ per separare le parole nel testo di input.

    using System;
    using System.Text.RegularExpressions;
    
    namespace Applications.ConsoleApps
    {
        public class ConsoleParser
        {
            public static void Main()
            {
                Console.WriteLine("Enter any text, followed by <Enter>:\n");
                String? s = Console.ReadLine();
                ShowWords(s ?? "You didn't enter anything.");
                Console.Write("\nPress any key to continue... ");
                Console.ReadKey();
            }
    
            private static void ShowWords(String s)
            {
                String pattern = @"\w+";
                var matches = Regex.Matches(s, pattern);
                if (matches.Count == 0)
                {
                    Console.WriteLine("\nNo words were identified in your input.");
                }
                else
                {
                    Console.WriteLine($"\nThere are {matches.Count} words in your string:");
                    for (int ctr = 0; ctr < matches.Count; ctr++)
                    {
                        Console.WriteLine($"   #{ctr,2}: '{matches[ctr].Value}' at position {matches[ctr].Index}");
                    }
                }
            }
        }
    }
    
    Imports System.Text.RegularExpressions
    
    Namespace Applications.ConsoleApps
        Public Module ConsoleParser
            Public Sub Main()
                Console.WriteLine("Enter any text, followed by <Enter>:")
                Console.WriteLine()
                Dim s = Console.ReadLine()
                ShowWords(s)
                Console.Write($"{vbCrLf}Press any key to continue... ")
                Console.ReadKey()
            End Sub
    
            Private Sub ShowWords(s As String)
                Dim pattern = "\w+"
                Dim matches = Regex.Matches(s, pattern)
                Console.WriteLine()   
                If matches.Count = 0 Then
                    Console.WriteLine("No words were identified in your input.")
                Else
                    Console.WriteLine($"There are {matches.Count} words in your string:")
                    For ctr = 0 To matches.Count - 1
                        Console.WriteLine($"   #{ctr,2}: '{matches(ctr).Value}' at position {matches(ctr).Index}")
                    Next
                End If
                Console.WriteLine()
            End Sub
        End Module
    End Namespace
    
    
  3. Creare una build di debug dell'app.

    Selezionare Compila>Compila soluzione. È anche possibile compilare ed eseguire la build di debug dell'applicazione selezionando Esegui debug>Avvia debug.

  4. Distribuire l'app.

    Dopo aver eseguito il debug e il test del programma, creare i file da distribuire con l'app. Per la pubblicazione da Visual Studio eseguire le operazioni seguenti:

    1. Modificare la configurazione della soluzione da Debug a Release sulla barra degli strumenti, per creare una versione di rilascio dell'app (invece di una versione di debug).

    2. Fare clic con il pulsante destro del mouse sul progetto (non sulla soluzione) in Esplora soluzioni e selezionare Pubblica.

    3. Nella scheda Pubblica selezionare Pubblica. I file che costituiscono l'applicazione vengono salvati nel file system locale.

    4. La scheda Pubblica ora visualizza un unico profilo FolderProfile. Le impostazioni di configurazione del profilo vengono visualizzate nella sezione Riepilogo della scheda.

    I file risultanti vengono inseriti in una directory di nome Publish in Windows e publish nei sistemi Unix, che è una sottodirectory della directory .\bin\release\netcoreapp2.1 del progetto.

Insieme ai file dell'applicazione, il processo di pubblicazione genera un file del database di programma (con estensione pdb) che contiene le informazioni di debug relative all'app. Il file è utile soprattutto per il debug delle eccezioni. È possibile scegliere di non includerlo nel pacchetto dei file dell'applicazione. È tuttavia consigliabile salvarlo perché può risultare utile per il debug della build di rilascio dell'app.

Distribuire il set completo dei file dell'applicazione con il metodo desiderato. È ad esempio possibile inserire i file in un file zip, usare un semplice comando copy o distribuire i file con un pacchetto di installazione a scelta. Dopo aver completato l'installazione gli utenti possono eseguire l'applicazione usando il comando dotnet e fornendo il nome file dell'applicazione, ad esempio dotnet fdd.dll.

Oltre ai file binari dell'applicazione, il programma di installazione deve aggregare il programma di installazione framework condiviso oppure rilevarlo come prerequisito durante l'installazione dell'applicazione. L'installazione del framework condiviso richiede l'accesso amministratore o alla radice perché è a livello di computer.

Distribuzione dipendente dal framework con dipendenze di terze parti

In una distribuzione dipendente dal framework con una o più dipendenze di terze parti, le dipendenze devono essere disponibili per il progetto. Prima della compilazione dell'app è necessario eseguire i passaggi aggiuntivi:

  1. Usare Gestione pacchetti NuGet per aggiungere al progetto un riferimento a un pacchetto NuGet e per installare il pacchetto se non è già disponibile nel sistema. Per aprire lo strumento per la gestione dei pacchetti, selezionare Strumenti>Gestione pacchetti NuGet>Gestisci pacchetti NuGet per la soluzione.

  2. Verificare che le dipendenze di terze parti (ad esempio, Newtonsoft.Json) siano installate nel sistema e, se non lo sono, installarle. La scheda Installati elenca i pacchetti NuGet installati nel sistema. Se Newtonsoft.Json non è presente nell'elenco, selezionare la scheda Sfoglia e immettere "Newtonsoft.Json" nella casella di ricerca. Selezionare Newtonsoft.Json, selezionare il progetto nel riquadro destro e quindi selezionare Installa.

  3. Se Newtonsoft.Json è già installato nel sistema aggiungerlo al progetto selezionando il progetto stesso nel riquadro destro della scheda Gestisci i pacchetti per la soluzione.

La portabilità di una distribuzione dipendente dal framework con dipendenze di terze parti corrisponde esattamente alla portabilità delle dipendenze. Se ad esempio una libreria di terze parti supporta solo macOS, l'app non è portabile in sistemi Windows. Questa situazione si verifica se la dipendenza di terze parti stessa dipende da codice nativo. Un buon esempio è il server Kestrel, che richiede una dipendenza nativa da libuv. Quando viene creata una distribuzione dipendente dal framework per un'applicazione con questo tipo di dipendenze di terze parti, l'output pubblicato contiene una cartella per ogni identificatore di runtime (RID) supportato dalla dipendenza nativa (e presente nel relativo pacchetto NuGet).

Distribuzione autonoma senza dipendenze di terze parti

La pubblicazione di una distribuzione autonoma senza dipendenze di terze parti comporta la creazione del progetto, la modifica del file csproj, la compilazione, il test e la pubblicazione dell'app. Il processo viene illustrato da un semplice esempio scritto in C#. Creare, codificare e testare inizialmente il progetto come nel caso di una distribuzione dipendente dal framework:

  1. Creare il progetto.

    Selezionare File>New (Nuovo) >Project (Progetto). Nella finestra di dialogo Nuovo progetto espandere le categorie di progetti del linguaggio (C# o Visual Basic) nel riquadro dei tipi di progetti Installati, scegliere il modello .NET Core e selezionare il modello Console App (.NET Core) (App console (.NET Core)) nel riquadro centrale. Immettere un nome di progetto, ad esempio "SCD" nella casella di testo Nome, quindi selezionare il pulsante OK.

  2. Aggiungere il codice sorgente dell'applicazione.

    Aprire il file Program.cs o Program.vb nell'editor e sostituire il codice generato automaticamente con il codice seguente. Questo codice richiede all'utente di immettere del testo e visualizza le singole parole immesse dall'utente. Usa l'espressione regolare \w+ per separare le parole nel testo di input.

    using System;
    using System.Text.RegularExpressions;
    
    namespace Applications.ConsoleApps
    {
        public class ConsoleParser
        {
            public static void Main()
            {
                Console.WriteLine("Enter any text, followed by <Enter>:\n");
                String? s = Console.ReadLine();
                ShowWords(s ?? "You didn't enter anything.");
                Console.Write("\nPress any key to continue... ");
                Console.ReadKey();
            }
    
            private static void ShowWords(String s)
            {
                String pattern = @"\w+";
                var matches = Regex.Matches(s, pattern);
                if (matches.Count == 0)
                {
                    Console.WriteLine("\nNo words were identified in your input.");
                }
                else
                {
                    Console.WriteLine($"\nThere are {matches.Count} words in your string:");
                    for (int ctr = 0; ctr < matches.Count; ctr++)
                    {
                        Console.WriteLine($"   #{ctr,2}: '{matches[ctr].Value}' at position {matches[ctr].Index}");
                    }
                }
            }
        }
    }
    
    Imports System.Text.RegularExpressions
    
    Namespace Applications.ConsoleApps
        Public Module ConsoleParser
            Public Sub Main()
                Console.WriteLine("Enter any text, followed by <Enter>:")
                Console.WriteLine()
                Dim s = Console.ReadLine()
                ShowWords(s)
                Console.Write($"{vbCrLf}Press any key to continue... ")
                Console.ReadKey()
            End Sub
    
            Private Sub ShowWords(s As String)
                Dim pattern = "\w+"
                Dim matches = Regex.Matches(s, pattern)
                Console.WriteLine()   
                If matches.Count = 0 Then
                    Console.WriteLine("No words were identified in your input.")
                Else
                    Console.WriteLine($"There are {matches.Count} words in your string:")
                    For ctr = 0 To matches.Count - 1
                        Console.WriteLine($"   #{ctr,2}: '{matches(ctr).Value}' at position {matches(ctr).Index}")
                    Next
                End If
                Console.WriteLine()
            End Sub
        End Module
    End Namespace
    
    
  3. Specificare se si intende usare la modalità invariante della globalizzazione.

    In particolare, se l'app è destinata a Linux, è possibile ridurre le dimensioni totali della distribuzione sfruttando la modalità invariante della globalizzazione. La modalità invariante della globalizzazione è utile per le applicazioni che non sono compatibili a livello globale e possono usare le convenzioni di formattazione, le convenzioni sulla combinazione di maiuscole e minuscole, il confronto tra stringhe e l'ordinamento delle impostazioni cultura invarianti.

    Per abilitare la modalità invariante fare clic con il pulsante destro del mouse sul progetto (non sulla soluzione) in Esplora soluzioni e selezionare Modifica SCD.csproj o Modifica SCD.vbproj. Aggiungere al file le seguenti righe evidenziate:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
        <RuntimeHostConfigurationOption Include="System.Globalization.Invariant" Value="true" />
      </ItemGroup> 
    
    </Project>
    
  4. Creare una build di debug dell'applicazione.

    Selezionare Compila>Compila soluzione. È anche possibile compilare ed eseguire la build di debug dell'applicazione selezionando Esegui debug>Avvia debug. Questo passaggio di debug consente di identificare i problemi dell'applicazione quando è in esecuzione sulla piattaforma host. È tuttavia necessario testarla sulle singole piattaforme di destinazione.

    Se è stata abilitata la modalità invariante della globalizzazione, verificare soprattutto se l'assenza di dati dipendenti dalle impostazioni cultura sia adatta per l'applicazione.

Dopo aver terminato il debug, è possibile pubblicare la distribuzione autonoma:

Dopo aver eseguito il debug e il test del programma creare i file da distribuire con l'app per ogni piattaforma di destinazione.

Per pubblicare l'app da Visual Studio eseguire le operazioni seguenti:

  1. Definire le piattaforme di destinazione per l'app.

    1. Fare clic con il pulsante destro del mouse sul progetto (non sulla soluzione) in Esplora soluzioni e selezionare Modifica SCD.csproj.

    2. Creare un tag <RuntimeIdentifiers> nella sezione <PropertyGroup> del file csproj che definisce le piattaforme di destinazione dell'app e specifica l'identificatore di runtime di ogni piattaforma di destinazione. È inoltre necessario aggiungere un punto e virgola per separare i RID. Per un elenco degli identificatori di runtime, vedere Runtime identifier catalog (Catalogo degli identificatori di runtime).

    Ad esempio, l'esempio seguente indica che l'app viene eseguita in sistemi operativi Windows a 64 bit e nel sistema operativo OS X a 64 bit.

    <PropertyGroup>
       <RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
    </PropertyGroup>
    

    L'elemento <RuntimeIdentifiers> può essere inserito in qualsiasi elemento <PropertyGroup> presente nel file csproj. Un file di esempio csproj completo è disponibile più avanti in questa sezione.

  2. Pubblicare l'app.

    Dopo aver eseguito il debug e il test del programma creare i file da distribuire con l'app per ogni piattaforma di destinazione.

    Per pubblicare l'app da Visual Studio eseguire le operazioni seguenti:

    1. Modificare la configurazione della soluzione da Debug a Release sulla barra degli strumenti, per creare una versione di rilascio dell'app (invece di una versione di debug).

    2. Fare clic con il pulsante destro del mouse sul progetto (non sulla soluzione) in Esplora soluzioni e selezionare Pubblica.

    3. Nella scheda Pubblica selezionare Pubblica. I file che costituiscono l'applicazione vengono salvati nel file system locale.

    4. La scheda Pubblica ora visualizza un unico profilo FolderProfile. Le impostazioni di configurazione del profilo vengono visualizzate nella sezione Riepilogo della scheda. Runtime di destinazione identifica il runtime pubblicato e Percorso di destinazione identifica il percorso in cui sono stati salvati i file per la distribuzione autonoma.

    5. Per impostazione predefinita tutti i file pubblicati vengono salvati in una singola directory. Per praticità è consigliabile creare un profilo separato per ogni runtime di destinazione e inserire i file pubblicati in una directory specifica per la piattaforma. Questo richiede la creazione di un profilo di pubblicazione separato per ogni piattaforma di destinazione. A questo punto ricompilare l'applicazione per ogni piattaforma procedendo nel modo seguente:

      1. Selezionare Crea nuovo profilo nella finestra di dialogo Pubblica.

      2. Nella finestra di dialogo Selezionare una destinazione di pubblicazione modificare il percorso Scegliere una cartella in bin\Release\PublishOutput\win-x64. Seleziona OK.

      3. Selezionare il nuovo profilo (FolderProfile1) nell'elenco dei profili e assicurarsi che Runtime di destinazione sia win-x64. In caso contrario, selezionare Impostazioni. Nella finestra di dialogo Impostazioni profilo impostare Runtime di destinazione su win-x64 e selezionare Salva. In caso contrario, selezionare Annulla.

      4. Selezionare Pubblica per pubblicare l'app per le piattaforme Windows 10 a 64 bit.

      5. Eseguire nuovamente la procedura per creare un profilo per la piattaforma osx-x64. Il percorso di destinazione è bin\Release\PublishOutput\osx-x64 e il runtime di destinazione è osx-x64. Il nome assegnato a questo profilo in Visual Studio è FolderProfile2.

    Ogni percorso di destinazione contiene il set completo di file (i file dell'app e tutti i file di .NET Core) necessari per l'avvio dell'app.

Insieme ai file dell'applicazione, il processo di pubblicazione genera un file del database di programma (con estensione pdb) che contiene le informazioni di debug relative all'app. Il file è utile soprattutto per il debug delle eccezioni. È possibile scegliere di non includerlo nel pacchetto dei file dell'applicazione. È tuttavia consigliabile salvarlo perché può risultare utile per il debug della build di rilascio dell'app.

Distribuire i file pubblicati con il metodo desiderato. È ad esempio possibile inserire i file in un file zip, usare un semplice comando copy o distribuire i file con un pacchetto di installazione a scelta.

Di seguito è riportato il file csproj completo per questo progetto.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
  </PropertyGroup>
</Project>

Distribuzione autonoma con dipendenze di terze parti

Una distribuzione autonoma con una o più dipendenze di terze parti comporta l'aggiunta delle dipendenze. Prima della compilazione dell'app è necessario eseguire i passaggi aggiuntivi:

  1. Usare Gestione pacchetti NuGet per aggiungere al progetto un riferimento a un pacchetto NuGet e per installare il pacchetto se non è già disponibile nel sistema. Per aprire lo strumento per la gestione dei pacchetti, selezionare Strumenti>Gestione pacchetti NuGet>Gestisci pacchetti NuGet per la soluzione.

  2. Verificare che le dipendenze di terze parti (ad esempio, Newtonsoft.Json) siano installate nel sistema e, se non lo sono, installarle. La scheda Installati elenca i pacchetti NuGet installati nel sistema. Se Newtonsoft.Json non è presente nell'elenco, selezionare la scheda Sfoglia e immettere "Newtonsoft.Json" nella casella di ricerca. Selezionare Newtonsoft.Json, selezionare il progetto nel riquadro destro e quindi selezionare Installa.

  3. Se Newtonsoft.Json è già installato nel sistema aggiungerlo al progetto selezionando il progetto stesso nel riquadro destro della scheda Gestisci i pacchetti per la soluzione.

Di seguito è riportato il file csproj completo per questo progetto:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
  </ItemGroup>
</Project>

Quando si distribuisce l'applicazione, anche le dipendenze di terze parti usate nell'app sono contenute nei file dell'applicazione. Non è necessario che le librerie di terze parti siano già presenti nel sistema in cui viene eseguita l'app.

È possibile distribuire una distribuzione autonoma solo con una libreria di terze parti alle piattaforme supportate da tale libreria. Il caso è simile alla presenza di dipendenze di terze parti con dipendenze native in una distribuzione dipendente dal framework: le dipendenze native esistono nella piattaforma di destinazione solo se sono state installate in precedenza in tale piattaforma.

Vedi anche