Gerne! Hier ist ein kleines Beispiel für ein plattformübergreifendes .NET Worker Service-Projekt, das unter Windows als Service, unter Linux als systemd-Daemon und unter macOS als launchd-Daemon laufen kann.
dotnet new worker -n MyWorkerService
cd MyWorkerServiceusing Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService() // Aktiviert Windows-Dienstmodus
.UseSystemd() // Aktiviert systemd-Unterstützung für Linux
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();Die Methoden UseWindowsService() und UseSystemd() sind plattformabhängig – sie tun nichts, wenn die App auf der jeweiligen Plattform nicht läuft. Somit kannst du denselben Code auf allen drei Systemen nutzen!
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker läuft: {time}", DateTimeOffset.Now);
await Task.Delay(5000, stoppingToken);
}
}
}dotnet rundotnet publish -c Release -r win-x64 --self-contained false # Für Windows
dotnet publish -c Release -r linux-x64 --self-contained false # Für Linux
dotnet publish -c Release -r osx-x64 --self-contained false # Für macOSBeispiele für die .service-Datei für Linux, eine .plist für macOS für den Autostart und das Ganze als Windows Service zu registrieren. Hier sind praktische Schritt-für-Schritt-Anleitung für alle drei Plattformen:
Erstelle die Datei:
sudo nano /etc/systemd/system/inventarworkerservice.serviceInhalt der Datei:
[Unit]
Description=Inventar Worker Service
After=network.target
[Service]
Type=notify
ExecStart=/pfad/zu/ihrer/app/InventarWorkerService
Restart=on-failure
RestartSec=5
KillSignal=SIGINT
SyslogIdentifier=inventarworkerservice
User=<aktueller_benutzername>
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://localhost:5000
[Install]
WantedBy=multi-user.targetDienst aktivieren und starten:
# Service aktivieren und starten
sudo systemctl enable inventarworkerservice.service
sudo systemctl start inventarworkerservice.service
# Status prüfen
sudo systemctl status inventarworkerservice.service
# Service stoppen
sudo systemctl stop inventarworkerservice.serviceDatei erstellen unter:
nano ~/Library/LaunchAgents/com.inventarworkerservice.plistInhalt:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.inventarworkerservice</string>
<key>ProgramArguments</key>
<array>
<string>/Users/thorstenhindermann/RiderProjects/InventarWorkerService/InventarWorkerService/bin/Debug/net10.0/InventarWorkerService</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<!-- Arbeitsverzeichnis -->
<key>WorkingDirectory</key>
<string>/Users/thorstenhindermann/RiderProjects/InventarWorkerService/InventarWorkerService/bin/Debug/net10.0/</string>
<!-- Umgebungsvariablen -->
<key>EnvironmentVariables</key>
<dict>
<key>ASPNETCORE_ENVIRONMENT</key>
<string>Development</string>
<key>ASPNETCORE_URLS</key>
<string>http://localhost:5000;https://localhost:5001</string>
<!-- Detailliertes Logging aktivieren -->
<key>ASPNETCORE_LOGGING__LOGLEVEL__DEFAULT</key>
<string>Debug</string>
<key>ASPNETCORE_LOGGING__LOGLEVEL__MICROSOFT</key>
<string>Information</string>
<key>ASPNETCORE_LOGGING__LOGLEVEL__MICROSOFT.HOSTING.LIFETIME</key>
<string>Information</string>
<!-- Console-Logging aktivieren -->
<key>ASPNETCORE_LOGGING__CONSOLE__INCLUDESCOPES</key>
<string>true</string>
<!-- Swagger in allen Umgebungen aktivieren -->
<key>ASPNETCORE_ENABLE_SWAGGER</key>
<string>true</string>
<!-- .NET Core Debug-Features -->
<key>DOTNET_ENVIRONMENT</key>
<string>Development</string>
<!-- Zusätzliche Debug-Optionen -->
<key>DOTNET_PRINT_TELEMETRY_MESSAGE</key>
<string>false</string>
<!-- Detaillierte Exception-Seiten -->
<key>ASPNETCORE_DETAILEDERRORS</key>
<string>true</string>
<!-- Host-spezifische Debugging-URLs -->
<key>ASPNETCORE_HOSTINGSTARTUPASSEMBLIES</key>
<string></string>
</dict>
<key>StandardOutPath</key>
<string>/Users/thorstenhindermann/Library/Logs/inventarworkerservice.log</string>
<key>StandardErrorPath</key>
<string>/Users/thorstenhindermann/Library/Logs/inventarworkerservice.error.log</string>
<!-- Benutzer (nur für LaunchDaemons) -->
<key>UserName</key>
<string>thorstenhindermann</string>
<!-- Netzwerk-Abhängigkeiten -->
<key>LaunchOnlyOnce</key>
<false/>
</dict>
</plist>Wenn die Datei erstellt ist, kannst du den Daemon aktivieren und starten üer die Einstellngen, da sich der Agendt dort gleich regisztriert und aufgelistet wird. Dieser wird in der Standardeinstellung bem Anmelden gleich mit gestartet.
Erreichbar unter Systemeinstellungen > Allgemeinstellungen > Anmeldeobjekte & Erweiterungen.
Über den Schalter rechts vom Namen kann der Agendt aktiviert oder deaktiviert werden. Aktuell im Bild ist dieser deaktiviert. Dies kann vom Nutzer selbst gesteuert werden.
Der Dienst kann wie folgt über die Kommandozeile aktiviert werden. Das ist aber keine permanente Aktivierung, sondern nur für die aktuell angemeldete Sitzung.
cd ~launchctl load ~/Library/LaunchDaemons/com.inventarworkerservice.plist
# Erfolg prüfen
launchctl list | grep inventarworkerservice
# Beispielausgabe:
16498 0 com.inventarworkerservicelaunchctl unload ~/Library/LaunchDaemons/com.inventarworkerservice.plist
# Erfolg prüfen
launchctl list | grep inventarworkerservice
# Beispielausgabe:
<keine Ausgabe>Veröffentlichen:
dotnet publish -c Release -r win-x64 --self-contained falseDas Terminal, die PowerShell oder Kommandozeile als Administrator bzw. mit Administrator-Rechten öffnen und den Service mit sc.exe registrieren.
sc create "InventarWorkerService" binPath= "C:\Users\hinde\RiderProjects\InventarWorkerService\InventarWorkerService\bin\Debug\net10.0\InventarWorkerService.exe"Die erfolgreiche Installation wird mit [SC] CreateService SUCCESS oder [SC] CreateService ERFOLG bestätigt.
Der Dienste-Eintrag in der services.msc-Ansicht in der mmc.exe ist dann sichtbar wie im Bild dargestellt:
Alternativ via PowerShell mit New-Service:
New-Service -Name "InventarWorkerService" `
-BinaryPathName "C:\Users\hinde\RiderProjects\InventarWorkerService\InventarWorkerService\bin\Debug\net10.0\InventarWorkerService.exe" `
-DisplayName "InventarWorkerService" `
-StartupType ManualDie erfolgreiche Installation wird der folgenden Meldung bestätigt:
Starten:
sc start InventarWorkerServiceDer erfolgreiche Start wird mit der folgenden Meldung quittiert:
SERVICE_NAME: InventarWorkerService
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 48224
FLAGS :Oder
Start-Service -Name "InventarWorkerService"Der Dienste-Eintrag in der services.msc-Ansicht in der mmc.exe ist dann sichtbar wie im Bild dargestellt:
Außerdem kann in der Ansicht der Dienst auch von diesem Dienstprogramm gestartet werden.
Service stoppen
sc stop "InventarWorkerService"Der erfolgreiche Start wird mit der folgenden Meldung quittiert:
SERVICE_NAME: InventarWorkerService
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0Oder
Stop-Service -Name "InventarWorkerService"Der Dienste-Eintrag in der services.msc-Ansicht in der mmc.exe ist dann sichtbar wie im Bild dargestellt:
Außerdem kann in der Ansicht der Dienst auch von diesem Dienstprogramm gestoppt oder neu gestartet werden.
Service deinstallieren
sc delete "InventarWorkerService"Erstellen Sie ein rc.d-Skript /usr/local/etc/rc.d/inventarworkerservice:
#!/bin/sh
# PROVIDE: inventarworkerservice
# REQUIRE: LOGIN
# KEYWORD: shutdown
. /etc/rc.subr
name="inventarworkerservice"
rcvar="inventarworkerservice_enable"
command="/pfad/zu/ihrer/app/InventarWorkerService"
pidfile="/var/run/inventarworkerservice.pid"
load_rc_config $name
run_rc_command "$1"Service in /etc/rc.conf aktivieren:
# In /etc/rc.conf hinzufügen
inventarworkerservice_enable="YES"
# Service starten
service inventarworkerservice start
# Service stoppen
service inventarworkerservice stopMit diesen Änderungen kann Ihre Anwendung als nativer Service auf allen genannten Betriebssystemen ausgeführt werden.
Die CtrlWorkerServiceApp ist eine einfache Konsolenanwendung, die den Worker Service startet und verwaltet. Sie können sie verwenden, um den Service zu starten, zu stoppen und den Status abzufragen.
# Service starten
CtrlWorkerServiceApp start
# Service stoppen
CtrlWorkerServiceApp stop
# Hilfe anzeigen
CtrlWorkerServiceApp --helpEine kompakte Beispielstruktur für dein plattformübergreifendes Worker-Service-Projekt inklusive Konfigs und Setup-Skripten für Windows, Linux und macOS.
MyWorkerService/
├── Worker.cs
├── Program.cs
├── MyWorkerService.csproj
├── configs/
│ ├── linux/
│ │ └── myworker.service
│ ├── macos/
│ │ └── com.example.myworker.plist
│ └── windows/
│ └── install_service.ps1
├── setup/
│ ├── setup_linux.sh
│ └── setup_macos.sh
#!/bin/bash
SERVICE_NAME=myworker
USER_NAME=$USER
APP_DIR=/opt/myworker
DLL_PATH=$APP_DIR/MyWorkerService.dll
sudo mkdir -p "$APP_DIR"
sudo cp -r ./bin/Release/netX.X/publish/* "$APP_DIR"
sudo cp ./configs/linux/myworker.service /etc/systemd/system/$SERVICE_NAME.service
sudo sed -i "s|/pfad/zu/MyWorkerService.dll|$DLL_PATH|" /etc/systemd/system/$SERVICE_NAME.service
sudo sed -i "s|deinbenutzername|$USER_NAME|" /etc/systemd/system/$SERVICE_NAME.service
sudo systemctl daemon-reexec
sudo systemctl enable $SERVICE_NAME
sudo systemctl start $SERVICE_NAME#!/bin/bash
PLIST_NAME=com.example.myworker.plist
PLIST_TARGET=~/Library/LaunchAgents/$PLIST_NAME
APP_PATH="$(pwd)/bin/Release/netX.X/publish/MyWorkerService.dll"
cp ./configs/macos/$PLIST_NAME "$PLIST_TARGET"
sed -i '' "s|/Pfad/zu/MyWorkerService.dll|$APP_PATH|" "$PLIST_TARGET"
launchctl load "$PLIST_TARGET"$serviceName = "MyWorkerService"
$displayName = "My .NET Worker Service"
$exePath = "C:\Pfad\zu\dotnet.exe C:\Pfad\zu\MyWorkerService.dll"
New-Service -Name $serviceName `
-BinaryPathName $exePath `
-DisplayName $displayName `
-StartupType Automatic
Start-Service $serviceNameErstelle die Projektstruktur:
dotnet new worker -n MyWorkerService
cd MyWorkerServiceFüge die zusätzlichen Ordner und Dateien ein:
mkdir -p configs/linux configs/macos configs/windows setupKopiere den Inhalt aus meiner vorherigen Antwort in folgende Dateien:
configs/linux/myworker.service
configs/macos/com.example.myworker.plist
configs/windows/install_service.ps1
setup/setup_linux.sh
setup/setup_macos.sh
Passe Program.cs und Worker.cs gemäß dem plattformübergreifenden Worker-Modell an (siehe oben).
Erstelle das .zip-Archiv (z.B. auf macOS/Linux):
cd ..
zip -r MyWorkerService.zip MyWorkerService/Oder auf Windows (in PowerShell):
Compress-Archive -Path .\MyWorkerService\* -DestinationPath .\MyWorkerService.zipNach der Implementierung können Sie den Service folgendermaßen abfragen:
- Service Status:
GET http://[IP-Adresse]:5000/api/inventar/status - Hardware Inventar:
GET http://[IP-Adresse]:5000/api/inventar/hardware - Software Inventar:
GET http://[IP-Adresse]:5000/api/inventar/software - Komplettes Inventar:
GET http://[IP-Adresse]:5000/api/inventar/full
curl -X GET "http://192.168.1.100:5000/api/inventar/full" \
-H "accept: application/json"Invoke-RestMethod -Uri "http://192.168.1.100:5000/api/inventar/full" -Method GetDie Swagger UI ist unter http://[IP-Adresse]:5000/swagger verfügbar und bietet eine interaktive Dokumentation der API.
- Speichern Sie die Datei als
api-tests.httpin Ihrem Projekt - In JetBrains Rider können Sie:
- Auf den grünen "Play"-Button neben jeder Anfrage klicken
Ctrl+Enter(Windows/Linux) oderCmd+Enter(macOS) verwenden- Über das Kontextmenü "Run" auswählen
- IP-Adressen: Ändern Sie
192.168.1.100zu Ihrer tatsächlichen Server-IP - Port: Falls Ihr Service auf einem anderen Port läuft, passen Sie
5000entsprechend an - Endpoints: Fügen Sie weitere API-Endpunkte hinzu, falls vorhanden
Das File nutzt:
- Variablen (
@baseUrl,@remoteUrl) für einfache Umgebungsumschaltung - Dynamische Werte (
{{$timestamp}}) für eindeutige Request-IDs - Mehrere Umgebungen (lokal, remote)
- Verschiedene Content-Types und Headers
So können Sie systematisch alle Ihre API-Endpunkte testen!
Ein Beispiel dafür ist das Modul namens SQLite von Code Owls LLC, verfügbar in der PowerShell Gallery.
- Nutzt SQLite-Datenbanken als virtuelle PowerShell-Laufwerke
- Ermöglicht Navigation und Manipulation von Tabellen wie bei Dateisystemobjekten
- Unterstützt Standard-Cmdlets wie Get-ChildItem, New-Item, Remove-Item, etc.
Install-Module -Name SQLiteNew-PSDrive -Name MyDB -PSProvider SQLite -Root "C:\Pfad\zur\Datenbank.db"
Set-Location MyDB:\Tables
Get-ChildItemDamit kannst du Tabellen wie Ordner durchsuchen und sogar direkt mit den Daten arbeiten. Wenn du magst, zeige ich dir ein konkretes Beispiel, wie du Daten aus einer Tabelle abfragen oder bearbeiten kannst. Was hast du vor?
Wenn du den SQLite Cmdlet Provider aus dem PowerShell Gallery Modul SQLite installiert hast und ein SQLite-Datenbanklaufwerk eingebunden wurde, kannst du mit PowerShell ganz einfach Daten abfragen.
Angenommen, du hast eine Tabelle namens users in deiner Datenbank. So könntest du die Daten darin anzeigen:
New-PSDrive -Name MyDB -PSProvider SQLite -Root "C:\Pfad\zur\Datenbank.db"Set-Location MyDB:\Tables\usersGet-ChildItemNew-PSDrive: Bindet die SQLite-DB als Laufwerk namens MyDB einSet-Location: Navigiert zur Tabelle users innerhalb des LaufwerksGet-ChildItem: Holt alle Datensätze (also die Rows) aus der Tabelle
Du kannst auch bestimmte Felder abfragen, z.B.:
Get-ChildItem | Select-Object Firstname, LastnameGet-ChildItem | Where-Object { $_.City -eq "Berlin" }Für PowerShell Core gibt es aktuell keinen SQLite Cmdlet Provider, der wie ein Dateisystem funktioniert (also mit New-PSDrive, Get-ChildItem, etc.). Die bekannten Provider wie das SQLite-Modul von Code Owls LLC sind nicht kompatibel mit PowerShell Core, da sie auf Windows-spezifische .NET Framework-Komponenten setzen.
Das Modul PSSQLite funktioniert plattformübergreifend – also auch unter macOS und Linux – und bietet Cmdlets wie Invoke-SqliteQuery, mit denen du direkt SQL-Abfragen ausführen kannst.
Install-Module -Name PSSQLite -Scope CurrentUser
$db = "./MeineDatenbank.sqlite"
$query = "SELECT * FROM users"
Invoke-SqliteQuery -DataSource $db -Query $queryDas ist zwar kein Provider im klassischen Sinne, aber du bekommst vollen Zugriff auf die Datenbankinhalte – und das ganz ohne Windows-Abhängigkeiten. Wenn du magst, helfe ich dir beim Einrichten oder zeige dir, wie du Tabellen erstellst, Daten einfügst oder filterst. Was möchtest du mit SQLite machen?
Das Modul PSSQLite bietet dir eine flexible Möglichkeit, SQLite-Datenbanken direkt aus PowerShell heraus zu verwalten – und das plattformübergreifend. Hier sind einige erweiterte Beispiele, die dir zeigen, wie du Tabellen erstellen, Daten einfügen, abfragen und sogar große Datenmengen effizient verarbeiten kannst.
$db = "./MeineDatenbank.sqlite"
$query = @"
CREATE TABLE users (
UserId INTEGER PRIMARY KEY AUTOINCREMENT,
Firstname TEXT NOT NULL,
Lastname TEXT NOT NULL,
Birthdate DATE
)
"@
Invoke-SqliteQuery -DataSource $db -Query $query$query = "INSERT INTO users (Firstname, Lastname, Birthdate) VALUES (@fn, @ln, @bd)"
$params = @{ fn = "Thorsten"; ln = "Beispiel"; bd = (Get-Date).AddYears(-30).ToString("yyyy-MM-dd") }
Invoke-SqliteQuery -DataSource $db -Query $query -SqlParameters $params$query = "SELECT * FROM users WHERE Lastname = @ln"
$params = @{ ln = "Beispiel" }
Invoke-SqliteQuery -DataSource $db -Query $query -SqlParameters $params# Beispiel-Daten generieren
$data = 1..1000 | ForEach-Object {
[pscustomobject]@{
Firstname = "Vorname$_"
Lastname = "Nachname$_"
Birthdate = (Get-Date).AddDays(-$_).ToString("yyyy-MM-dd")
}
}
# In DataTable konvertieren
$dataTable = $data | Out-DataTable
# Bulk Copy ausführen
Invoke-SQLiteBulkCopy -DataTable $dataTable -DataSource $db -Table users -NotifyAfter 100 -VerboseInvoke-SqliteQuery -DataSource $db -Query "PRAGMA table_info(users)"Hier kommt eine geballte Ladung SQLite-Power – alles direkt nutzbar mit dem PSSQLite-Modul in PowerShell Core. Ich zeige dir, wie du Views, Indizes, Trigger erstellst und die Struktur deiner Datenbank analysierst – ganz ohne GUI.
Ein View ist eine gespeicherte SQL-Abfrage, die du wie eine Tabelle behandeln kannst.
$query = @"
CREATE VIEW active_users AS
SELECT UserId, Firstname, Lastname
FROM users
WHERE IsActive = 1
"@
Invoke-SqliteQuery -DataSource $db -Query $queryInvoke-SqliteQuery -DataSource $db -Query "SELECT * FROM active_users"
``
#### 🚀 2. Index erstellen – Performance-Booster
Ein Index beschleunigt Suchabfragen auf bestimmten Spalten.
```powershell
$query = "CREATE INDEX idx_lastname ON users(Lastname)"
Invoke-SqliteQuery -DataSource $db -Query $query💡 Tipp: Nutze EXPLAIN QUERY PLAN zur Analyse, ob dein Index verwendet wird.
Ein Trigger reagiert auf INSERT, UPDATE oder DELETE und führt automatisch SQL aus.
$query = @"
CREATE TRIGGER log_insert
AFTER INSERT ON users
FOR EACH ROW
BEGIN
INSERT INTO user_log (UserId, Action, Timestamp)
VALUES (NEW.UserId, 'INSERT', DATETIME('now'));
END;
"@
Invoke-SqliteQuery -DataSource $db -Query $queryInvoke-SqliteQuery -DataSource $db -Query "SELECT name FROM sqlite_master WHERE type='table'"Invoke-SqliteQuery -DataSource $db -Query "PRAGMA table_info(users)"Invoke-SqliteQuery -DataSource $db -Query "SELECT name FROM sqlite_master WHERE type='view'"
Invoke-SqliteQuery -DataSource $db -Query "SELECT name FROM sqlite_master WHERE type='trigger'"
Invoke-SqliteQuery -DataSource $db -Query "SELECT name FROM sqlite_master WHERE type='index'"Hier ist ein PowerShell-Skript, das mit dem PSSQLite-Modul die Struktur deiner SQLite-Datenbank analysiert und dokumentiert – inklusive Tabellen, Spalten, Views, Indizes und Trigger. Die Ausgabe erfolgt als Markdown-Datei, die du z. B. in GitHub oder VS Code schön lesen kannst.
# Pfad zur SQLite-Datenbank
$dbPath = "./MeineDatenbank.sqlite"
$outputFile = "./DatenbankDokumentation.md"
# Funktion zum Ausführen von SQL-Abfragen
function Run-Query($query) {
Invoke-SqliteQuery -DataSource $dbPath -Query $query
}
# Starte Dokumentation
@"
# 📘 SQLite-Datenbankstruktur
**Datenbank:** $dbPath
**Erstellt am:** $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
"@ | Out-File $outputFile -Encoding UTF8
# Tabellen auflisten
$tables = Run-Query "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
Add-Content $outputFile "`n## 📂 Tabellen"
foreach ($table in $tables) {
Add-Content $outputFile "`n### 🧱 Tabelle: $($table.name)"
$columns = Run-Query "PRAGMA table_info([$($table.name)])"
Add-Content $outputFile "| Spalte | Typ | Nullable | Default |"
Add-Content $outputFile "|--------|-----|----------|---------|"
foreach ($col in $columns) {
$nullable = if ($col.notnull -eq 0) { "✅" } else { "❌" }
Add-Content $outputFile "| $($col.name) | $($col.type) | $nullable | $($col.dflt_value) |"
}
}
# Views
$views = Run-Query "SELECT name, sql FROM sqlite_master WHERE type='view'"
if ($views.Count -gt 0) {
Add-Content $outputFile "`n## 👁️ Views"
foreach ($view in $views) {
Add-Content $outputFile "`n### View: $($view.name)`n```sql`n$($view.sql)`n```"
}
}
# Indizes
$indexes = Run-Query "SELECT name, tbl_name, sql FROM sqlite_master WHERE type='index' AND sql IS NOT NULL"
if ($indexes.Count -gt 0) {
Add-Content $outputFile "`n## 📌 Indizes"
foreach ($idx in $indexes) {
Add-Content $outputFile "`n### Index: $($idx.name) auf Tabelle $($idx.tbl_name)`n```sql`n$($idx.sql)`n```"
}
}
# Trigger
$triggers = Run-Query "SELECT name, tbl_name, sql FROM sqlite_master WHERE type='trigger'"
if ($triggers.Count -gt 0) {
Add-Content $outputFile "`n## ⚙️ Trigger"
foreach ($trg in $triggers) {
Add-Content $outputFile "`n### Trigger: $($trg.name) auf Tabelle $($trg.tbl_name)`n```sql`n$($trg.sql)`n```"
}
}
Write-Host "✅ Dokumentation erstellt: $outputFile"Dieses Skript erzeugt eine gut lesbare Markdown-Datei mit allen relevanten Strukturelementen deiner Datenbank. Du kannst es natürlich anpassen – z.B. für HTML, CSV oder direktes Reporting.
Hier kommt die erweiterte Version deines PowerShell-Skripts mit Statistiken, Foreign Keys und Beispielabfragen – alles dokumentiert in deiner Markdown-Datei. Damit erhältst du eine vollständige Übersicht über die Struktur und Beziehungen deiner SQLite-Datenbank.
Add-Content $outputFile "`n## 📊 Tabellenstatistiken"
foreach ($table in $tables) {
$countQuery = "SELECT COUNT(*) AS Count FROM [$($table.name)]"
$count = Run-Query $countQuery
Add-Content $outputFile "`n**$($table.name):** $($count.Count) Datensätze"
}Add-Content $outputFile "`n## 🔗 Foreign Keys"
foreach ($table in $tables) {
$fks = Run-Query "PRAGMA foreign_key_list([$($table.name)])"
if ($fks.Count -gt 0) {
Add-Content $outputFile "`n### Tabelle: $($table.name)"
Add-Content $outputFile "| Von-Spalte | Ziel-Tabelle | Ziel-Spalte | On Update | On Delete |"
Add-Content $outputFile "|------------|--------------|-------------|-----------|-----------|"
foreach ($fk in $fks) {
Add-Content $outputFile "| $($fk.from) | $($fk.table) | $($fk.to) | $($fk.on_update) | $($fk.on_delete) |"
}
}
}Add-Content $outputFile "`n## 🧪 Beispielabfragen"
foreach ($table in $tables) {
$columns = Run-Query "PRAGMA table_info([$($table.name)])"
$firstCol = $columns[0].name
$query = "SELECT $firstCol FROM [$($table.name)] LIMIT 5"
Add-Content $outputFile "`n### $($table.name)`n```sql`n$query`n```"
}Die Markdown-Datei enthält jetzt:
- Tabellenübersicht mit Spalten
- Views, Indizes, Trigger
- Foreign Key-Beziehungen
- Datensatzanzahl pro Tabelle
- Beispielhafte SELECT-Abfragen
Jetzt wird dein SQLite-Dokumentationsskript richtig mächtig. Ich erweitere es um die folgenden Features:
Wir analysieren Foreign Keys und bauen eine einfache Baumstruktur, die zeigt, welche Tabellen andere referenzieren.
Add-Content $outputFile "`n## 🌳 Tabellenabhängigkeiten"
foreach ($table in $tables) {
$fks = Run-Query "PRAGMA foreign_key_list([$($table.name)])"
if ($fks.Count -gt 0) {
Add-Content $outputFile "`n**$($table.name)** referenziert:"
foreach ($fk in $fks) {
Add-Content $outputFile "- ➤ `$($fk.table)` über `$($fk.from)` → `$($fk.to)`"
}
}
}Zusätzlich zur Markdown-Datei kannst du eine HTML-Version erzeugen. Beispiel:
$htmlFile = "./DatenbankDokumentation.html"
$htmlHeader = @"
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>SQLite-Dokumentation</title>
<style>
body { font-family: sans-serif; margin: 2em; }
h2, h3 { color: #2c3e50; }
table { border-collapse: collapse; width: 100%; margin-bottom: 1em; }
th, td { border: 1px solid #ccc; padding: 0.5em; text-align: left; }
th { background-color: #f5f5f5; }
</style>
</head>
<body>
<h1>📘 SQLite-Datenbankstruktur</h1>
<p><strong>Datenbank:</strong> $dbPath<br><strong>Erstellt am:</strong> $(Get-Date)</p>
"@
$htmlHeader | Out-File $htmlFile -Encoding UTF8Dann kannst du die Tabellenstruktur und Inhalte als HTML-Elemente hinzufügen – z.B. mit Add-Content $htmlFile "<table>...</table>".
Hier ist dein vollständiges PowerShell-Skript zur automatischen Dokumentation einer SQLite-Datenbankstruktur inklusive: Tabellen, Spalten, Views, Indizes, Trigger Foreign Keys und Tabellenabhängigkeiten Beispielabfragen und Statistiken Markdown- und HTML-Export PDF-Erzeugung über Microsoft Word (sofern verfügbar)
# SQLite-Dokumentation mit PSSQLite
# Autor: Copilot für Thorsten
# Voraussetzungen: PSSQLite-Modul
Import-Module PSSQLite
$dbPath = "./MeineDatenbank.sqlite"
$mdFile = "./DatenbankDokumentation.md"
$htmlFile = "./DatenbankDokumentation.html"
$pdfFile = "C:\Temp\DatenbankDokumentation.pdf"
function Run-Query($query) {
Invoke-SqliteQuery -DataSource $dbPath -Query $query
}
# Markdown-Dokumentation starten
@"
# 📘 SQLite-Datenbankstruktur
**Datenbank:** $dbPath
**Erstellt am:** $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
"@ | Out-File $mdFile -Encoding UTF8
$tables = Run-Query "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
# Tabellenstruktur
Add-Content $mdFile "`n## 📂 Tabellen"
foreach ($table in $tables) {
Add-Content $mdFile "`n### 🧱 Tabelle: $($table.name)"
$columns = Run-Query "PRAGMA table_info([$($table.name)])"
Add-Content $mdFile "| Spalte | Typ | Nullable | Default |"
Add-Content $mdFile "|--------|-----|----------|---------|"
foreach ($col in $columns) {
$nullable = if ($col.notnull -eq 0) { "✅" } else { "❌" }
Add-Content $mdFile "| $($col.name) | $($col.type) | $nullable | $($col.dflt_value) |"
}
}
# Tabellenstatistiken
Add-Content $mdFile "`n## 📊 Tabellenstatistiken"
foreach ($table in $tables) {
$count = Run-Query "SELECT COUNT(*) AS Count FROM [$($table.name)]"
Add-Content $mdFile "`n**$($table.name):** $($count.Count) Datensätze"
}
# Foreign Keys
Add-Content $mdFile "`n## 🔗 Foreign Keys"
foreach ($table in $tables) {
$fks = Run-Query "PRAGMA foreign_key_list([$($table.name)])"
if ($fks.Count -gt 0) {
Add-Content $mdFile "`n### Tabelle: $($table.name)"
Add-Content $mdFile "| Von-Spalte | Ziel-Tabelle | Ziel-Spalte | On Update | On Delete |"
Add-Content $mdFile "|------------|--------------|-------------|-----------|-----------|"
foreach ($fk in $fks) {
Add-Content $mdFile "| $($fk.from) | $($fk.table) | $($fk.to) | $($fk.on_update) | $($fk.on_delete) |"
}
}
}
# Tabellenabhängigkeiten
Add-Content $mdFile "`n## 🌳 Tabellenabhängigkeiten"
foreach ($table in $tables) {
$fks = Run-Query "PRAGMA foreign_key_list([$($table.name)])"
if ($fks.Count -gt 0) {
Add-Content $mdFile "`n**$($table.name)** referenziert:"
foreach ($fk in $fks) {
Add-Content $mdFile "- ➤ `$($fk.table)` über `$($fk.from)` → `$($fk.to)`"
}
}
}
# Beispielabfragen
Add-Content $mdFile "`n## 🧪 Beispielabfragen"
foreach ($table in $tables) {
$columns = Run-Query "PRAGMA table_info([$($table.name)])"
$firstCol = $columns[0].name
$query = "SELECT $firstCol FROM [$($table.name)] LIMIT 5"
Add-Content $mdFile "`n### $($table.name)`n```sql`n$query`n```"
}
# Views, Indizes, Trigger
$views = Run-Query "SELECT name, sql FROM sqlite_master WHERE type='view'"
if ($views.Count -gt 0) {
Add-Content $mdFile "`n## 👁️ Views"
foreach ($view in $views) {
Add-Content $mdFile "`n### View: $($view.name)`n```sql`n$($view.sql)`n```"
}
}
$indexes = Run-Query "SELECT name, tbl_name, sql FROM sqlite_master WHERE type='index' AND sql IS NOT NULL"
if ($indexes.Count -gt 0) {
Add-Content $mdFile "`n## 📌 Indizes"
foreach ($idx in $indexes) {
Add-Content $mdFile "`n### Index: $($idx.name) auf Tabelle $($idx.tbl_name)`n```sql`n$($idx.sql)`n```"
}
}
$triggers = Run-Query "SELECT name, tbl_name, sql FROM sqlite_master WHERE type='trigger'"
if ($triggers.Count -gt 0) {
Add-Content $mdFile "`n## ⚙️ Trigger"
foreach ($trg in $triggers) {
Add-Content $mdFile "`n### Trigger: $($trg.name) auf Tabelle $($trg.tbl_name)`n```sql`n$($trg.sql)`n```"
}
}
Write-Host "✅ Markdown-Dokumentation erstellt: $mdFile"
# HTML aus Markdown generieren (optional mit Pandoc)
if (Get-Command pandoc -ErrorAction SilentlyContinue) {
pandoc $mdFile -f markdown -t html -o $htmlFile
Write-Host "🌐 HTML-Datei erstellt: $htmlFile"
}
# PDF aus HTML generieren (über Word COM-Objekt, nur Windows)
if ($IsWindows) {
$Word = New-Object -ComObject Word.Application
$Word.Visible = $false
$Doc = $Word.Documents.Open((Resolve-Path $htmlFile))
$Doc.SaveAs([ref] $pdfFile, [ref] 17)
$Doc.Close()
$Word.Quit()
Write-Host "📄 PDF-Datei erstellt: $pdfFile"
} else {
Write-Host "⚠️ PDF-Export nur unter Windows mit Microsoft Word möglich."
}- Für den PDF-Export brauchst du Microsoft Word und Windows. Alternativ kannst du pandoc mit wkhtmltopdf kombinieren.
- Das Skript erzeugt eine Markdown-Datei, optional eine HTML-Datei, und wenn möglich eine PDF-Datei.
- Du kannst es als Dokumentiere-SQLite.ps1 speichern und direkt ausführen.
PowerShell-Provider, der mit SQLite-Datenbanken funktioniert. Es läuft unter Windows, macOS und Linux. Sie können es direkt aus der PowerShell Gallery installieren. Installation: Install-Module -Name PSSQLite Beispiel für die Verwendung:
# Modul importieren
Import-Module PSSQLite
# Eine Abfrage gegen eine Datenbankdatei ausführen (wird erstellt, wenn
sie nicht existiert)
Invoke-SqliteQuery -DataSource 'C
:\Users\thinder\RiderProjects\InventarWorkerService\InventarWorkerService\
bin\Debug\net10.0\mydatabase.db' -Query 'CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY, name TEXT);'
Invoke-SqliteQuery -DataSource 'C
:\Users\thinder\RiderProjects\InventarWorkerService\InventarWorkerService\
bin\Debug\net10.0\mydatabase.db' -Query 'INSERT INTO users (name) VALUES (
"John Doe"), ("Jane Doe");'
# Daten abfragen und anzeigen
Invoke-SqliteQuery -DataSource 'C
:\Users\thinder\RiderProjects\InventarWorkerService\InventarWorkerService\
bin\Debug\net10.0\mydatabase.db' -Query 'SELECT * FROM users;'Eine andere Möglichkeit ist die direkte Verwendung der .NET-Bibliothek Microsoft.Data.Sqlite, die ebenfalls plattformübergreifend ist, aber die Verwendung erfordert, dass Sie die .NET-Klassen direkt in PowerShell aufrufen, anstatt Cmdlets zu verwenden. Für die meisten Anwendungsfälle ist das PSSQLite-Modul jedoch einfacher zu handhaben.
Interaktion mit SQLite-Datenbanken. Der Hauptunterschied und das zentrale Merkmal von SQLitePS ist, dass es einen PowerShell Provider implementiert.
Ein PowerShell Provider ermöglicht es Ihnen, auf eine Datenquelle zuzugreifen und sie zu verwalten, als wäre sie ein Dateisystem-Laufwerk (wie C:). Anstatt nur Befehle zum Abfragen der Datenbank zu haben, können Sie mit SQLitePS ein SQLite-Datenbankfile als "Laufwerk" einbinden und dann mit Standard-PowerShell-Befehlen wie cd, dir (oder Get-ChildItem), Get-Item usw. darin navigieren.
- Ein "Laufwerk" erstellen: Sie mappen eine SQLite-Datei auf einen Laufwerksbuchstaben (z.B. DB:).
- Navigieren: Mit cd DB: wechseln Sie in die Datenbank.
- Objekte auflisten: Mit dir können Sie die Tabellen, Views und andere Objekte in der Datenbank auflisten.
- Struktur ansehen: Sie können weiter in Tabellen "hineinnavigieren" (cd MyTable), um Spalten und Indizes zu sehen.
| SQLitePCmdlet Provider | PSSQLite Cmdlet |
|---|---|
| Provider-basiert | Cmdlet-basiert: Fokus auf das Au... |
| cd, dir, Get-Item | Invoke-SqliteQuery, `Open-Sqlite... |
| "Ich möchte sehen, was in di... | "Ich muss dieses SQL-Skript ausf... |
| Läuft ebenfalls plattformübe... | Läuft plattformübergreifend und ... |
# Installation und Import
Install-Module -Name SQLitePS
Import-Module SQLitePS
# Erstelle ein neues PSDrive, das auf deine Datenbankdatei zeigt
New-PSDrive -Name "MyDB" -PSProvider "SQLite" -Root "C:\Users\t
hinder\RiderProjects\InventarWorkerService\InventarWorkerService\bin\Debu
g\net10.0\mydatabase.db"
# Wechsle in das neue Laufwerk
cd MyDB:
# Liste die Tabellen in der Datenbank auf
dir
# Wechsle in den "tables" Container
cd tables
# Liste wieder die Tabellen auf
dir
# Wechsle in eine spezifische Tabelle, um ihre Spalten zu sehen
cd users
# Liste die Spalten der "users" Tabelle auf
dirSQLitePS bietet einen sehr "PowerShell-idiomatischen" Weg, um mit SQLite zu arbeiten, der sich stark an der Dateisystem-Metapher orientiert. Es ist grossartig für Administratoren, die eine Datenbank schnell und interaktiv untersuchen wollen.
Für reine Datenabfragen in Skripten wird oft PSSQLite bevorzugt, da der Invoke-SqliteQuery-Befehl sehr direkt und einfach zu verwenden ist. Beide Module sind jedoch leistungsfähig und haben ihre Berechtigung.
Wenn unter Linux/Ubuntu mit normalen USer-Rechten der InventarWorkerService nicht gestartet werden kann, dann versuche
sudo setcap 'cap_net_bind_service=+ep' /home/thinder/RiderProjects/InventarWorkerService/InventarWorkerService/bin/Debug/net10.0/InventarWorkerService
sudo setcap 'cap_net_bind_service=+ep' /home/thinder/RiderProjects/InventarWorkerService/InventarViewerApp/bin/Debug/net10.0/InventarViewerApp
# Auflösen des SymLink
readlink -f /usr/bin/dotnet
# Antwort
/usr/lib/dotnet/dotnet
sudo setcap 'cap_net_bind_service=+ep' /usr/lib/dotnet/dotnetProjektstart: 26.06.2025 (Geburtstag und 1. Urlaubstag) Funktionsumfang abgeschlossen: 16.11.2025 Implementierung abgeschlossen: TT.MM.JJJJ







