Erweiterte Programmierung


Sie können die bestehende iPlus-Datenbank, um zusätzliche Tabellen und Felder erweitern. Da Sie die Entity-Framework-Modelle aus gip.core.datamodel oder gip.mes.datamodel nicht verändern können, müssen Sie Ihr eigenes Modell in Ihrer Custom-Assembly hinzufügen.

Sie können natürlich auch eine ganz eigene Datenbank erstellen und iPlus verwenden. Es spielt jedoch keine Rolle welches der beiden Varianten Sie bevorzugen, die Arbeitsschritte sind in beiden Fällen gleich:

  1. Eine neue Assembly definieren.
  2. Ein Entity-Framework-Datenmodell generieren.
  3. T4-Template generiren und austauschen.
  4. Kompilierte Modelle und Modellaktualisierungen
  5. Datenbankkontext-Klasse erweitern um iPlus-Features.
  6. Updates mittels SQL-Skripten bereitstellen.
  7. Partielle Entity-Klassen hinzufügen.
  8. Modellaktualisierungen

Hinweis: Bei einem wechsel von EF4 auf EFCore muss das Skript dbsync_2023-05-15_00-01_iplusV5.sql und dbsync_2023-03-31_00-01_reportHandlerV5.sql zuerst ausgeführt werden.

 

Dieses Beispielprojekt können Sie im Download-Bereich herunterladen


Fügen Sie mittels Visual-Studio ein neues Assembly-Projekt hinzu (.NET 7). 

Bitte beachten Sie die iPlus-Namenskonventionen bei der Benennung Ihrer Assembly, weil sie sonst nicht beim Registriervorgang erkannt wird. (Der Registriervorgang wird beim Login ausgeführt, indem Sie die Strg-Taste gedrückt halten und auf den Login-Button drücken). Wir empfehlen den Dateinamen mit "*.datamodel.dll" enden zu lassen.

Im Beispielprojekt haben wir sie mycompany.package.datamodel.dll genannt.


Bevor Sie weiterlesen machen Sie sich bitte mit dem Erstellen von Entitiy-Framework-Datenmodellen vertraut.

Danach legen Sie das Modell mittels "Database-First-Ansatz" und "Reverse-Engineering" an, weil dies von iPlus unterstützt wird. Sie können auch den Code-First-Ansatz verwenden, müssen jedoch dann selbst für die Migrierung sorgen. Die Migration bzw. die automatische Aktualisierung der Datenbanktabellen ist in iPlus mittels SQL-Skripten vorgesehen, das später im Abschnitt "E. Updates mittels SQL-Skripten bereitstellen" näher beschrieben ist. 

Im vorigen Kapitel wurde kurz auf die Klasse ACObjectContextHelper eingegangen. Sie ist eine Hilfsklasse zur Erweiterung der "Microsoft.EntityFrameworkCore.DbContext"-Klasse, um iPlus-Features (wie z.B. Längenprüfungen, Min/Max-Werte, Automatisches Setzen von Zeitstempeln, Default-Werte, Erweiterte Referenzprüfungen beim Löschen...). 


Vor der Verwendung der Befehle zur Installation der T4-Vorlagen müssen die Dotnet-Tools installiert sein. Sie können sie mit folgendem Befehl in der .NET CLI installieren:

dotnet tool install --global dotnet-ef

Mit diesem Befehl wird mmer die aktuellste Version von EF-Tools installiert:

https://learn.microsoft.com/de-de/ef/core/get-started/overview/install

Sie sollten aber immer nur die Version verwenden, die mit der aktuellen .NET-Core Framework-Version bzw. EF-Core-Version übereinstimmt:

dotnet tool update dotnet-ef --version 8.0.11 --global

Deinstallationsbefehl:

dotnet tool uninstall dotnet-ef --global

 

Bei Verwendung des Database-First-Ansatzes müssen Sie das T4-Template paket installieren und die Template zum Projekt hinzufügen. Sie können das Paket mit folgendem Befehl in der .NET CLI installieren:

dotnet new install Microsoft.EntityFrameworkCore.Templates

Nach der Installation des T4-Template paket wird das Ausführen dieses Befehls die T4-Template erstellen:

dotnet new ef-templates

Die T4-Templates werden im Lösungsverzeichnis erstellt.:

  • CodeTemplates/
    • EFCore/
      • DbContext.t4
      • EntityType.t4

Wenn Sie das Verzeichnis für die erstellten Vorlagen ändern möchten, können Sie die Befehlsoption --output <output> verwenden. Dadurch können Sie ein anderes Ausgabeverzeichnis angeben.

Nachdem die T4-Templates erstellt wurden, ersetzen Sie sie durch die iPlus T4-Templates aus dem Beispielprojekt. Diese Templates definieren, wie die Entitäten aus der Datenbank erstellt werden.

Fühlen Sie sich frei, die Vorlagen entsprechend Ihren spezifischen Anforderungen anzupassen. Derzeit erstellen sie die Entitäten und die Context-Klasse, sodass sie mit iPlus kompatibel sind. Sie können sie auch debuggen, indem Sie der offiziellen Anleitung von Microsoft folgen.

Um die Vorlagen auszuführen, verwenden Sie den Befehl Scaffold-DbContext in der Package Manager Console. Stellen Sie sicher, dass das Paket Microsoft.EntityFrameworkCore.Design im Projekt installiert ist, in dem Sie scaffolding durchführen möchten, sowie das relevante Datenbankanbieter-Paket.

Im Beispielprojekt wurde der Befehl zum Erstellen der Entitäten wie folgt verwendet:

Scaffold-DbContext "Server=.\;Database=ExampleV4;Trusted_Connection=True;Encrypt=False" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -UseDatabaseName

Nachdem Sie diesen Befehl ausgeführt haben, müssen Sie die Verbindungszeichenfolge zur Datenbank angeben, z.B. Server=.\;Database=ExampleV4. Dann erklären Sie, dass die Verbindung vertrauenswürdig ist und keine Verschlüsselung verwendet wird. Anschließend geben Sie den Datenbankanbieter an, der in unserem Fall Microsoft.EntityFrameworkCore.SqlServer ist. Wir verwenden außerdem den Parameter -UseDatabaseNames, um sicherzustellen, dass der Scaffolder die originalen Datenbanknamen für Spalten und Tabellen verwendet.

 


Aufgrund der T4-Textvorlage und dem Entity-Wizzard-Tool sollten Sie sich an folgende Konventionen halten, damit Ihre späteren LINQ-Abfragen für andere Entwickler gut lesbar sind:

  1. Verwenden Sie Primärschlüssel vom Typ (GUID / uniqueidentifier) (1).
  2. Benennen Sie den Primärschlüssel so wie den Tabellennamen und fügen "ID" am Ende hinzu. z.B. "InOrderPosID" (2).
  3. Fremdschlüssel sollten so benannt werden, dass sie mit dem Namen des Primärschlüssels der referenzierten Tabelle enden (3).
  4. Optional können Sie kaskadiertes Löschen verwenden (z.B. Kopf- und Positionstabellen) (4).

  5. iPlus enthält einen T4-Vorlagenpluralisierer, der die Namen der Entitätsklassendateien mit den Entitätsnamen abstimmt. Sie haben die Flexibilität, den Pluralisierer an Ihre spezifischen Anforderungen anzupassen.
  6. In Fällen, in denen der Entitätsname im Plural endet, ist es wichtig, darauf zu achten, dass der T4-Vorlagenpluralisierer den Tabellennamen entsprechend ändern kann. Daher sollten Sie ihn bei Bedarf anpassen, um sicherzustellen, dass er Ihren Vorlieben und Anforderungen entspricht.

Um die iPlus-Features hinzuzufügen, legen Sie eine neue C#-Datei mit einer neuen Klasse an, die von der Klasse erbt, die das T4-Template generiert hat. In unserem Beispiel die Klasse MyCompanyDB, die von MyCompanyEntites erbt:

Versehen Sie MyCompanyEntites  mit der ACClassInfo-Attributklasse vom Typ "Global.ACKinds.TACDBAManager". Mit dieser Einstellung geben Sie Ihre neue Datenbank-Kontext-Klasse dem iPlus-Framework bekannt, damit sie in der iPlus-Entwicklungsumgebung im iPlus-Anwendungsbaum erscheint (Nachdem Sie iPlus mit der Strg-Taste gestartet haben). 

(Falls Sie Ihr Datenbankmodell mit dem Model-First-Ansatz erstellen, dann müssen Sie ebenfalls Ihre Kontextklasse mit der ACClassInfo-Attributklasse versehen wie zuvor beschrieben.)

MyCompanyEntites muss zudem die Schnittstelle gip.core.datamodel.IACEntityObjectContext implementieren. Instanzieren Sie dazu die Hilfsklasse ACObjectContextHelper. Die Methoden und Eigenschaften diese Klasse sind exakt gleich benannt, wie die in der IACEntityObjectContext-Schnittstelle. Somit müssen Sie nur noch die Methodenaufrufe in Ihrer Klasse an die ACObjectContextHelper-Instanz delegieren und Sie haben somit Ihren Kontext um iPlus-Featurs erweitert.

(Die Nutzung der iPlus-Features (ACObjectContextHelper) ist beim Model-First-Ansatz nicht möglich und daher müssen Sie die Schnittstelle auch selbständig implementieren)

 

Nachdem Sie die T4-Templates ausgeführt haben, wird die Hauptkontextklasse nach dem Datenbanknamen + "Context" benannt. Im bereitgestellten Beispielcode wurde die Kontextklasse "ExampleV5Context" genannt und wie folgt deklariert:

public partial class ExampleV5Context : DbContext

Kompilierte Modelle

iPlus verwendet kompilierte Modelle für die Leistungsvorteile (bis zu 10-mal bessere Leistung und verkürzte Startzeit). Um kompilierte Modelle zu verwenden, müssen Sie den Befehl in der .NET CLI ausführen.

Vor dem Ausführen des Befehls ist es notwendig, Anpassungen am Code innerhalb der Kontextklasse vorzunehmen, die von den T4-Vorlagen generiert wird. In der Methode OnConfiguring() dieser Klasse stellen Sie sicher, dass die Methode .UseSqlServer() auskommentiert und die Methode UseModel() auskommentiert ist. Anschließend ändern Sie die Verbindungszeichenfolge. Dieser Schritt ist entscheidend, da das Entity Framework auf die Kontextklasse angewiesen ist, um die für die Erstellung der kompilierten Modelle verwendete Datenbankverbindung zu bestimmen, und das Modell ist zu diesem Zeitpunkt noch nicht erstellt.

Der folgende Befehl dient als Referenz aus dem Beispielprojekt. In Ihrem individuellen Projekt müssen Sie jedoch die Parameter für die Projekt, Kontext, und Namespace Optionen an Ihre spezifischen Anforderungen anpassen:

NET CLI: dotnet ef dbcontext optimize --output-dir CompiledModels --namespace mycompany.package.datamodel --project mycompany.package.datamodel --context ExampleV5Context

Nach erfolgreicher Ausführung des Befehls ist es notwendig, die Verbindungszeichenfolge auszukommentieren und die Methode zu aktivieren, die für die Verwendung der kompilierten Modelle verantwortlich ist.

 


Modellaktualisierungen

Immer wenn das Modell geändert oder aktualisiert wird, ist es notwendig, den Scaffold-Befehl und dann den Optimize-Befehl auszuführen. Dieses Mal müssen Sie jedoch den Parameter --force mit dem Scaffold-Befehl verwenden, um die vorhandenen Klassen zu überschreiben. Dieser Vorgang wird automatisiert mit einem iPlusModelUpdate.bat Batch-Skript, das Sie ausführen können. Das Skript befindet sich im iPlus-Ordner, und beim Ausführen des Skripts werden die Befehle ausgeführt. Sie können das Skript nach Ihren Bedürfnissen anpassen, und es verwendet PowerShell-Befehle, um die Verbindungszeichenfolge zu dekommentieren, sodass der Vorgang vollständig automatisiert ist

Note:

Wenn in Ihrem Code eine Ausnahme oder ein Fehler auftritt, wird der Scaffold-Prozess nicht ausgeführt. In solchen Fällen haben Sie die Möglichkeit, den Parameter --no-build beim Scaffold-Befehl zu verwenden. Dieser Parameter verhindert, dass das Projekt während des Scaffold-Vorgangs automatisch kompiliert wird.


Wenn Sie Änderungen in Tabellen, Feldern und Relationen durchgeführt und Ihr edmx-Modell aktualisiert haben, ist Ihre Software bei anderen Kunden noch nicht funktionsfähig, weil die dortige Installation ebenfalls auf den neuesten Datenbankstand gebracht werden muss.

Diese Aufgabe übernimmt das Update-System von iPlus für Sie. Vor dem eigentlichen Assembly-Registriervorgang, den Sie mit der Strg-Taste beim iPlus-Login starten, können SQL-Skripte automatisiert ausgeführt werden. Diese Aktualisierungslogik ist in der Assembly "gip.core.dbsyncer.dll" implementiert. Damit dies funktioniert müssen folgende Einstellungen gemacht werden:

 

  1. Legen Sie einen Ordner mit dem Namen "DbScripts" an.
  2. Legen Sie einen Unterordner an, der mit einem maximal 10-stelligen Namen benannt wird. In dem Beispiel oben "MyCompDB".
  3. Legen Sie eine info.xml-Datei in diesem Ordner mit folgendem Inhalt an:

 

 

4. Tragen Sie in das Element <DbSyncerInfoContextID> den Namen des Ordners ein, indem sich diese xml-Datei befindet.

5. In das Element <ConnectionName> tragen Sie den Namen des Entity-Containers ein, der auch dem Klassennamen entspricht, den der T4-Generator erzeugt hat.

6. Im <Name>-Element können Sie einen beliebigen Beschreibungstext eintragen.

7. Die info-xml muss immer beim Build in das bin-Verzeichnis kopiert werden. Deswegen stellen Sie die Eigenschaft "In Ausgabeverzeichnis kopieren" auf "Immer kopieren" oder "Kopieren wenn neuer".

 

Ab diesem Moment ist nun Ihr Projekt updatefähig. Jede künftige Datenbank-Änderung, die Sie auf Ihrem Entwicklungssystem durchführen, müssen als ein SQL-Skript in Ihrem Projektordner hinzufügen. Stellen Sie es ebenfalls wie die info.xml auf "Immer kopieren", damit das SQL-Skript in das Build-Verzeichnis kopiert wird für das spätere Deployment. Wenn es verschiedenen edmx-Modelle gibt, die jedoch mit der gleichen Datenbank arbeiten, müssen die Scripte Kontextübergreifend miteinander synchronisiert werden. Damit der DB-Syncer (gip.core.dbsyncer.dll) dies in der richtgen Reihenfolge durchführt, müssen die SQL-Skript-Dateinamen nach einem definierten Namensschema vergeben werden, die einen exakten Zeitstempel enthalten:

dbsync_YYYY-MM-DD_hh-mm_developername.sql

Der developername kann durch den tatsächlichen Entwickler-, Abteilungs- oder Firmennamen ersetzt werden. Dieser taucht dann in der SQL-Änderungshistorie auf die in der Tabelle [@DbSyncerInfo] gespeichert ist:

 

Die Spaltennamen in der Tabelle [@DbSyncerInfoContext] entsprechen den Element-Namen in der info.xml-Datei. (Das Feld Order hat keine Relevanz, sie können eine beliebige Zahl verwenden).

 

Wichtig: ConnectionStrings.config = SQL-Skripte!

Die ConnectionString.config muss für jedes edmx-Modell bzw. für jeden Entity-Container-Namen einen eigenen <add>-Abschnitt haben. Für "gip.core.datamaodel.Database" ist der Entity-Container-Name "iPlusV4_Entities". Für "gip.mes.datamodel.DatabaseApp" ist es "iPlusMESV4_Entities".

SQL-Update-Skripte werden auf der Datenbank ausgeführt die in dem entsprechenden Connectionstring eingestellt ist. Damit steuern Sie, ob Sie eine von iPlus getrennte Datenbank verwalten bzw. aktualisieren möchten oder ob Sie Ihre Custom-Tabellen in die iPlus-Datenbank hinzufügen möchten! In beiden Fällen wird jedoch die Änderungshistorie in die iPlus-Datenbank geschrieben!

 


Der Entity-Framework-Generator generiert Entity-Frameworkklassen anhand der T4-Vorlage folgende Klassen-Signatur:

public partial class Material : VBEntityObject, IInsertInfo, IUpdateInfo, IDeleteInfo

gip.core.datamodel.VBEntityObject ist die Basisklasse für alle Entity-Klassen, damit die zusätzlichen iPlus-Features genutzt werden können. Sie spezifiziert eine Reihe von virtuellen Methoden und gemeinsamen Funktionalitäten, die in den Ableitungen überschrieben bzw. wiederverwendet werden können. Die Schnittstellen IInsertInfo, IUpdateInfo und IDeleteInfo werden automatisch hinzugefügt, weil das T4-Template im edmx-Modell die Entities nach den Feldern "InsertDate, InsertName, UpdateName..." untersucht.

Den restlichen Programmcode programmieren Sie dann in einer weiteren C#-Datei wo sie die partielle Klasse erweitern (Siehe F im ersten Bild oben). Die Partielle Klasse sollte mit den folgenden farblich umrandeten Code-Bereichen erweitert werden:

 

Partielle Klassendefinition (A)

Fügen Sie eine partielle Klassendefinition hinzu.

 

Klassen und Eigenschaftsbeschreibung mittels Attributklassen 

(A) & (B): Fügen Sie eine ACClassInfo-Attributklasse (A) mit Global.ACKinds.TACDBA (B) hinzu. Damit geben Sie diese Entity-Klasse dem iPlus-Framework bekannt. Sie erscheint dann in der Entwicklungsumgebung unterhalb der zugehörigen Datenbankkontextklasse im iPlus-Anwendungsbaum (Das gilt auch für den Model-First-Ansatz).

(C): Da die Eigenschaften der Entity-Klasse in der anderen partiellen Klasse dekariert sind, die vom T4-Template-Generator erstellt wurde, kann das ACClassPropertyInfo-Attribut nicht dort deklariert werden, weil sie durch ein erneutes Ausführen des T4-Templates wieder entfernt wird. Stattdessen verwenden Sie die ACPropertyEntity-Attributklasse (C). Für jedes Datenbankfeld bzw. Eigenschaft der Entity-Klasse deklarieren Sie eine ACPropertyEntity-Zeile und geben im ersten Parameter den Namen der Eigenschaft an. Die Restlichen Kontruktionsparameter sind dieselben wie auch bei der ACClassPropertyInfo-Klasse. Bei String-Eigenschaften (SQL-Typ varchar) geben Sie die maximale Länge des Strings mit "MaxLength" an.

Falls Sie jedoch den Model-First-Ansatz verwenden (indem Sie auf die iPlus-Features verzichten) können Sie Ihre Eigenschaften ganz normal mit dem ACClassPropertyInfo-Attribut versehen.

(D): Optional: Definieren Sie ein Template für die Speicherbaren Abfragen (ACQueryDefinition). Eine Speicherbare Abfrage sollte immer dann definiert werden, wenn die Tabelle (bzw. die Entity-Klasse) in Businessobjekten die Haupttabelle ist nach der gefiltert und navigiert wird.

(F): Optional: Definieren Sie ein ACSerializable-Attribut, damit ein Intelligenter-Zeiger auf ein Objekt dieser Entity-Klasse in einer netzwerkfähigen Eigenschaft verwendet werden kann.

 

Implementierung von VBEntityObject

VBEntityObject besitzt einige virtuelle Methoden mit einer Standardimplementierung für das Hinzufügen, Löschen und Ändern von Objekten. Aufgrund von Entity Framework Core und kurzlebigen Kontexten ist es wichtig, dass jedes Entitätsobjekt von VBEntityObject erbt, da es die Eigenschaft des langanhaltenden Kontexts und andere wichtige Eigenschaften besitzt, die für die Kompatibilität als EntityKey entscheidend sind. Überschreiben Sie diese, wenn Sie Ihre individuelle Logik hinzufügen möchten und rufen Sie am Ende der Methode die Basis-Implementierung auf.

(A): Für das Hinzufügen von neuen Objekten deklarieren Sie eine statische Methode mit folgender Signatur:

public static MyEntityClassT NewACObject(MyDbContextClass dbApp, IACObject parentACObject, string secondaryKey)

In dieser Methode erzeugen Sie dann eine neue Instanz der Entity-Klasse (MyEntityClassT) und setzen den Primärschlüssel mit einer neuen GUID (B).

(C): Rufen Sie anschließend die Erweiterungs-Methode DefaultValuesACObject() auf, damit alle Eigenschaften Ihrer Entity-Klasse bzw. generell eines IACObject's mit den Default-Werten initialisiert wird, die in der Entwicklungsumgebung konfiguriert worden sind.

(D): Falls Ihre Tabelle einen Sekundärschlüssel besitzt (Ein Datenbankfeld mit einem leserlichen Schlüssel) können Sie diesen über den Nummerngenerator generieren lassen und die neue Nummer dem "secondaryKey"-Parameter übergeben. Die Nummernkreise des Nummerngenerators können Sie mit dem Businessobjekt vorab gip.bso.iplus.BSONoConfiguration konfigurieren.

(E): Rufen Sie die SetInsertAndUpdateInfo()-Methode auf um den Usernamen und die aktuelle Uhrzeit in die "Insert"-Felder eintragen zu lassen.

Achtung: Das Objekt befindet sich noch nicht im Change-Tracking! Entweder fügen Sie das neu generierte Objekt sofort in den DB-Kontext hinzu oder machen Sie das außerhalb der NewACObject()-Methode.

Wichtige Änderungen in Version V5: Wenn Sie Eigenschaften zur teilweisen Entitätsklasse hinzufügen, ist es wichtig, das [NotMapped]-Attribut hinzuzufügen. Entity Framework Core wird die Eigenschaften standardmäßig aufgrund dieses Attributs nicht in die Datenbank mappen.