Erweiterte Programmierung


Sie können die bestehende iPlus-Datenbank, um zusätzliche Tabellen und Felder erweitern. Da Sie die Entity-Framework-Modelle (*.edmx) 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. HINWEIS: Wir unterstützen Entity Framework Core 7 (.NET 7) aber die Dokumentation ist noch nicht fertig.

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 austauschen.
  4. Datenbankkontext-Klasse erweitern um iPlus-Features.
  5. Updates mittels SQL-Skripten bereitstellen.
  6. Partielle Entity-Klassen hinzufügen.

 

 

Dieses Beispielprojekt können Sie im Download-Bereich herunterladen


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

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 "System.Data.Objects.ObjectContext"-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...). Aus diesem Grunde unterstützt iPlus, zur Zeit nur "Entity-Framework 4" für diese zusätzlichen Features und daher sollte der Entity-Framework-Generator in Visual-Studio nicht die DBContext-Klasse generieren. Die Codegenerierung wird im nächsten Abschnitt näher erläutert.

Wenn Sie also auf die iPlus-Validierungsfunktionalitäten, die automatisierte Kontextverwaltung und den Datenbankqueues verzichten möchten dann können Sie auch die neueren Versionen von Entity-Framework einsetzen.

In beiden Fällen Erstellen Sie erstmalig die "*.edmx"-Datei mit dem von Visual Studio bereitgestellten Generator für EF-6 (DBContext) und die edmx-Datei wird in den Projektbaum eingetragen.


Damit nun der Code-Generator nicht DBContext generiert, sondern ObjectContext müssen Sie die von Visual-Studio hinzugefügten tt-Templates im Baum löschen und stattdessen durch ein T4-Template für ObjectContext & iPlus-Framework ersetzen, das Sie aus dem Beispielprojekt kopieren können (mycompany.tt).

Bennen Sie das "mycompany.tt"-Template um in denselben Namen wie Ihre *.edmx-Datei (siehe Beispielbild oben). Öffnen Sie das T4-Template im Code-Editor und tragen ihren *-edmx-Dateinamen in die SourceCsdlPath-Eigenschaft ein:

 

Bevor Sie nun das T4-Template ausführen, sollten sie die Eigenschaftsnamen für Navigationseigenschaften in ein "besser leserliches Format" umwandeln. Dafür gibt es das Tool "gip.tool.entitywizzard.exe" im iPlus-Installationsverzeichnis. Starten Sie das Programm und wählen Ihre "*.edmx"-Datei aus:

 

Anschließend führen Sie die T4-Textvorlage aus.

 

Vorgehensweise beim Aktualisieren des Modells

Immer wenn Sie Ihr edmx-Modell aktualisiert haben gilt dieselbe Reihenfolge:

  1. Wenn Sie beim Speichern des edmx-Modells vom Generator gefragt werden, ob Sie das T4-Template ausführen wollen bestätigen sie mit "Nein".
  2. Führen Sie "gip.tool.entitywizzard.exe" aus.
  3. Führen Sie die T4-Textvorlage aus.

 


Nach dem Ausführen des T4-Templates wird der generierte C#-Datei unterhalb des T4-Templates eingefügt. Im Beispiel heißt die Datei mycompany.cs und die Klasse wurde so deklariert:

public partial class MyCompanyEntities : ObjectContext 

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)

 

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. Ü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.

 

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).