Der generierte Implementierungsrahmen bietet einen Abschnitt Call
, unter dem Aufrufe für die in der Aufrufstruktur definierten Bausteine vorbereitet sind. IntelliSense® von Visual Studio® bietet dabei Unterstützung für das Auffinden von passenden Bausteinaufrufen:
Genauso bietet IntelliSense® Unterstützung bei der Suche nach erlaubten Operationen für den Bausteinaufruf:
Wenn man den erwünschten Call festgelegt hat, bietet auch hier IntelliSense® Unterstützung bei der Erkennung der benötigten Schnittstellenobjekte:
Die Schnittstellenobjekte für den Aufruf können aus den verfügbaren Datenobjekten (lokale, globale oder Parameterobjekte) bestückt werden. Durch die generierten Typen wird hierbei von Visual Studio® resp. vom Compiler geprüft, ob die Zuweisungen passen. Im nachfolgenden Beispiel wird versehentlich ein Einzelobjekt zugewiesen, obwohl ein Mengenobjekt erwartet wird:
Ein Aufruf passiert typischerweise durch das Besorgen eines Aufrufobjektes, Bestücken der Daten, Ausführen des Bausteins und Abfragen des Status. Datenobjekte, die als globales oder Parameterobjekt übergeben werden sollen, und in dem aufrufenden Baustein auch als globales oder Parameterobjekt bekannt sind, werden automatisch als Argument vorbestückt. Eine solche Zuweisung kann natürlich jederzeit übersteuert werden. Neben der direkten Zuweisung einzelner Objekte stehen außerdem folgende Methoden zur Verfügung:
void AssignMatchingArguments(bool createNewForMissing = false);
Versucht für alle Argumente passende Zuweisungen zu finden. Die Zuweisungen werden ausschließlich auf der Basis des
Namens gesucht. Bei Namensgleichheit wird geprüft, ob die Datenstruktur und die Klasse passt. Optional werden für
fehlende Zuweisungen lokale Objekte mit anonymen Namen erzeugt, die beim Dispose
der Aufrufaktion aufgeräumt
werden.
Falls keine passende Zuweisung gefunden werden kann und das optionale Argument createNewForMissing
den Wert true
hat, werden für die fehlende Zuweisungen anonyme lokale Objekte angelegt und zugewiesen.
void AssignArgumentsWithNewObjects();
Erstellt eine neue Zuweisung für jedes einzelne Argument mit einem dafür neu angelegtem anonymen lokalen Objekt.
void ClearAssignedObjects(bool localObjectsOnly = true);
Setze alle zugewiesenen lokalen Objekte zurück. Die Datenbereiche werden initialisiert. Optional können auch die
zugewiesenen Parameter- oder globalen Objekte zurückgesetzt werden. Unveränderbare Objekte (IsReadOnly
) werden
übersprungen.
Wenn das optionale Argument localObjectsOnly
den Wert false
hat, werden nicht nur die Daten der zugewiesenen lokalen Objekte, sondern
die Daten aller Zuweisungen initialisiert.
Zum besseren Verständnis der diversen Möglichkeiten zur Ausführung einzelner Bausteine sei an dieser Stelle auf die Beschreibung „Kontrollfluss in der TAA“ verwiesen.
In der Klasse, die den Bausteinaufruf unterstützt, sind folgende Eigenschaften verfügbar:
public String Application { get; } public String Type { get; } public String Name { get; } public String Operation { get; }
Die Eigenschaften beschreiben den zu rufenden Baustein.
Für die synchrone Ausführung des Bausteins steht die Methode Run
zur Verfügung. Diese Methode liefert den Zustand der Ausführung als Rückkehrwert. Alternativ kann auch die Eigenschaft State
am Aufrufobjekt abgefragt werden, wobei in dem Fall der Zustand der letzten Ausführung des Bausteines geliefert wird.
Ein Aufrufobjekt kann mehrmals benutzt werden. Eine erneute Argumentzuweisung ist dabei nicht notwendig. Nachfolgend Beispielcode dazu:
// Besorgen des Aufrufobjektes var callBndInfoLesen = Call.DzBndInfo.Lesen; // bestücken der (fehlenden) daten callBndInfoLesen.Pvgob = pvgob; callBndInfoLesen.Snlid = snlid; // ausführen und zustand auswerten while (callBndInfoLesen.Run() != DzBndInfo.StateEnum.Ok) { // zustand auswerten switch (callBndInfoLesen.State) { case DzBndInfo.StateEnum.Fehler: case DzBndInfo.StateEnum.Fehlerliste: case DzBndInfo.StateEnum.NichtOk: // fehlerbehandlung break; } }
Man beachte, dass durch die generierten Klassen auch beim Ausfüllen der möglichen Zustandswerte IntelliSense® in der Lage ist, passende Vorschläge zu machen und somit nicht nur Komfort bietet, sondern auch die Fehleranfälligkeit deutlich reduziert.
Die Run
-Methode wird üblicherweise über ein spezifisches Aufrufobjekt ausgeführt. In diesem Fall liefert die Run
-Methode als Ergebnis den Zustand. In manchen Situationen kann es jedoch angebracht sein, kein spezifisches Aufrufobjekt, sondern ein allgemeines Aufrufobjekt zu verwalten und darauf die Run
-Methode auszuführen. Die in diesem Fall ausgeführte Methode liefert dann keinen Zustand, da diese spezifisch für den jeweilig aufzurufenden Baustein ist.
var call = this.Call.AfGevoFachfunktionen.Fherab; var stat = call.Run(); ModuleCallOperation call = this.Call.AfGevoFachfunktionen.Fherab; call.Run(); // void
public Task<T> RunAsync()
Über die Methode RunAsync
kann die Ausführung des Baustein auch asynchron angefordert werden, um während der Baustein ausgeführt wird andere Aufgaben abzuwickeln, bzw. mehrere Aufrufe parallel auszuführen:
public async Task<ObjList<Ad0033>> GetPartnerZuGob(string gobOb) { var result = this.Data.NewList(PvPartnerListe.Type); using (var call = this.Call.PvzPvgeslZuGobLesen.Lesen.New()) { call.AssignArgumentsWithNewObjects(); call.Pvgesa.Add(r => { r.X3xObArtK = 2; r.GobOb = gobOb; }); await call.RunAsync(); result.Add(call.Pvgesl); call.ClearAssignedObjects(); } return result; }
public async Task PvgeslEnsure(string[] gobObs) { // ermitteln wieviele calls parallel benötigt werden var available = this.GetPartnerGobobs(); var notavailable = gobObs.Where(i => !available.Contains(i)).Select(i => i).Distinct().ToList(); if (notavailable.Count == 0) { // alle bereits gelesen return; } var maxItems = 2; //4; var anzCalls = notavailable.Count / maxItems + (notavailable.Count % maxItems == 0 ? 0 : 1); // gobobs "verteilen" und call vorbereiten var calls = new List<PvzPvgeslZuGobLesen.LesenPtn_Call>(); var curItem = 0; for (var lnrCall = 1; lnrCall <= anzCalls; ++lnrCall) { var call = this.Call.PvzPvgeslZuGobLesen.LesenPtn.New(); call.AssignArgumentsWithNewObjects(); for (var lnrGobOb = curItem; lnrGobOb < curItem + maxItems; ++lnrGobOb) { if (lnrGobOb < notavailable.Count) { var item = call.Pvgesl.Add(); item.GobOb = notavailable[lnrGobOb]; } } curItem += maxItems; calls.Add(call); } // all calls starten, und warten await Task.WhenAll(calls.Select(c => c.RunAsync())); // ergebnisse verarbeiten foreach (var call in calls) { this.Pvgesl.Add(call.Pvgesl); call.ClearAssignedObjects(); call.Dispose(); } }
Anstelle des Zustands des Bausteins liefert die Methode RunAsync
ein System.Threading.Tasks.Task<TResult>
zurück, welcher sich um die Ausführung des Bausteins kümmert, und kann damit auch mit await
in asynchronen Funktionen verwendet werden. Der Zustand des Bausteins wird dabei zurück geliefert, bzw. kann ggf. über die Eigenschaft Result
der zurück gelieferten Task<T>
abgefragt werden.
RunAsync
(noch) vorausgesetzt, dass der aufgerufene Baustein auch mit Hilfe von generierten Basisassemblies implementiert ist.
public ModuleCallOperationAsync AsyncTask { get; }
Über die Eigenschaft AsyncTask
der aufrufbaren Bausteinoperation kann auf weitere Eigenschaften des asynchronen Bausteinaufrufs zugegriffen werden. Siehe dazu die Beschreibung der Klasse ModuleCallOperationAsync
. Wenn noch kein RunAsync
ausgeführt wurde, ist die Eigenschaft AsyncTask
gleich null
.
public String Prepare()
Die Methode Prepare
soll erlauben, dass der Kontrollfluss vollständig vom Anwendungscode geregelt wird, während der Datenfluss von der TAA übernommen wird. Nachdem die Daten der aufrufbaren Bausteinoperation zugewiesen sind, kann die Methode Prepare
aufgerufen werden, um den Datentransport in der TAA Infrastruktur vorzubereiten. Als Ergebnis der Prepare
-Methode wird eine Zeichenfolge geliefert, die an anderer Stelle in einem expliziten Register
benutzt werden kann, um den vorbereiteten Datenstrom nutzen zu können. Diese andere Stelle kann durchaus in einem anderen Thread oder sogar einem anderen Prozess liegen, nicht jedoch auf einem anderen Rechner. Die TAA wird den Datentransport zu dem Zeitpunkt veranlassen, wo diese benötigt werden, ggf. also auch über Prozessgrenzen hinweg1). So könnte bspw. die Vorbereitung wie folgt aussehen:
lstMA.PreparedToken = module.Call.HgUrlMaInYear.Lesen.Prepare();
Um dann anschließend bspw. wie folgt benutzt zu werden:
private HgMaInYear Module { get { lock (this) { if (m_Modl == null) { m_Modl = new HgMaInYear(); } if (!m_Modl.IsRegistered) { m_Modl.Register(PreparedToken); } return m_Modl; } } }
Die vom Prepare
gelieferte Zeichenfolge kann öfters benutzt werden, um die vorbereiteten Daten nutzen zu können. Allerdings kann nur eine Registrierung dazu gleichzeitig aktiv sein. Insofern kann also die Implementierung sich beliebig oft mit Unregister
von der TAA abmelden, und dann wieder anmelden, wenn die Daten benötigt werden. Wenn der Datenversorger jedoch beendet wird, sind auch die von ihm zur Verfügung gestellten Daten nicht mehr gültig und kann die vorbereitete Zeichenfolge nicht weiter für den Zugriff auf diese Daten benutzt werden.
Wenn für eine Instanz einer aufrufbaren Bausteinoperation bereits ein Prepare
ausgeführt wurde, wird die Prepare
Methode immer die gleiche Zeichenfolge zurück liefern.
public ModuleCallOperationPrepare Prepared { get; }
Über die Eigenschaft Prepared
der aufrufbaren Bausteinoperation kann auf weitere Eigenschaften der vorbereiteten Bausteinoperation zugegriffen werden. Außerdem kann über diese Eigenschaft auch auf diverse Ereignisse reagiert werden. Siehe dazu die Beschreibung der Klasse ModuleCallOperationPrepare
. Wenn noch kein Prepare
ausgeführt wurde, ist die Eigenschaft Prepared
gleich null
.
public T CreateInstance<T>()
Mit der CreateInstance
-Methode kann eine Instanz der Klasse erzeugt werden, die für die Implementierung des Bausteins konfiguriert ist. Im Zusammenspiel mit der Prepare
-Methode kann so ein wirksames Zusammenspiel für die anwendungsseitige Umsetzung des Kontrollflusses geboten werden.
// prepare the modl for the usercontrol preparedToken = module.Call.HgUrlMaInYear.Lesen.Prepare(); var xqt = module.Call.HgUrlMaInYear.Lesen.CreateInstance<HgUrlMaInYear>(); // an anderer Stelle im Code unter Zuhilfenahme des Tokens und der Instanz xqt.Execute(preparedToken); // oder auch: xqt.Execute(module.Call.HgUrlMaInYear.Lesen.Prepared.PreparedToken);
Bei der Ausführung eines Bausteins bspw. bei der Run
-Methode wird der Zustand als Ergebnis zurück geliefert. Um den Bausteinzustand zu erfahren, kann man diesen auch als State
von der für den Aufruf benutzten Instanz abfragen.
NativeSupport
-Lösung kann der Zustand deutlich konkreter abgefragt werden.
Um den Codeumfang für das Setzen des Zustandes eines Bausteins auf Basis des Zustandes eines gerufenen Bausteines zu verkürzen, werden außerdem folgende Methoden angeboten:
bool SyncStateWithCaller(); bool SyncStateWithModule(Module other);
Bei diesen Methoden wird versucht, dem Baustein des Aufrufers resp. dem übergebenen Baustein den gleichen Zustand zu geben, wie der aktuelle Zustand des gerufenen Bausteins, so wie dieser für die aktuelle Instanz des Bausteinaufrufes gesetzt wurde. Die Methoden liefern true
falls das geklappt hat, oder false
, falls der anzupassende Baustein keinen Zustand kennt, der dem des gerufenen Bausteins entspricht.
public ReadOnlyCollection<Condition> Conditions { get; }
Alle spezifischen Instanzen für einen Bausteinaufruf haben eine Eigenschaft Conditions
, in der alle Conditions
aufgelistet werden, die bei der Ausführung der jeweiligen Operation in dem betreffenden Baustein erstellt worden sind. Wenn unterschiedliche Instanzen für den gleichen Bausteinaufruf2) verwendet werden, hat jeder dieser Instanzen nur die Conditions
, die auch im Rahmen der Ausführung dieser Instanz entstanden sind.
Wenn eine aufrufbare Operation als this.Call.<Callee>.<Operation>
angesprochen wird, so wird immer dieselbe Instanz einer Klasse für die Aufrufunterstützung benutzt. Diese Instanz kann auch durchaus öfters für einen Run
, Spawn
oder Prepare
benutzt werden. Allerdings sollte es dabei auch immer dieselbe physikalische Instanz des aufzurufenden Bausteins, resp. der Operation. Wenn man einen Baustein tatsächlich in mehreren Ausprägungen ausführen möchte, muss man dazu eine getrennte Instanz für die aufrufbare Operation benutzen. So könnte man bspw. die gleiche Operation mehrfach mit unterschiedlichen Argumenten bestücken und (parallel) ausführen, um anschließend die Zustände und Datenergebnisse der unterschiedlichen Ausführungen miteinander zu vergleichen oder zu kombinieren. Nachfolgend ein zugegebenermaßen weniger sinnvolles Beispiel, um den Sinn der New
-Methode zu illustrieren.
var dta2014 = this.Data.NewList(HgUrlEckdaten.Type); var st2014 = this.Data.New(HgUrlSteuerdaten.Type); st2014.HjmAwrtJ = 2014; var call2014 = this.Call.HgUrlMaInYear.Lesen.New(); call2014.Hgusteu = st2014; call2014.Hgurldt = dta2014; call2014.Run(); var dta2015 = this.Data.NewList(HgUrlEckdaten.Type); var st2015 = this.Data.New(HgUrlSteuerdaten.Type); st2015.HjmAwrtJ = 2015; var call2015 = this.Call.HgUrlMaInYear.Lesen.New(); call2015.Hgusteu = st2015; call2015.Hgurldt = dta2015; call2015.Run(); if (call2014.State == HgUrlMaInYear.StateEnum.Ok && call2015.State == HgUrlMaInYear.StateEnum.Ok) { if (call2014.Hgurldt.Count != call2015.Hgurldt.Count) { // .... } }
public Module Module
Die Eigenschaft Module liefert eine Referenz zu dem Baustein zurück.3)