Bausteinaufrufe

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.

Eigenschaften

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.

Run

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

RunAsync

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.

Es wird für die Verwendung von 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.

Spawn

In Bearbeitung

Prepare

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.

CreateInstance

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

State

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.

Man beachte, dass normalerweise in der TAA der Zustand eines gerufenen Bausteins innerhalb des Aufrufers nur einmalig verwaltet wird, unabhängig von der Aufrufinstanz, und sogar unabhängig von dem jeweils ausgelösten Ereignis. In der 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.

Conditions

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.

New

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) {
		// ....
	}
}

Module

public Module Module

Die Eigenschaft Module liefert eine Referenz zu dem Baustein zurück.3)

1)
der prozessübergreifende Datenaustausch ist jedoch aktuell noch nicht implementiert, und wird erst dann technisch umgesetzt werden, wenn dieser konkret benötigt wird.
2)
wie solche über New erzeugt werden können
3)
10.00
dotnet:native:call · Zuletzt geändert: 21.12.2023 07:51

Copyright © 1992-2024 TeamWiSE Gesellschaft für Softwaretechnik mbH         Adressen |  Kontakt |  AGB |  Datenschutzerklärung |  Impressum