Erweiterte Programmierung


Im Kapitel für Geschäftsobjekte haben Sie gelernt wie man Programme erstellt, die man über das Menü auf einem Client startet. Endanwender nutzen Geschäftsobjekte für Ihr Tagesgeschäft (implementierte Geschäftsprozesse und Funktionalitäten).

Auf der anderen Seite gibt es ACComponent-Instanzen, die in den Anwendungsprojekten (bzw. Anwendungsbäumen) auf Serverseite existieren und ebenfalls zur automatisierten Abwicklung der Geschäfts- bzw. Produktionsprozesse verantwortlich sind. Das können entweder statische Prozessanwendungsinstanzen oder dynamische Workflowinstanzen sein, die während der Laufzeit in die Anwendungsbäume geladen werden.

Nun möchte man als Programmierer vermeiden, dass man redundanten Code programmiert, um dieselben Geschäftsprozessfunktionalitäten client- oder serverseitig abbilden zu können. Zudem kommt noch das Problem hinzu, dass man oft projektspezifische Änderungen abweichend zum Standard programmieren muss, die ebenfalls auf Client- als auch Serverseitig verfügbar sein sollen. Eventuell benötigt der Kunde auch Kommunikationsschnittstellen zu externen Systemen die ebenfalls die gemeinsamen Funktionalitäten verwenden möchten. Oder Sie möchten diese Funktionalitäten auch auf mobilen Endgeräten zur Verfügung stellen...

Für diese Anforderungen ist die serviceorientierte Architektur von iPlus geradezu prädestiniert, weil Sie den gemeinsamen Code mit sogenannten Manager-Klassen bereitstellen können, die unterhalb der Adresse "\Root\LocalServiceObjects" organisiert sind.

 

Managerklassen fügen Sie in der Entwicklungsumgebung unterhalb "LocalServiceObjects" hinzu:

 

Alle von iPlus bereitgestellte Managerklassen sind von der abstrakten Basisklasse "gip.core.autocomponent.PARole" abgeleitet und enden mit dem Wort "Manager". (Der Begriff "Manager" wurde aus dem Grunde gewählt, weil es eine Brücke zu realen Welt darstellen soll.  Personen, die eine Dienstleistung in Anspruch nehmen wollen, müssen sich an eine zentrale Stelle in einer Firma oder des öffentlichen Dienstes wenden. z.B. Die Ausstellung eines Reisepasses; das Gehalt das man von der Personalabteilung erhält; Einen Arbeitsauftrag, den man vom seinem Vorgesetzten erhält. Diese zentralen Stellen übernehmen daher eine gewisse "Rolle" oder Funktion in einem Gesamtsystem.)

Wenn Sie also Ihre eigene Manager-Klasse entwickeln wollen, sollten Sie sich an diese Namensgebung halten und auch von PARole ableiten.

Wir empfehlen zudem den Starttyp auf "Bei Bedarf" zu setzen, weil es ausreicht die Klasse erst dann zu instanzieren wenn sie auch tatsächlich benötigt wird. Zudem können Instanzen in "LocalServiceObjects" nicht persistierbar sein, weil jeder Client und Server diese Instanzen für sich in der eigenen Anwendungsdomäne instanziiert. Folglich müssen Manager-Klassen Zustandslos sein!

 

Programmierrichtlinien für Managerklassen

  1. Da die gemeinsamen Funktionen von Managerklassen von verschiedenen Stellen zeitgleich aufgerufen werden (Multihreading), dürfen sie keine privaten Felder haben (Stateless sein).
  2. Sie müssen die Klasse funktional programmieren. Das bedeutet, dass alle benötigten Daten beim Methodenaufruf als Parameter übergeben werden müssen. Auch interne Methodenaufrufe ebenso.
  3. Managerklassen dürfen daher auch nicht mit Threadsperren abgesichert werden, weil es keine privaten Feldwerte gibt (Zustand).

 

 

Im folgenden Beispiel sehen Sie wie man eine Manager-Klasse programmiert:

 

In der Basisklasse PARole ist folgende Methode deklariert: 

public static T GetServiceInstance<T>(ACComponent requester, string acIdentifier, CreationBehaviour creationBehaviour = CreationBehaviour.Default) where T : IACComponent

Diese statisch Hilfsmethode gibt die Instanz einer beliebigen Manager-Klasse zurück. Sie sucht nach der Instanz in folgender Reihenfolge:

  1. Unterhalb der anfordernden Komponente selbst (Kind). Falls Sie dort nicht gefunden wurde, dann wird sie 
  2. unterhalb "\Root\LocalServiceObjects" gesucht. Falls Sie dort nicht gefunden wurde, dann wird sie
  3. im Anwendungsprojekt "\Services" gesucht (Netzwerkinstanz)

Im Beispielcode oben wurden zwei Methoden (A) und (B) deklariert, welche die GetServiceInstance<T>-Methode verwenden. In Ihrer Manager-Klasse, die Sie programmieren möchten, müssen Sie sich jedoch entscheiden, ob sie lokal (A) oder als Netzwerkdienst (B) verfügbar sein soll. Beides wie im Beispiel oben ist nicht möglich.

Die anderen beiden Methoden (C) und (D) verwenden GetServiceInstance() und erstellen einen intelligenten Zeiger ACRef<T>.

Die Methode "SumLines()" (E) ist für die lokale Verwendung vorgesehen. Bitte beachten Sie hier, dass alle notwendigen Daten als Parameter übergeben werden müssen, weil Manager-Klassen zustandslos sein müssen.

Die Methode "SumLinesByID" (F) ist für die netzwerkfähige Verwendung vorgesehen.

 

Nun schauen wir uns an, wie diese "Manager-Klasse" verwendet wird:

 

Falls Sie Ihre Manager-Klasse lokal verwenden möchten, dann sollten Sie es wie im Beispiel unter Punkt (A) und (C) programmieren. Zur Verwendung als netzwerkfähige Instanz programmieren Sie es wie unter Punkt (B) und (D).

Vergessen Sie nicht den intelligenten Zeiger in der Deinitialisierungsphase zu trennen (E).


Die bekannteste Managerklasse ist der Nummerngenerator (gip.core.autocomponent.ACVBNoManager), den Sie bereits bei der Programmierung einer New()-Methode für ein Geschäftsobjekt kennengelernt haben. Der Nummerngenerator ist unter  "LocalServiceObjects" angelegt. Weil er sehr häufig benötigt wird, kann direkt über die Hilfseigenschaft "IRoot.NoManager" auf ihn zugegriffen werden. Da er zustandslos programmiert ist, müssen alle notwendigen Daten per Parameter übergeben werden:

string secondaryKey = Root.NoManager.GetNewNo(Database, typeof(InOrder), InOrder.NoColumnName, InOrder.FormatNewNo, this);
CurrentInOrder = InOrder.NewACObject(DatabaseApp, null, secondaryKey);

GetNewNo ist eine virtuelle Methode und hat folgende Signatur:

public virtual string GetNewNo(IACEntityObjectContext context, Type type, string entityNoFieldName, string formatNewNo, IACComponent invoker = null)

Sie können diese Methode in einer Ableitung überschreiben für ein projektspezifisches Verhalten. Damit Ihre Klasse verwendet wird, tauschen Sie die Klasse per Kontextmenü in der Entwicklungsumgebung aus:

 

Den Nummernkreis, den der Nummerngenerator verwenden soll, können Sie im Geschäftsobjekt "gip.bso.mes.BSONoConfiguration" einstellen.