Distribuzione a file singolo

La creazione di bundle di tutti i file dipendenti dall'applicazione in un singolo file binario offre agli sviluppatori di applicazioni l'opzione interessante di implementare e distribuire l'applicazione come file singolo. La distribuzione a file singolo è disponibile sia per il modello di distribuzione dipendente dal framework che per le applicazioni autonome.

Le dimensioni del file singolo in un'applicazione autonoma sono elevate perché includono il runtime e le librerie del framework. In .NET 6 è possibile pubblicare file trimmed per ridurre le dimensioni totali delle applicazioni compatibili con trim. L'opzione di distribuzione di un file singolo può essere combinata con le opzioni di pubblicazione ReadyToRun e Trim.

Importante

Per eseguire una singola app file in Windows 7, è necessario usare .NET Runtime 6.0.3 o versione successiva.

File di progetto di esempio

Ecco un file di progetto di esempio che specifica la pubblicazione di un file singolo:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <PublishSingleFile>true</PublishSingleFile>
    <SelfContained>true</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

</Project>

Queste proprietà hanno le funzioni seguenti:

  • PublishSingleFile. Abilita la pubblicazione a file singolo. Abilita inoltre gli avvisi di un file singolo durante dotnet build.
  • SelfContained. Determina se l'applicazione è indipendente o dipendente dal framework.
  • RuntimeIdentifier. Specifica il sistema operativo e il tipo di CPU di destinazione. Imposta anche <SelfContained>true</SelfContained> come impostazione predefinita.

Le applicazioni a file singolo sono sempre specifiche del sistema operativo e dell'architettura. È necessario pubblicare per ogni configurazione, ad esempio Linux x64, Linux Arm64, Windows x64 e così via.

I file di configurazione di esecuzione, ad esempio *.runtimeconfig.json e *.deps.json, sono inclusi nel file singolo. Se è necessario un file di configurazione aggiuntivo, è possibile inserirlo accanto al file singolo.

Pubblicare un'applicazione a file singolo

Pubblicare una applicazione a file singolo usando il comando dotnet publish.

  1. Aggiungere <PublishSingleFile>true</PublishSingleFile> al file di progetto.

    Questa modifica produce una applicazione a file singolo nella pubblicazione autonoma. Vengono inoltre visualizzati avvisi di compatibilità dei file singoli durante la compilazione.

    <PropertyGroup>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>
    
  2. Pubblicare l'app per un identificatore di runtime specifico usando dotnet publish -r <RID>

    L'esempio seguente pubblica l'applicazione per Windows come applicazione a file singolo indipendente.

    dotnet publish -r win-x64

    L'esempio seguente pubblica l'applicazione per Linux come applicazione a file singolo dipendente dal framework.

    dotnet publish -r linux-x64 --self-contained false

<PublishSingleFile> deve essere impostato nel file di progetto per abilitare l'analisi dei file durante la compilazione, ma è anche possibile trasmettere queste opzioni come argomenti dotnet publish:

dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained false

Per altre informazioni, vedere Pubblicare applicazioni .NET Core con l'interfaccia della riga di comando di .NET.

Escludere i file dall'incorporamento

Alcuni file possono essere esclusi in modo esplicito dall'incorporazione nel file singolo impostando i metadati seguenti:

<ExcludeFromSingleFile>true</ExcludeFromSingleFile>

Ad esempio, per inserire alcuni file nella directory di pubblicazione, ma non aggregarli nel file:

<ItemGroup>
  <Content Update="Plugin.dll">
    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
  </Content>
</ItemGroup>

Includere file PDB all'interno del bundle

Il file PDB per un assembly può essere incorporato nell'assembly stesso (il .dll) usando l'impostazione seguente. Poiché i simboli sono parte dell'assembly, essi fanno parte anche dell'applicazione:

<DebugType>embedded</DebugType>

Ad esempio, aggiungere la proprietà seguente al file di progetto di un assembly per incorporare il file PDB in tale assembly:

<PropertyGroup>
  <DebugType>embedded</DebugType>
</PropertyGroup>

Altre considerazioni

Le applicazioni a file singolo hanno tutti i file PDB correlati insieme all'applicazione, non aggregati per impostazione predefinita. Se si desidera includere i PDB all'interno dell'assembly per i progetti compilati, impostare DebugType su embedded. Vedere Includere file PDB all'interno del pacchetto.

I componenti C++ gestiti non sono adatti per la distribuzione di file singoli. È consigliabile scrivere applicazioni in C# o in un altro linguaggio C++ non gestito perché siano compatibili con un file singolo,

Librerie native

Solo le DLL gestite vengono aggregate con l'applicazione in un file singolo eseguibile. All'avvio dell'applicazione, le DLL gestite vengono estratte e caricate in memoria, evitando l'estrazione in una cartella. Con questo approccio, i file binari gestiti sono incorporati nel bundle di file singolo, ma i file binari nativi del runtime principale sono file separati.

Per incorporare tali file per l'estrazione e ottenere un file di output, impostare la proprietà IncludeNativeLibrariesForSelfExtract su true.

Specificare che IncludeAllContentForSelfExtract estrae tutti i file, inclusi gli assembly gestiti, prima di eseguire il file eseguibile. Ciò può essere utile per rari problemi di compatibilità delle applicazioni.

Importante

Se viene usata l'estrazione, i file vengono estratti su disco prima dell'avvio dell'applicazione:

  • Se la variabile di ambiente DOTNET_BUNDLE_EXTRACT_BASE_DIR è impostata su un percorso, i file vengono estratti in una directory in tale percorso.
  • In caso contrario, se in esecuzione in Linux o macOS, i file vengono estratti in una directory in $HOME/.net.
  • Se in esecuzione in Windows, i file vengono estratti in una directory in %TEMP%/.net.

Per evitare manomissioni, tali directory non devono essere scrivibili da utenti o servizi con privilegi diversi. Non usare /tmp o /var/tmp nella maggior parte dei sistemi Linux e macOS.

Nota

In alcuni ambienti Linux, ad esempio in systemd, l'estrazione predefinita non funziona perché $HOME non è definita. In questi casi, è consigliabile impostare $DOTNET_BUNDLE_EXTRACT_BASE_DIR in modo esplicito.

Per systemd, una buona alternativa consiste nel definire DOTNET_BUNDLE_EXTRACT_BASE_DIR nel proprio file di unità del servizio come %h/.net, che systemd si espande correttamente in $HOME/.net per l'account che esegue il servizio.

[Service]
Environment="DOTNET_BUNDLE_EXTRACT_BASE_DIR=%h/.net"

Incompatibilità dell'API

Alcune API non sono compatibili con la distribuzione di file singoli. Le applicazioni potrebbero richiedere modifiche se utilizzano queste API. Se si usa un framework o un pacchetto di terze parti, è possibile che venga utilizzata una di queste API e richiedano modifiche. La causa più comune dei problemi è la dipendenza dai percorsi dei file per file o DLL fornite con l'applicazione.

La tabella seguente contiene i dettagli dell'API della libreria di runtime pertinente per l'uso di un file singolo.

API Nota
Assembly.CodeBase Genera l'eccezione PlatformNotSupportedException.
Assembly.EscapedCodeBase Genera l'eccezione PlatformNotSupportedException.
Assembly.GetFile Genera l'eccezione IOException.
Assembly.GetFiles Genera l'eccezione IOException.
Assembly.Location Restituisce una stringa vuota.
AssemblyName.CodeBase Restituisce null.
AssemblyName.EscapedCodeBase Restituisce null.
Module.FullyQualifiedName Restituisce una stringa con il valore <Unknown> o genera un'eccezione.
Marshal.GetHINSTANCE Restituisce -1.
Module.Name Restituisce una stringa con il valore <Unknown>.

Sono disponibili alcuni consigli per la correzione di scenari comuni:

Post-elaborazione di file binari prima della creazione di bundle

Alcuni flussi di lavoro richiedono la post-elaborazione dei file binari prima della creazione di bundle. Un esempio comune è la firma. La dotnet SDK fornisce punti di estensione MSBuild per consentire l'elaborazione di file binari appena prima della creazione di bundle a file singolo. Le API disponibili sono:

  • La destinazione PrepareForBundle che verrà chiamata prima GenerateSingleFileBundle
  • Un oggetto <ItemGroup><FilesToBundle /></ItemGroup> contenente tutti i file che verranno raggruppati
  • Una proprietà AppHostFile che specifica il modello apphost. La post-elaborazione potrebbe voler escludere l'apphost dall'elaborazione.

Per eseguire questa operazione, è necessario creare una destinazione che verrà eseguita tra PrepareForBundle e GenerateSingleFileBundle.

Si consideri l'esempio di nodo del progetto .NET seguente Target:

<Target Name="MySignedBundledFile" BeforeTargets="GenerateSingleFileBundle" DependsOnTargets="PrepareForBundle">

È possibile che gli strumenti debbano copiare i file nel processo di firma. Ciò può verificarsi se il file originale è un elemento condiviso non di proprietà della compilazione, ad esempio il file proviene da una cache NuGet. In questo caso, è previsto che lo strumento modifichi il percorso dell'elemento corrispondente FilesToBundle in modo che punti alla copia modificata.

Comprimere gli assembly nelle applicazioni a file singolo

Le applicazioni a file singolo possono essere create con la compressione abilitata negli assembly incorporati. Impostare la proprietà EnableCompressionInSingleFile su true. Il file singolo prodotto avrà tutti gli assembly incorporati compressi, cosa che può ridurre significativamente le dimensioni del file eseguibile.

La compressione include un costo delle prestazioni. All'avvio dell'applicazione, gli assembly devono essere decompressi in memoria, operazione che richiede tempo. È consigliabile misurare sia la modifica delle dimensioni sia il costo di avvio per abilitare la compressione prima di usarla. L'impatto può variare in modo significativo tra applicazioni diverse.

Esaminare un'applicazione a file singolo

Le applicazioni a file singolo possono essere esaminate usando lo strumento ILSpy. Lo strumento può visualizzare tutti i file in bundle nell'applicazione e può esaminare il contenuto degli assembly gestiti.

Vedi anche