Sie haben entschieden, dass das Auftragsbuch modernisiert wird. Bevor Sie etwas ändern, müssen Sie wissen, was Sie ändern: welche Projekte, welche Pakete, welche Windows-Eigenheiten, welche Datenbankobjekte, und vor allem, wer wen aufruft. Die Inventur ist die Arbeit, die Sie später alle paar Stunden sparen, weil niemand mehr raten muss.

Eine Inventur ist keine Bewertung durch ein Werkzeug. Sie liefert Tatsachen, die Sie selbst prüfen können, und eine Karte, die das Team gemeinsam liest. In dieser Lektion erstellen Sie beides, einmal von Hand mit einem kleinen Skript, und ordnen dann ein, was die Werkzeuge von Microsoft dazu beitragen.

Was eine Inventur erfasst

Blick Frage Wie Sie es erfahren
Projekte Welche Projekte gibt es, in welchem Format, für welche Zielversion? Projektdateien lesen
Pakete Welche Pakete in welchen Versionen, und gibt es sie für .NET? packages.config, später dotnet package list
Windows-Abhängigkeiten Welche Programmteile hängen an Windows (Registry, Druck, Anmeldung, Browser-Steuerelement)? Suche im Quelltext
Datenbank Welche Tabellen, Prozeduren und Trigger gibt es, und wer nutzt sie? Abfrage der Systemsichten
Wer ruft wen Wie hängen die Klassen und Formulare zusammen? Lesen, Suchen, eine Zeichnung
Umgebung Welche Server, Laufwerke, Gruppen, Drucker und Konten nutzt das Programm? Konfiguration und Gespräch mit den Menschen

Die ersten vier Zeilen lassen sich zum großen Teil automatisieren. Die letzten beiden nicht: „Wer ruft wen“ und „Umgebung“ brauchen Menschen, die das Programm kennen. Frau Aydin weiß, dass der Export jeden Freitag läuft. Im Quelltext steht das nirgends.

Was Microsoft empfiehlt

Microsofts Dokumentation zum Anheben von .NET-Programmen nennt Dinge, die vor dem Umbau zu prüfen sind. Sie sind der Rahmen für die Inventur:

  • Abhängigkeiten zuerst. Pakete und Bibliotheken müssen auf .NET, .NET Standard oder .NET Core zielen. Ein Paket, das nur .NET Framework unterstützt, ist ein Hindernis oder ein Fall für den Kompatibilitätsmodus (Warnung NU1701), den Sie testen müssen.
  • Windows-spezifische APIs. Viele APIs von .NET Framework gibt es auf .NET nicht, weil sie an Windows hängen, etwa die Windows-Registrierung oder das GDI+-Zeichenmodell. Das Windows Compatibility Pack (Paket Microsoft.Windows.Compatibility) stellt einen großen Teil davon bereit, nach Microsofts Angaben rund 20.000 APIs, unter anderem für Registry, Ereignisprotokoll, WMI, Verzeichnisdienste und Zugriffssteuerung.
  • Technologien, die es nicht mehr gibt. Anwendungsdomänen (AppDomains), Remoting, Code Access Security, Security Transparency, System.EnterpriseServices und Windows Workflow Foundation sind auf .NET nicht verfügbar. Manche APIs existieren noch, werfen aber zur Laufzeit eine PlatformNotSupportedException. Microsoft rät deshalb, früh zu testen.
  • Drittanbieter-Steuerelemente. Sie wurden eventuell nie für .NET veröffentlicht und bleiben dann bei .NET Framework.
  • Projektformat. Das Projekt sollte im SDK-Format vorliegen und PackageReference statt packages.config verwenden.

Dazu gibt es Werkzeuge, die Microsoft als Hilfe beschreibt. Sie ersetzen die Inventur nicht, sie beschleunigen sie:

Werkzeug Was es tut Hinweis
GitHub-Copilot-Modernisierung Bewertung, Planung und Ausführung in drei Stufen; bewertet Projektstruktur, Abhängigkeiten und Codemuster; legt seinen Stand unter .github/upgrades/ ab von Microsoft empfohlen; setzt GitHub Copilot voraus
Azure Migrate: Anwendungs- und Codebewertung für .NET Analyse von Code und Anwendung mit Empfehlungen für Cloud-Bereitstellung die Copilot-Modernisierung nutzt dieses Werkzeug
.NET Upgrade Assistant Befehlszeilenwerkzeug für das Anheben von Microsoft als veraltet bezeichnet, nur noch für Umgebungen ohne Copilot gedacht
Plattformkompatibilitäts-Analyzer (CA1416) warnt, wenn Sie Windows-spezifische APIs ohne passenden Plattformbezug aufrufen für Ziele ab net5.0 standardmäßig an, für andere Ziele einschaltbar

Dieser Kurs zeigt jeden Schritt von Hand. Ein Werkzeug darf Ihnen die Arbeit abnehmen, aber Sie müssen prüfen, was es getan hat, und dafür müssen Sie wissen, was es hätte tun sollen. Für die Inventur gilt das doppelt: Die Antwort „das Werkzeug hat nichts gefunden“ ist nur etwas wert, wenn Sie wissen, wonach es gesucht hat.

Das Skript: Projekte, Pakete, Abhängigkeiten

Das Skript liest einen Ordner mit Projekten und gibt drei Tabellen aus: die Projekte (Format, Zielversion, Art), die Pakete aus packages.config und die Treffer für Windows-Abhängigkeiten. Es sucht mit regulären Ausdrücken in den *.cs- und *.config-Dateien und nennt je Treffer, was er für die Migration bedeutet. Es liest nur und ändert nichts. Die Suchmuster sind eine Liste, die Sie erweitern können.

PowerShell
<#
.SYNOPSIS
Inventur eines .NET-Projektbaums: Projekte, Pakete und Treffer für Windows-Abhängigkeiten. Liest nur, ändert nichts.

.EXAMPLE
./scripts/Inventur.ps1 -Pfad ./Brandt.Auftragsbuch
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [string] $Pfad
)

$ErrorActionPreference = 'Stop'
$Pfad = (Resolve-Path -LiteralPath $Pfad).Path
$ohneBuild = '[\\/](bin|obj|packages)[\\/]'

# Name -> Muster -> was es für die Migration bedeutet
$muster = [ordered]@{
    'Windows Forms'      = @{ Regex = 'System\.Windows\.Forms'; Folge = 'net10.0-windows, UseWindowsForms' }
    'DataSet/ADO.NET'    = @{ Regex = 'SqlDataAdapter|DataSet|DataTable|SqlCommandBuilder'; Folge = 'Zugriff auf Daten ablösen' }
    'App.config'         = @{ Regex = 'ConfigurationManager'; Folge = 'appsettings.json, Configuration' }
    'Windows-Anmeldung'  = @{ Regex = 'WindowsIdentity|WindowsPrincipal'; Folge = 'Microsoft Entra ID' }
    'Drucken/Drawing'    = @{ Regex = 'PrintDocument|PrintPreviewDialog|System\.Drawing'; Folge = 'System.Drawing.Common, später PDF' }
    'Browser-Steuerelem.' = @{ Regex = 'WebBrowser'; Folge = 'WebView2' }
    'Registry'           = @{ Regex = 'Microsoft\.Win32|Registry\.'; Folge = 'Windows Compatibility Pack' }
    'Ereignisprotokoll'  = @{ Regex = 'EventLog'; Folge = 'Windows Compatibility Pack' }
    'Netzlaufwerk (UNC)' = @{ Regex = '\\\\[A-Za-z0-9._-]+\\'; Folge = 'Blob-Speicher oder Freigabe' }
    'Codepages'          = @{ Regex = 'Encoding\.GetEncoding\('; Folge = 'Codepages-Provider nötig' }
    'AppDomain/Remoting' = @{ Regex = 'AppDomain\.CreateDomain|System\.Runtime\.Remoting|BinaryFormatter'; Folge = 'in .NET nicht verfügbar' }
    'Native Aufrufe'     = @{ Regex = 'DllImport'; Folge = 'prüfen, Windows-spezifisch' }
}

$projekte = foreach ($datei in Get-ChildItem -LiteralPath $Pfad -Filter '*.csproj' -Recurse -File | Where-Object FullName -NotMatch $ohneBuild) {
    $xml = [xml](Get-Content -LiteralPath $datei.FullName -Raw)
    $sdk = $null -ne $xml.Project.Sdk
    $ziel = $xml.SelectNodes("//*[local-name()='TargetFramework' or local-name()='TargetFrameworkVersion' or local-name()='TargetFrameworks']") | Select-Object -First 1
    $art = $xml.SelectNodes("//*[local-name()='OutputType']") | Select-Object -First 1
    $verweise = @($xml.SelectNodes("//*[local-name()='ProjectReference']") | ForEach-Object { $_.Include })
    [pscustomobject]@{
        Projekt = $datei.BaseName
        Format  = if ($sdk) { 'SDK' } else { 'klassisch' }
        Ziel    = if ($ziel) { $ziel.InnerText } else { '?' }
        Art     = if ($art) { $art.InnerText } else { 'Library' }
        Verweise = $verweise.Count
    }
}

$pakete = foreach ($datei in Get-ChildItem -LiteralPath $Pfad -Filter 'packages.config' -Recurse -File | Where-Object FullName -NotMatch $ohneBuild) {
    foreach ($paket in ([xml](Get-Content -LiteralPath $datei.FullName -Raw)).packages.package) {
        [pscustomobject]@{ Paket = $paket.id; Version = $paket.version; Quelle = 'packages.config' }
    }
}

$quellen = Get-ChildItem -LiteralPath $Pfad -Filter '*.cs' -Recurse -File | Where-Object FullName -NotMatch $ohneBuild
$konfig = Get-ChildItem -LiteralPath $Pfad -Filter '*.config' -Recurse -File | Where-Object FullName -NotMatch $ohneBuild
$treffer = foreach ($name in $muster.Keys) {
    $gefunden = @(($quellen + $konfig) | Select-String -Pattern $muster[$name].Regex)
    if ($gefunden.Count -gt 0) {
        [pscustomobject]@{
            Abhängigkeit = $name
            Treffer      = $gefunden.Count
            Dateien      = ($gefunden.Path | ForEach-Object { Split-Path $_ -Leaf } | Sort-Object -Unique) -join ', '
            Folge        = $muster[$name].Folge
        }
    }
}

'--- Projekte'
$projekte | Format-Table -AutoSize | Out-String -Width 200
'--- Pakete'
$pakete | Format-Table -AutoSize | Out-String -Width 200
'--- Windows-Abhängigkeiten'
$treffer | Format-Table -AutoSize -Wrap | Out-String -Width 200
PowerShell
./scripts/Inventur.ps1 -Pfad ./Brandt.Auftragsbuch
--- Projekte

Projekt             Format    Ziel Art    Verweise
-------             ------    ---- ---    --------
Brandt.Auftragsbuch klassisch v4.8 WinExe        0

--- Pakete

Paket           Version Quelle
-----           ------- ------
Newtonsoft.Json 13.0.3  packages.config

--- Windows-Abhängigkeiten

Abhängigkeit        Treffer Dateien                                                         Folge
------------        ------- -------                                                         -----
Windows Forms             4 HauptForm.cs, KundenForm.cs, Program.cs, WartungsForm.cs        net10.0-windows, UseWindowsForms
DataSet/ADO.NET          15 AuftragsDaten.cs, Exporte.cs, HauptForm.cs                      Zugriff auf Daten ablösen
App.config                2 Einstellungen.cs                                                appsettings.json, Configuration
Windows-Anmeldung         2 Berechtigung.cs                                                 Microsoft Entra ID
Drucken/Drawing           8 HauptForm.cs, KundenForm.cs, Rechnungsdruck.cs, WartungsForm.cs System.Drawing.Common, später PDF
Browser-Steuerelem.       2 KundenForm.cs                                                   WebView2
Registry                  3 HauptForm.cs                                                    Windows Compatibility Pack
Ereignisprotokoll         1 Program.cs                                                      Windows Compatibility Pack
Netzlaufwerk (UNC)        1 App.config                                                      Blob-Speicher oder Freigabe
Codepages                 1 Exporte.cs                                                      Codepages-Provider nötig

Das Bild ist deutlich. Das Programm ist ein Projekt im klassischen Format, es hat ein Paket, und es hängt an Windows Forms, am DataSet, an App.config, an der Windows-Anmeldung, am Drucken, am Browser-Steuerelement, an der Registry, am Ereignisprotokoll und an einem Netzlaufwerk. Die Spalte „Folge“ ist ein Vorschlag, kein Befund: Sie ordnet jede Abhängigkeit einer Lektion dieses Kurses zu, und Sie prüfen jeden Eintrag selbst.

Die Datenbank

Die Datenbank sagt Ihnen, was die Programmierer nie aufgeschrieben haben. Zwei Abfragen genügen: die Objekte und die Abhängigkeiten zwischen ihnen. Die Sicht sys.sql_expression_dependencies führt eine Zeile je Abhängigkeit von Objekt zu Objekt. Gespeicherte Prozeduren und Trigger erscheinen dort als abhängige Objekte.

SQL
SET NOCOUNT ON;

SELECT o.type_desc AS Art, o.name AS Name, ISNULL(p.name, N'') AS GehoertZu
FROM sys.objects AS o
LEFT JOIN sys.objects AS p ON p.object_id = o.parent_object_id AND o.type = N'TR'
WHERE o.is_ms_shipped = 0 AND o.type IN (N'U', N'V', N'P', N'FN', N'TR')
ORDER BY o.type_desc, o.name;

SELECT OBJECT_NAME(d.referencing_id) AS Von, d.referenced_entity_name AS Nach
FROM sys.sql_expression_dependencies AS d
WHERE d.referencing_id IN (SELECT object_id FROM sys.objects WHERE is_ms_shipped = 0)
GROUP BY OBJECT_NAME(d.referencing_id), d.referenced_entity_name
ORDER BY Von, Nach;
PowerShell
sqlcmd -S "(localdb)\MSSQLLocalDB" -E -d Auftragsbuch -b -i scripts/Inventur-Datenbank.sql -W
Art Name GehoertZu
--- ---- ---------
SQL_STORED_PROCEDURE WartungFaellig 
SQL_TRIGGER trg_Auftraege_WartungErledigt Auftraege
USER_TABLE Auftraege 
USER_TABLE Kunden 
USER_TABLE Wartungsplan 
Von Nach
--- ----
CK_Auftraege_Art Auftraege
CK_Auftraege_Status Auftraege
CK_Wartungsplan_Intervall Wartungsplan
trg_Auftraege_WartungErledigt deleted
trg_Auftraege_WartungErledigt inserted
trg_Auftraege_WartungErledigt Wartungsplan
WartungFaellig Kunden
WartungFaellig Wartungsplan

Zwei Dinge fallen auf. Der Trigger trg_Auftraege_WartungErledigt hängt an Auftraege und ändert Wartungsplan, und kein Teil des Programms ruft ihn auf: Er läuft von allein, wenn eine Zeile sich ändert. Wer die Tabelle Auftraege später in eine andere Datenbank oder in ein anderes Programm legt, nimmt diese Regel unbemerkt mit oder verliert sie. Und die Sicht kennt nur Abhängigkeiten innerhalb der Datenbank: Wie das Programm die Prozedur WartungFaellig aufruft, steht dort nicht.

Was die Skripte nicht sehen

Jedes Werkzeug sucht nach dem, was Sie ihm sagen. Der Rest bleibt unsichtbar:

Bruchstelle Warum das Skript sie nicht findet
B5 Preisregeln im Formular Es sind gewöhnliche C#-Methoden. Kein Muster verrät, dass sie Geschäftsregeln sind.
B6 doppelte Regel „nächste Wartung“ Eine Zeile AddMonths sieht harmlos aus. Dass die Prozedur dasselbe tut, steht an anderer Stelle.
B10 feste Schrift und Maße Das Skript sucht keine Schriftgrößen.
B11 globaler Zustand Program.Daten ist ein Eigenschaftsname, kein Fehler.
Wer bedient was, wann Steht in Köpfen, nicht im Code.

Das ist kein Mangel des Skripts, sondern die Natur einer Inventur aus dem Quelltext. Deshalb gehört das Gespräch dazu: Fragen Sie Frau Aydin und Herrn Kowalski, wann sie was benutzen, und was sie tun, wenn das Programm nicht läuft. Notieren Sie, was Sie hören.

Die Landkarte

Die Landkarte zeigt, wie das Programm zusammenhängt und wo es die Außenwelt berührt. Ein Diagramm spart Seiten Text. Es gehört in docs/landkarte.md und wird als Mermaid gezeichnet, damit es im Repository und im Review sichtbar ist. Pfeile bedeuten „ruft auf“ oder „liest“. Die Außenwelt steht rechts.

mermaid
flowchart LR
    subgraph Programm["Brandt.Auftragsbuch.exe"]
        P[Program]
        H[HauptForm]
        K[KundenForm]
        W[WartungsForm]
        D[AuftragsDaten]
        R[Rechnungsdruck]
        X[Exporte]
        E[Einstellungen]
        B[Berechtigung]
    end
    P --> D
    P --> H
    H --> K
    H --> W
    H --> R
    H --> X
    H --> D
    H --> B
    K --> D
    W --> D
    W --> X
    R -->|Preisregeln| H
    X -->|Preisregeln| H
    D --> E
    X --> E
    B --> E
    D --> SQL[("SQL Server: Tabellen, Prozedur, Trigger")]
    X --> NL["Netzlaufwerk Buchhaltung"]
    H --> REG["Registry des Benutzers"]
    P --> EL["Ereignisprotokoll"]
    B --> WIN["Windows-Anmeldung und Gruppen"]
    K --> WB["Browser-Steuerelement"]
    R --> DR["Drucker"]

Lesen Sie das Diagramm auf Auffälligkeiten. Zwei Pfeile fallen auf: Rechnungsdruck und Exporte rufen das Hauptformular auf, um Preise zu berechnen. Ein Formular ist die Quelle von Geschäftsregeln, und die zwei anderen Klassen hängen davon ab. Das ist eine Naht, die Sie in der nächsten Lektion suchen werden. Und AuftragsDaten hängt an fast allem: ein dichter Knoten, an dem später viel hängt.

Die Bewertung

Jede Abhängigkeit bekommt eine Einstufung in drei Stufen: gering (ein Paket oder eine Einstellung genügt), mittel (eine Ersatztechnik und etwas Umbau) und hoch (die Bauart ändert sich). Die Stufen sind ein Urteil des Teams, keine Messung. Schreiben Sie dazu, woher Sie es wissen: aus Microsofts Dokumentation, aus einem Versuch oder aus Erfahrung.

Abhängigkeit Stufe Begründung
Windows Forms gering Windows Forms gibt es auf .NET, nur unter Windows (Dokumentation); das Projekt braucht das Ziel net10.0-windows.
App.config gering App.config funktioniert in .NET weiter über das Paket System.Configuration.ConfigurationManager (Dokumentation); der Umstieg auf appsettings.json ist ein späterer Schritt.
Registry, Ereignisprotokoll gering Beides deckt das Windows Compatibility Pack ab (Dokumentation).
Browser-Steuerelement mittel Für das WPF-Steuerelement nennt die Dokumentation den Ersatz WebView2; für Windows Forms gilt es in Lektion M2.3 zu prüfen.
Drucken, Zeichnen mittel zu prüfen in Lektion M2.3; später ersetzt eine PDF-Erzeugung das Papier.
Netzlaufwerk mittel im Web nicht verfügbar; Ersatz ist ein Speicher in der Cloud (Lektion M4.5).
DataSet und Adapter hoch liegt unter jedem Formular; wird mit der Logik herausgelöst (Lektion M3.2).
Windows-Anmeldung hoch im Web gibt es sie so nicht; Ersatz ist Microsoft Entra ID (Modul M5).
Preisregeln im Formular hoch müssen zuerst herausgelöst werden, bevor etwas anderes sie nutzt (Lektion M3.1).
Trigger und Prozedur hoch unsichtbar für das Programm; müssen mit den Daten gedacht werden (Lektion M3.3).

Was kann schiefgehen

  • Das Skript wird als Beweis genommen. „Das Skript hat nichts zu Remoting gefunden“ heißt nur, dass die Muster nichts fanden. Lesen Sie die Muster, bevor Sie dem Ergebnis trauen.
  • Alles gleich wichtig. Eine Landkarte mit 40 Kästchen ohne Bewertung hilft niemandem. Bewerten Sie, und lassen Sie Unwichtiges weg.
  • Nur der Quelltext. Der Export am Freitag, die Gruppe der Disposition und das Netzlaufwerk stehen in der Umgebung, nicht im Code.
  • Die Stufen werden Zahlen. „Mittel“ bleibt ein Urteil. Wer es in Tage umrechnet, erfindet eine Genauigkeit, die es nicht gibt.
  • Die Datenbank wird übersehen. Ein Trigger, den kein Programm aufruft, ist die Regel, die zuerst verloren geht.
  • Das Werkzeug ersetzt die Prüfung. Auch die Bewertung der Copilot-Modernisierung ist ein Vorschlag. Vergleichen Sie ihn mit Ihrer Landkarte und klären Sie jeden Unterschied.

Übung 1

Legen Sie scripts/Inventur.ps1 und scripts/Inventur-Datenbank.sql in Ihrem Repository an und führen Sie beide gegen Ihr Auftragsbuch aus. Legen Sie die Ausgaben in docs/inventur.txt ab. Vergleichen Sie danach die Treffer des Skripts mit den zwölf Bruchstellen aus Lektion M1.2: Welche findet das Skript, welche nicht?

So prüfen Sie Ihr Ergebnis: Sie haben eine Liste mit zwei Spalten, „vom Skript gefunden“ und „nur beim Lesen gefunden“, und zu jeder Bruchstelle in der zweiten Spalte steht, warum das Skript sie nicht sieht.

Lösung 1

Die Ausgaben sehen aus wie oben gezeigt. Der Vergleich:

Bruchstelle Vom Skript gefunden? Warum
B1 klassisches Projekt ja Spalte „Format: klassisch“ und Paket aus packages.config
B2 App.config ja Muster ConfigurationManager
B3 Windows-Anmeldung ja Muster WindowsIdentity
B4 DataSet ja Muster SqlDataAdapter, DataSet, DataTable
B5 Preisregeln im Formular nein gewöhnliche Methoden, kein Muster
B6 doppelte Regel nein, nur die Datenbankobjekte die Prozedur und der Trigger erscheinen in der Abfrage; dass Exporte dieselbe Regel enthält, nicht
B7 Druck ja Muster PrintDocument, System.Drawing
B8 Netzlaufwerk, Kodierung ja Muster für UNC-Pfade und Encoding.GetEncoding(
B9 Browser-Steuerelement ja Muster WebBrowser
B10 feste Schrift nein kein Muster dafür
B11 globaler Zustand nein Program.Daten ist ein gewöhnlicher Name
B12 Registry, Ereignisprotokoll ja Muster Microsoft.Win32, Registry. und EventLog

Das Skript findet also acht der zwölf Bruchstellen, und genau die vier, die es nicht findet, sind die, die bei einer Modernisierung am meisten Arbeit machen: Regeln und Zustand. Das ist die wichtigste Erkenntnis der Inventur.

Übung 2

Schreiben Sie docs/landkarte.md. Sie enthält: (1) eine Zusammenfassung in drei Sätzen, (2) die Projekte und Pakete als Tabelle, (3) die Landkarte als Mermaid-Diagramm, (4) die Bewertung der Abhängigkeiten als Tabelle mit Stufe und Begründung, (5) die Datenbankobjekte und (6) offene Fragen an Frau Aydin und Herrn Kowalski.

So prüfen Sie Ihr Ergebnis: Jede Abhängigkeit aus dem Skript steht in der Bewertung, jede Stufe hat eine Begründung, und in den offenen Fragen steht mindestens eine, die nur ein Mensch beantworten kann.

Lösung 2

markdown
# Landkarte: Brandt.Auftragsbuch

## Zusammenfassung

Das Auftragsbuch ist ein Windows-Forms-Projekt im klassischen Format auf .NET Framework 4.8 mit einem Paket (Newtonsoft.Json). Es greift
mit einem DataSet direkt auf drei Tabellen, eine Prozedur und einen Trigger zu. Das Skript findet zehn Abhängigkeiten; die
Geschäftsregeln stehen in einem Formular und zum Teil doppelt in der Datenbank.

## Projekte und Pakete

| Projekt | Format | Ziel | Art |
| --- | --- | --- | --- |
| Brandt.Auftragsbuch | klassisch | v4.8 | WinExe |

| Paket | Version |
| --- | --- |
| Newtonsoft.Json | 13.0.3 |

## Landkarte

(das Mermaid-Diagramm aus der Lektion)

## Bewertung

(die Tabelle aus der Lektion, mit Stufe und Begründung)

## Datenbank

Tabellen Kunden, Auftraege, Wartungsplan; Prozedur WartungFaellig; Trigger trg_Auftraege_WartungErledigt (schiebt den Wartungsplan
weiter, wird von keinem Programmteil aufgerufen).

## Offene Fragen

- Frau Aydin: Welche Preisregeln gelten außer den vier im Formular? Gibt es Sonderpreise für einzelne Kunden?
- Herr Kowalski: Wer liest das JSON des Wartungsplans auf dem Tablet, und was geschieht, wenn die Datei fehlt?
- Herr Brandt: Läuft der Export nach Plan oder auf Zuruf, und wer prüft ihn?

Die Landkarte ist ein Arbeitsstand. Sie wird mit jeder Lektion genauer; Nähte und Reihenfolge folgen in der nächsten.

Was Sie jetzt können

  • Sie erfassen Projekte, Pakete, Windows-Abhängigkeiten und Datenbankobjekte mit einem Skript und einer Abfrage.
  • Sie wissen, was solche Werkzeuge nicht finden, und fragen die Menschen, die das Programm bedienen.
  • Sie ordnen die Hilfswerkzeuge von Microsoft ein und prüfen ihre Ergebnisse.
  • Sie zeichnen eine Landkarte und bewerten jede Abhängigkeit mit Begründung.

In der nächsten Lektion suchen Sie in der Landkarte die Nähte: die Stellen, an denen sich das Programm trennen lässt.

Weiterlesen