Added a `dotnet-tools.json` file to define a .NET tool manifest, specifying the `dotnet-ef` tool with version `10.0.9`. Introduced a `publish.bat` script for publishing the `EnvelopeGenerator.Server` project as a self-contained application targeting `win-x64` and .NET 8. The script handles cleaning, publishing, output verification, and provides deployment instructions for IIS. Updated the `EnvelopeGenerator.sln` file to include a reference to the `publish.bat` script under the `EnvelopeGenerator.Server` project using a `SolutionItems` section.
EnvelopeGenerator.Server — Publish & Deployment Guide
Inhaltsverzeichnis
- Unterschied zu einer normalen ASP.NET Core API
- Warum Self-Contained Publish?
- Publish-Befehl (Terminal)
- IIS-Konfiguration
- Verzeichnisstruktur nach dem Publish
- Häufige Fehler
Unterschied zu einer normalen ASP.NET Core API
EnvelopeGenerator.Server ist keine gewöhnliche ASP.NET Core Web API. Es handelt sich um eine Blazor Auto (Server + WebAssembly Hybrid)-Anwendung.
| Merkmal | Normale ASP.NET Core API | EnvelopeGenerator.Server (Blazor Auto) |
|---|---|---|
| Projekttyp | Microsoft.NET.Sdk.Web |
Microsoft.NET.Sdk.Web + WASM Client |
| Frontend | Keins / Razor Pages | Blazor Server + Blazor WASM |
| WASM-Komponente | Nein | Ja (EnvelopeGenerator.Server.Client) |
| Framework-DLL-Bindung | Tolerant gegenüber Runtime-Versionen | Strikt: WASM erwartet exakte Assembly-Versionen |
| Publish ohne .NET auf Zielserver | Nicht nötig (FDD reicht meist) | Self-Contained Pflicht empfohlen |
| IIS Application Pool | .NET CLR v4.0 oder No Managed Code |
Zwingend: No Managed Code |
| Publish-Paketgröße | ~5–20 MB | ~500 MB (enthält .NET Runtime) |
web.config processPath |
dotnet + .dll |
.\EnvelopeGenerator.Server.exe |
Warum die WASM-Komponente den Unterschied macht
Die WASM-Seite der Anwendung (EnvelopeGenerator.Server.Client) bindet Assemblies wie
Microsoft.Extensions.DependencyInjection.Abstractions in einer fest definierten Version.
Bei einem Framework-Dependent Deployment werden diese Assemblies nicht mitgeliefert und
müssen auf dem Zielserver vorhanden sein — in der exakt passenden Version.
Fehlt die passende .NET-Runtime auf dem Zielserver, erscheint folgender Fehler beim Start:
Unhandled exception. System.IO.FileNotFoundException:
Could not load file or assembly
'Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0'
Warum Self-Contained Publish?
Beim Self-Contained Deployment werden alle benötigten .NET Runtime-DLLs in das Ausgabeverzeichnis kopiert. Die Anwendung ist damit vollständig unabhängig von der auf dem Zielserver installierten .NET-Version.
| Framework-Dependent | Self-Contained | |
|---|---|---|
| .NET auf Zielserver nötig | Ja | Nein |
| Paketgröße | ~20 MB | ~500 MB |
runtimeconfig.json |
frameworkVersion vorhanden |
includedFrameworks (Runtime eingebettet) |
| Fehleranfälligkeit auf Fremd-PC | Hoch | Minimal |
Publish-Befehl (Terminal)
Empfohlener Befehl (Self-Contained, win-x64)
dotnet publish EnvelopeGenerator.Server\EnvelopeGenerator.Server\EnvelopeGenerator.Server.csproj ^
-c Release ^
-f net8.0 ^
--self-contained true ^
--runtime win-x64 ^
-o .\publish-output
Dieser Befehl muss vom Solution-Root-Verzeichnis aus ausgefuehrt werden. Alternativ:
publish.batim selben Verzeichnis wie diese README ausfuehren.
Parameter-Erklaerung
| Parameter | Bedeutung |
|---|---|
-c Release |
Release-Konfiguration (optimiert, kein Debug-Code) |
-f net8.0 |
Ziel-Framework explizit angeben (Pflicht, da <TargetFrameworks> mehrere Werte haben kann) |
--self-contained true |
Alle .NET Runtime-DLLs ins Ausgabeverzeichnis kopieren |
--runtime win-x64 |
Zielplattform: Windows 64-Bit |
-o .\publish-output |
Ausgabeverzeichnis |
Doğrulama nach dem Publish
Nach erfolgreichem Publish folgende Dateien im Ausgabeverzeichnis prüfen:
# Diese Dateien MÜSSEN vorhanden sein (Self-Contained-Nachweis):
Test-Path ".\publish-output\hostfxr.dll" # .NET Host
Test-Path ".\publish-output\coreclr.dll" # .NET Core Runtime
Test-Path ".\publish-output\Microsoft.Extensions.DependencyInjection.Abstractions.dll"
Test-Path ".\publish-output\EnvelopeGenerator.Server.exe"
Test-Path ".\publish-output\web.config"
Alle Ergebnisse müssen True sein.
IIS-Konfiguration
WICHTIG: Diese Einstellungen unterscheiden sich von einer normalen ASP.NET Core API und sind zwingend erforderlich.
1. Application Pool — No Managed Code
ASP.NET Core (und damit auch Blazor) verwaltet seinen eigenen Runtime-Lifecycle. IIS darf keinen .NET CLR-Managed-Code-Kontext aktivieren.
Einstellung:
IIS Manager
→ Application Pools
→ [Pool-Name der Anwendung] → Basic Settings
→ .NET CLR Version: "No Managed Code" ← ZWINGEND
Fehler bei falscher Einstellung: HTTP 500.30 — ASP.NET Core app failed to start (sc-win32-status: 574 in IIS-Logs)
2. ASP.NET Core Module V2
Das IIS-Modul AspNetCoreModuleV2 muss installiert sein.
Es wird über das .NET Hosting Bundle mitgeliefert.
Prüfen:
IIS Manager → Modules → "AspNetCoreModuleV2" vorhanden?
Falls nicht installiert: .NET 8 Hosting Bundle herunterladen und nach der Installation IIS neu starten:
net stop was /y && net start w3svc
3. web.config — Korrekte Konfiguration für Self-Contained
Nach dem Publish wird web.config automatisch generiert. Für Self-Contained muss sie so aussehen:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\EnvelopeGenerator.Server.exe"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
Kritische Unterschiede zu Framework-Dependent:
| Eigenschaft | Framework-Dependent (FALSCH) | Self-Contained (RICHTIG) |
|---|---|---|
processPath |
dotnet |
.\EnvelopeGenerator.Server.exe |
arguments |
.\EnvelopeGenerator.Server.dll |
(leer oder weggelassen) |
4. Berechtigungen für das logs-Verzeichnis
Wenn stdoutLogEnabled="true" gesetzt wird (zur Fehlerdiagnose), muss das logs-Verzeichnis
existieren und der IIS-Prozess muss Schreibrechte haben:
New-Item -ItemType Directory -Path "C:\inetpub\wwwroot\<App-Pfad>\logs" -Force
icacls "C:\inetpub\wwwroot\<App-Pfad>\logs" /grant "IIS_IUSRS:(OI)(CI)F"
Ohne dieses Verzeichnis kann die Anwendung bei aktiviertem Logging nicht starten.
5. Application Pool Recycle nach Deployment
Nach jedem Deployment den Application Pool neu starten:
# IIS Manager → Application Pools → [Pool] → Recycle
# oder per Kommandozeile (als Administrator):
%windir%\system32\inetsrv\appcmd recycle apppool /apppool.name:"<Pool-Name>"
Verzeichnisstruktur nach dem Publish
publish-output\
├── EnvelopeGenerator.Server.exe ← Startpunkt (Self-Contained)
├── EnvelopeGenerator.Server.dll ← Managed Assembly
├── EnvelopeGenerator.Server.runtimeconfig.json
├── EnvelopeGenerator.Server.deps.json
├── web.config ← IIS-Konfiguration (auto-generiert)
├── hostfxr.dll ← .NET Host (Self-Contained-Nachweis)
├── coreclr.dll ← .NET Core Runtime
├── Microsoft.Extensions.*.dll ← Framework-DLLs (jetzt enthalten!)
├── DevExpress.*.dll ← UI-Komponenten
├── wwwroot\ ← Statische Web-Assets
│ ├── _framework\ ← WASM-Binaries
│ └── ...
└── logs\ ← Stdout-Logs (manuell anlegen!)
Häufige Fehler
| Fehler | Ursache | Lösung |
|---|---|---|
FileNotFoundException: Microsoft.Extensions.DependencyInjection.Abstractions |
Framework-Dependent Publish auf Server ohne .NET 8 | Self-Contained Publish verwenden |
HTTP 500.30 in IIS |
App startet nicht | Application Pool auf No Managed Code setzen |
HTTP 500.30 + sc-win32-status: 574 |
App Pool falsch oder AspNetCoreModuleV2 fehlt |
Pool prüfen + Hosting Bundle installieren |
App startet per .exe, aber nicht in IIS |
web.config hat noch processPath="dotnet" |
web.config auf .\EnvelopeGenerator.Server.exe korrigieren |
| Logs-Verzeichnis fehlt → App startet nicht | stdoutLogEnabled="true" aber logs\ existiert nicht |
logs\-Ordner anlegen + IIS_IUSRS Schreibrecht geben |