Ein Add-on für pitFM erstellen
Das hier beschriebene Beispielprojekt ist vollständig unter pit Azure Devops verfügbar. In diesem Repository kann man sich das Projekt ansehen und die benötigten Dateien herunterladen. Die benötigten Dateien sind in diesem How-to auch separat verlinkt.
Kurze Einführung
pitFM unterstützt Add-ons, welche Zusatzfunktionalität in Klassenformeln zur Verfügung stellen können. Diese Add-ons können mit .Net Framework 4.7.2 in C# geschrieben werden.
Voraussetzungen
- Visual Studio 2022
- Git
Projekt erstellen
Das Visual Studio öffnen und ein neues Projekt erstellen. Es muss Klassenbibliothek (.NET Framework) ausgewählt werden.

Die Klassenbibliothek mit .NET und .NET Standard wird von pitFM nicht unterstützt.

Nach Auswahl der Klassenbibliothek muss der Name des Projekts und der Projektmappe(Solution) vergeben werden.
Als Projektname wird der zukünftige Add-on name angegeben. Die Projektmappe bekommt den selben Namen.

Dateiversionierung initialisieren
PS C:\source\addons\pitFM_Example> git init
Reinitialized existing Git repository in C:/source/addons/pitFM_Example/.git/
PS C:\source\addons\pitFM_Example> dir -force
Verzeichnis: C:\source\addons\pitFM_Example
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--h-- 27.10.2025 10:14 .git
d--h-- 27.10.2025 09:33 .vs
d----- 27.10.2025 10:12 pitFM_Example
-a---- 27.10.2025 09:33 1175 pitFM_Example.sln
Wichtige Dateien hinzufügen
- Die .gitignore in das Solutionverzeichnis kopieren.
Diese enthält vordefinierte Datei- und Verzeichnisnamen, welche bei einem git commit ignoriert werden sollen. - Die .editorconfig in das Solutionverzeichnis kopieren.
Diese enthält wichtige Style- und Syntaxregeln, welche die IDE und der Compiler beachten sollen. - Die Datei README.MD im Solutionverzeichnis erstellen.
TBD: Hier soll die Dokumentation für das Projekt erfolgen. Diese Datei wird als Pit-Add-on-Dokumentation verwendet. Azure DevOps zeigt diese Datei ebenfalls als Information für das Repository des Add-ons an. - Die nuget.config in das Solutionverzeichnis kopieren.
Diese enthält den Verweis auf die Azure DevOps Artifacts, welche pit interne Nuget-Pakete und weitere Ressourcen enthalten.
Erster Commit
Mit git status überprüfen, welche Dateien unchanged sind.
PS C:\source\addons\pitFM_Example> git status
On branch main
No commits yet
Untracked files:
.editorconfig
.gitignore
README.MD
nuget.config
pitFM_Example.sln
pitFM_Example/
Alle Dateien hinzufügen. Zuvor überprüfen, ob die .gitignore, .editorconfig und nuget.config im git status mit aufgeführt sind.
PS C:\source\addons\pitFM_Example> git add .
PS C:\source\addons\pitFM_Example> git status
On branch main
No commits yet
Changes to be committed:
new file: .editorconfig
new file: .gitignore
new file: README.MD
new file: nuget.config
new file: pitFM_Example.sln
new file: pitFM_Example/Class1.cs
new file: pitFM_Example/pitFM_Example.csproj
Dateien committen.
PS C:\source\addons\pitFM_Example> git commit -m "initial"
[main (root-commit) e3c78d6] initial
8 files changed, 661 insertions(+)
create mode 100644 .editorconfig
create mode 100644 .gitignore
create mode 100644 README.MD
create mode 100644 nuget.config
create mode 100644 pitFM_Example.sln
create mode 100644 pitFM_Example/Class1.cs
create mode 100644 pitFM_Example/Properties/AssemblyInfo.cs
create mode 100644 pitFM_Example/pitFM_Example.csproj
C# Projektdatei auf SDK Format umstellen
Vor diesem Schritt sollte das Add-on in der IDE gespeichert werden. Die IDE ist zu schließen, um Probleme beim Bearbeiten der Projektdatei zu vermeiden.
Die Projektdatei pitFM_Example.csproj in einem Texteditor öffnen und mit folgendem Text ersetzen. Dabei sind die Werte folgender Elemente anzupassen.
- RootNamespace: Als Root-Namespace ist immer pit.pitFm.Addons.[Projektname] zu verwenden
- AssemblyName: Die ist der Assembly- und gleichzeitig der Dateiname des Add-on. Das Namensschema ist aktuell pitFM_[FachlicherName].
- Product: Hier soll derselbe Name wie der AssemblyName verwendet werden.
- Copyright: Ist ggf. anzupassen.
- AssemblyTitle: Muss 2x im Projekt angepasst werden. Es wird ebenfalls der AssemblyName erwartet mit dem Zusatz, ob das Add-on als strong named Variante oder nicht als strong named erstellt wurde. Die Formulierung in der Klammern nicht verändern.
- Wichtig: Wenn die pit CliDefines nicht benötigt werden, weil das Add-on nicht mit pit Entitys arbeitet, sollte die Referenz in der Projekt-Datei wieder entfernt werden. Einfach nach CliModel suchen und Referenz entfernen.
Die Pakete CliModel und CliDefines können nur vom Nuget-Paketdienst gefunden werden, wenn diese nuget.config im Solutionverzeichnis liegt. siehe Wichtige Dateien hinzufügen
Die FileVersion und AssemblyVersion sind auf 0.0.0.1 zu lassen, da diese über die Azure Build Pipeline überschrieben werden. Dadurch wird bei der erstellten Add-on-DLL erkennen, dass diese ein Developer-Drop ist oder die Release-Pipeline ohne gültige Release-Version gebaut wurde.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<RootNamespace>pit.pitFm.Addons.Example</RootNamespace>
<AssemblyName>pitFM_Example</AssemblyName>
<!-- Assembly-Infos & Versionierung -->
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<Product>pitFM_Example</Product>
<Copyright>Copyright © pit-cup GmbH 2025</Copyright>
<Company>pit-cup GmbH</Company>
<FileVersion>0.0.0.1</FileVersion>
<AssemblyTitle>pitFM_Example (no StrongName)</AssemblyTitle>
<AssemblyVersion>0.0.0.1</AssemblyVersion>
<!-- Sprache/Build-Qualität -->
<LangVersion>8</LangVersion>
<Deterministic>true</Deterministic>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<!-- Build-Settings -->
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseWithStrongName|AnyCPU'">
<AssemblyTitle>pitFM_Example (StrongName)</AssemblyTitle>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\ReleaseWithStrongName\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\pit-Addon.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup>
<!-- Definiert den Paketnamen für das pit CliModel je nach Buildkonfiguration. Nur notwendig wenn CliModel wirklich im Addon verwendet wird. Entfernen wenn nicht benötigt. -->
<CliModelPackageName Condition="'$(Configuration)' == 'Debug'">PitFm.CliModel.noSN</CliModelPackageName>
<CliModelPackageName Condition="'$(Configuration)' == 'Release'">PitFm.CliModel.noSN</CliModelPackageName>
<CliModelPackageName Condition="'$(Configuration)' == 'ReleaseWithStrongName'">PitFm.CliModel</CliModelPackageName>
<!-- Definiert den Paketnamen für das pit CliDefines je nach Buildkonfiguration. -->
<CliDefinesPackageName Condition="'$(Configuration)' == 'Debug'">PitFm.CliDefines.noSN</CliDefinesPackageName>
<CliDefinesPackageName Condition="'$(Configuration)' == 'Release'">PitFm.CliDefines.noSN</CliDefinesPackageName>
<CliDefinesPackageName Condition="'$(Configuration)' == 'ReleaseWithStrongName'">CliDefines</CliDefinesPackageName>
</PropertyGroup>
<ItemGroup>
<!-- Referenz auf CliModel. Nur notwendig wenn CliModel wirklich im Addon verwendet wird. Entfernen wenn nicht benötigt. -->
<PackageReference Include="$(CliModelPackageName)" Version="26.0.0" />
<!-- Referenz auf CliDefines -->
<PackageReference Include="$(CliDefinesPackageName)" Version="21.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>
Jetzt muss das Verzeichnis Properties, mit seinem Inhalt, im Projektverzeichnis gelöscht werden.
Die Class1.cs kann ebenfalls gelöscht werden.
PS C:\source\addons\pitFM_Example> cd .\pitFM_Example\
PS C:\source\addons\pitFM_Example\pitFM_Example> rm -r .\Properties\
PS C:\source\addons\pitFM_Example\pitFM_Example> rm .\Class1.cs
Jetzt die Solution in der IDE öffnen und kompilieren. Es sollten keine Fehlermeldungen und Warnungen angezeigt werden. Falls es Probleme mit dem CliModel- oder CliDefines-Paket gibt, sollte ein nuget restore gemacht werden.
Änderungen in git stagen und committen.
PS C:\source\addons\pitFM_Example> git add .
PS C:\source\addons\pitFM_Example> git status
On branch main
Changes to be committed:
deleted: pitFM_Example/Class1.cs
deleted: pitFM_Example/Properties/AssemblyInfo.cs
modified: pitFM_Example/pitFM_Example.csproj
PS C:\source\addons\pitFM_Example> git commit -m "Migrate project to SDK-style with cleanup"
[main 8a6249b] Migrate project to SDK-style with cleanup
3 files changed, 55 insertions(+), 78 deletions(-)
delete mode 100644 pitFM_Example/Class1.cs
delete mode 100644 pitFM_Example/Properties/AssemblyInfo.cs
Addon implementieren
Pit Add-on Klasse erstellen
Hinweis
- Jede Datei enthält genau eine Typdefinition (z. B. Klasse, Interface, Enum, Record oder Struct).
- Der Dateiname entspricht exakt dem Typnamen.
- Typnamen beginnen mit einem Großbuchstaben und verwenden PascalCase.

Eine neue Klasse PitFmInterface im Projekt erstellen. Die Klasse muss als public static class definiert sein. Die Methode, welche im pitFM Klassenformel verwendet werden soll muss eine public static method sein. Siehe Codebeispiel.
namespace pit.pitFm.Addons.Example
{
public static class PitFmInterface
{
public static void ExampleInitialisieren()
{
}
}
}
Dem Projekt eine weitere Klasse [Example]Wrapper hinzufügen. Diese Klasse soll Instanziierbar, also nicht static sein. Die enthaltenen Methoden sollen public Member-Methoden sein.
namespace pit.pitFm.Addons.Example
{
public class ExampleWrapper
{
public void Initialisieren()
{
}
}
}
Anpassung der PitFmInterface Klasse
Damit das Add-on auch in pitIS funktioniert, muss es pro Thread(Pit-Session) eine eigene Instanz erhalten. Die kann nur mit intanziierbaren Klassen erreicht werden. Aus diesem Grund wird die Funktionalität des Add-ons in einem instanziierbaren Wrapper gekapselt.
public static class PitFmInterface
{
private static readonly ThreadLocal<ExampleWrapper> ExampleWrapperField =
new ThreadLocal<ExampleWrapper>(() => new ExampleWrapper());
private static ExampleWrapper ExampleWrapper => ExampleWrapperField.Value;
public static void ExampleInitialisieren()
=> ExampleWrapper.Initialisieren();
}
Die Klasse PitFmInterface und ExampleWrapper können als partial definiert werden. Dadurch kann diese Klasse auf mehrere Dateien aufgeteilt werden. Zu beachten ist, dass hier ein neuer Dateiname vergeben werden muss, der Klassenname jedoch gleich bleibt. Als Dateiname sollte beispielsweise PitFmInterfaceConversions verwendet werden PitFmInterface[Bezeichnung für fachliche Gruppierung]. Das Beispielprojekt im pit Azure Devops wendet diese Aufteilung an. Dort kann dies gern nachvollzogen werden.
Azure-Build-Pipelines definieren
Das Erstellen und Einbinden der Azure-Pipelines wird hier beschrieben.
Committen nicht vergessen ...
TBD Commitregeln...
- Erste Zeile ≤ 72 Zeichen, im Imperativ formuliert (z. B. „add support“, nicht „added“).
- Keine generischen Nachrichten wie „Update code“.
- Ein Commit pro kohärente Änderung (Clean Commit History).
- Optionale BREAKING CHANGE:-Angabe im Footer für Major-Release-Automatisierung.
Weitere Programmierregeln
Interfaces erhalten ein vorangestelltes „I“.
💡 Das Präfix „I“ bei Interfaces ist ein fester Bestandteil der C#-Namenskonventionen (vgl. .NET Design Guidelines).
✅ Beispiel:public interface IService { } public class Service : IService { }❌ Falsch:
public interface ServiceInterface { } // fehlendes I public class MyIService : ServiceInterface { } // "I" wird nicht in den Klassennamen übernommen.Variablen haben aussagekräftige, beschreibende Namen.
Ungarische Notation wird nicht verwendet.
💡 Sprechende Variablennamen fördern Lesbarkeit, vereinfachen Refactoring und reduzieren Fehlinterpretationen.
💡 Ungarische Notation widerspricht modernen Typinformationsmechanismen des Compilers und IDEs.
✅ Beispiele:int customerCount; string userName; DateTime lastLoginDate;❌ Falsch:
int iCustomerCount; // ungarische Notation string strName; // ungarische Notation var x; // nichtssagender Name
Was gehört alles in die README.MD
TBD ... Howto Readme.md von Microsoft Eine README.md ist der erste Eindruck deines Repositories und sollte sowohl Entwicklern als auch Reviewern helfen, den Zweck, Aufbau und die Nutzung des Projekts schnell zu verstehen. Im .NET-/C#-Kontext (besonders bei Libraries oder Desktop-Apps) hat sich folgender Aufbau bewährt
Kurzempfehlung
Halte die README so, dass ein neuer Entwickler in <5 Minuten versteht:
- Was macht das Projekt?
- Wie baue ich es?
- Wie benutze ich es?
- Wie trage ich bei?
Anti-Pattern
- README nur mit generischem Text ("Projektbeschreibung hier einfügen")
- Fehlende Hinweise zu Framework-Versionen oder Build-Anforderungen
- Keine Erwähnung der Domäne oder des Zwecks → erschwert DDD-Zuordnung
Regeln aus .editorconfig
Die genauen Regeln aus der .editorconfig und wie diese zu erfüllen sind, wird in "Die Coding-Regeln der .editorconfig" beschrieben.
Testprojekt erstellen
Das Erstellen des Testprojekts und Unittests wird in "UnitTests erstellen" beschrieben.