Funktionalität einer generierten Schlüsseltabelle

Wenn eine Schlüsseltabelle in eine .NET-Assembly generiert wird, so werden bestimmte Klassen und Strukturen erzeugt, die von dem aktuellen Stand der Entwicklungsdatenhaltung (bspw. Rochade) abgeleitet werden.

Zur Benutzung der Schlüsseltabelle wird im Code über die statische Factory-Methode New() eine Instanz der erzeugten Klasse zur Schlüsseltabelle angelegt1). Diese Klasse hat den Namen der Schlüsseltabelle und basiert auf der generischen Klasse TeamWiSE.Runtime.Reftab.Base<T> resp. letztendlich grundsätzlich auf TeamWiSE.Runtime.Reftab.Base. Zusätzlich wird auch eine Klasse basierend auf dem Kurznamen der Schlüsseltabelle erzeugt. Der Name dieser Klasse besteht aus einem T, gefolgt durch den Kurznamen der Klasse in Großbuchstaben2). Die Klasse beinhaltet lediglich die statische Factory-Methode New(), mit der dann eine Instanz der passenden Schlüsseltabellenklasse erzeugt wird. Diese Klassen implementieren IEnumerable<T> resp. IEnumerable. Die generierte Klasse hat als Namespace eine Konstruktion aus Firmenkürzel, der Angabe Taa, dem Typ der Schlüsseltabelle (Drse oder Drst), sowie der Anwendung, zu der die Schlüsseltabelle gehört. So würde sich bspw. die Klasse Ablehnungsgrund resp. die Klasse TL56 in der Namespace Al.Taa.Drse.Levert befinden.

Instanzen der Klasse können beliebig oft angelegt und wieder verworfen werden3). Intern wird die Tabelle mit einem Verfahren vorgehalten, das dafür sorgt, dass normalerweise die Tabelle auf schnellstmögliche Art und Weise zur Verfügung steht. Insbesondere besteht keine Notwendigkeit, eine Instanz der Schlüsseltabelle irgendwo global vorzuhalten, um Ladezeiten zu sparen. Das würde nur unnötig Speicher verbrauchen und keinen zusätzlichen Zeitgewinn bringen.

In Visual Studio® kann IntelliSense® bei der Suche nach der richtigen Schlüsseltabelle benutzt werden. Man beachte, dass es in dem gezeigten Beispiel neben ErtragsanteilRente auch eine Klasse ErtragsanteilRenteRec gibt. Diese Klasse wird benutzt, um einzelne Zeilen der Tabelle darzustellen.

Der ursprüngliche Name der Schlüsseltabelle wird für die Nutzung in .NET in sog. UpperCamelCase-Schreibweise umgesetzt. Dabei werden die Bindestriche als Trennung zwischen den einzelnen Namensteilen entfernt und die Namensteile konkateniert, jeweils anfangend mit einem Großbuchstaben. So wird bspw. aus ABTRETUNG-RANG-FOLGE der Name AbtretungRangFolge gebildet.

Wenn im Anwendungscode nun einen Eintrag für bestimmte Schlüsselwerte gesucht wird, so gibt es dazu mehrere Möglichkeiten, wie nachfolgend beschrieben.

Indexer

Ein einzelner Eintrag einer Schlüsseltabelle kann angesprochen werden durch die Nutzung der sog. Indexer-Funktionalität der generierten Tabelle. Dabei werden sämtliche Schlüsselwerte als Index der Tabelle übergeben. Welche Schlüssel benötigt werden, wird wiederum durch IntelliSense® angezeigt:

Anschließend lassen sich die einzelne Spalten (auch die Schlüsselspalten) als Eigenschaften der resultierenden Struktur direkt, wiederum mit IntelliSense®-Unterstützung, benutzen.

Man beachte, dass auch die in der Entwicklungsdatenbank eingetragenen Beschreibungen zu den Spalten in IntelliSense® gezeigt werden (Bezeichnung des Ablehnungsgrunds). Auch an dieser Stelle sind die Namen der einzelnen Spalten, resp. Felder in dem UpperCamelCase Verfahren umgesetzt worden. So könnte bspw. das fertige Coding aussehen:

sample.cs
//	C a p t i o n 
//	-------------
public string Caption(ushort AbleGrndK, string GevotKurz)
{
	return Ablehnungsgrund.New()[AbleGrndK, GevotKurz].AbleGrndBz;
}

Contains

Wenn versucht wird, einen Eintrag über den Indexer zu finden, wird eine KeyNotFoundException aufgeworfen, wenn zu den gesuchten Schlüsselwerten kein Eintrag vorhanden ist. Um den Laufzeitverlust für das Aufwerfen der Ausnahme zu vermeiden, sollte, falls (und nur wenn) das Nicht-Vorhandensein des Eintrages durchaus akzeptabel und keine wirkliche Ausnahme ist, die Gültigkeit vorher mit der Contains()-Methode geprüft werden. Man beachte: Falls nach erfolgreichem Contains()-Aufruf anschließend mit derselben Instanz der Schlüsseltabellenklasse der Indexer benutzt wird, entsteht kein Zeitverlust für eine erneute Suche - für diese übliche Reihenfolge wird intern vorgesorgt.

sample.cs
if (!lvZusBestimmBsVtZug.Contains(kTBsKenn, kTKenn, kZusTbK)) {
	Program.Log("no value found for {0}.{1}.{2}", kTBsKenn, kTKenn, kZusTbK);
}
 
var row = lvZusBestimmBsVtZug[kTBsKenn, kTKenn, kZusTbK];

GetOrDefault

Alternativ zu Contains, kann die Methode GetOrDefault4) den gefundenen Eintrag zurückliefen. Über die Eigenschaft IsExisting kann geprüft werden, ob der Eintrag gültig ist und es sich um den gefundenen Eintrag handelt, oder kein passender Eintrag vorhanden ist:

sample.cs
var row = lvZusBestimmBsVtZug.GetOrDefault(kTBsKenn, kTKenn, kZusTbK);
if (!row.IsExisting) {
	Program.Log("no value found for {0}.{1}.{2}", kTBsKenn, kTKenn, kZusTbK);
}

LINQ

Beliebig komplexe Suchen können mit LINQ (Language-integrated Query) vorgenommen werden. Das Beispiel zum Indexer könnte in LINQ wie folgt aussehen:

sample.cs
var tab = AL.Ref.Levert.Ablehnungsgrund.New();
var caption = tab.FirstOrDefault(r => r.AbleGrndK == 3 && r.GevotKurz == "VERLVDAU").AbleGrndBz;

Die Benutzung von LINQ ist dabei geringfügig teurer als die Benutzung des Indexers. Allerdings lassen sich über LINQ beliebig komplexe Ausdrücke formulieren, wie bspw. folgende Auswertung über Schriftstücke in der Tabelle, die für mehr als einen GeVo oder Ablehnungsgrund benutzt werden:

sample.cs
uint prev = 0;
var dups = tab
	.Where(r => r.SftStkK > 1)
	.GroupBy(r => r.SftStkK)
	.Where(g => g.Count() > 1)
	.SelectMany(g => g.ToList());
foreach (var dup in dups) {
	Debug.WriteLine("Schriftstück {0} {1}benutzt für GeVo {2} ({3} - {4})", 
		dup.SftStkK, 
		prev != dup.SftStkK ? "" : "wieder", 
		dup.GevotKurz, 
		dup.AbleGrndK,
		dup.AbleGrndBz);
	prev = dup.SftStkK;
}

Das liefert folgende Liste:

Schriftstück 50000938 benutzt für GeVo FOSTRAWE (1 - Fonds ist nicht verfügbar)
Schriftstück 50000938 wiederbenutzt für GeVo FOSWITCH (1 - Fonds ist nicht verfügbar)
Schriftstück 50001645 benutzt für GeVo VERKVDAU (1 - Individuelle Ablehnung)
Schriftstück 50001645 wiederbenutzt für GeVo VERLVDAU (1 - Individuelle Ablehnung)
Schriftstück 50000451 benutzt für GeVo BFRVVG (2 - Beitragsfreistellung unter Vorbehalt)
Schriftstück 50000451 wiederbenutzt für GeVo BFRVVG (3 - falscher Termin im ersten Versicherungsjahr)
Schriftstück 50000451 wiederbenutzt für GeVo BFRVVG (4 - befristete Beitragsfreistellung beantragt)
Schriftstück 50000451 wiederbenutzt für GeVo BFRVVG (5 - fehlende eigenhändige Unterschrift des VN)
Schriftstück 50000833 benutzt für GeVo ERHFORI (2 - Ablehnung aufgrund Risikoprüfung)
Schriftstück 50000833 wiederbenutzt für GeVo EINZUVVG (10 - Ablehnung aufgrund Risikoprüfung)
Schriftstück 50000945 benutzt für GeVo FOSTRAWE (2 - Fehlende eigenhändige Unterschrift des VN)
Schriftstück 50000945 wiederbenutzt für GeVo FOSWITCH (2 - Fehlende eigenhändige Unterschrift des VN)
Schriftstück 50000429 benutzt für GeVo KDGVERTR (2 - Unterschrift VN fehlt)
Schriftstück 50000429 wiederbenutzt für GeVo KDGVERTR (3 - Kündigung unter Vorbehalt)
Schriftstück 50000429 wiederbenutzt für GeVo KDGVERTR (6 - abweichende Unterschrift)
Schriftstück 50000429 wiederbenutzt für GeVo KDGVERTR (8 - Fehlende Unterschrift des Gläubigers)
Schriftstück 50001739 benutzt für GeVo VERKVDAU (2 - Ablehnung aufgrund negativem Preview)
Schriftstück 50001739 wiederbenutzt für GeVo EINZUVVG (12 - Aufgrund neg. Preview, Mehrbeitrag unerwünscht)
Schriftstück 50000864 benutzt für GeVo ERHFORI (3 - Rückstellung aufgrund Risikoprüfung)
Schriftstück 50000864 wiederbenutzt für GeVo EINZUVVG (11 - Rückstellung aufgrund Risikoprüfung)
Schriftstück 50000944 benutzt für GeVo FOSTRAWE (3 - wird eine andere Überschussverw.art gewünscht)
Schriftstück 50000944 wiederbenutzt für GeVo FOSWITCH (3 - wird eine andere Überschussverw.art gewünscht)
Schriftstück 50001644 benutzt für GeVo VERLVDAU (3 - Ablehnung aufgrund Einstiegsplausi)
Schriftstück 50001644 wiederbenutzt für GeVo VERKVDAU (5 - Ablehnung aufgrund Einstiegsplausi)
Schriftstück 50000941 benutzt für GeVo FOSTRAWE (5 - Individuelle Ablehnung)
Schriftstück 50000941 wiederbenutzt für GeVo FOSWITCH (5 - Individuelle Ablehnung)
Schriftstück 50000942 benutzt für GeVo FOSTRAWE (6 - Ablehnung aufgrund Einstiegsplausi)
Schriftstück 50000942 wiederbenutzt für GeVo FOSWITCH (6 - Ablehnung aufgrund Einstiegsplausi)

Exclude

Zu den einzelnen Instanzen einer Schlüsseltabellenklasse kann ein delegate (ggf. als lambda-Ausdruck) gesetzt werden, der bestimmte Einträge der Tabelle für die Suche ignoriert. Auf diese Weise entfällt die Notwendigkeit, eine Kopie der Tabelle mit einem Ausschnitt aus den Gesamtwerten anzulegen. Das Setzen eines derartigen Filters ist deutlich effizienter als das Kopieren einer Tabelle.

sample.cs
var berufsrisiken = Berufsrisiken.New();
berufsrisiken.Exclude = BerufsrisikenAusschluss;
var row = berufsrisiken[key];
[...snip...]
private static bool BerufsrisikenAusschluss(BerufsrisikenRec rec)
{
	return rec.DarDialRelKz != "J";
}

Oder als lambda-Ausdruck:

sample.cs
var berufsrisiken = Berufsrisiken.New();
berufsrisiken.Exclude = r => r.DarDialRelKz != "J";
var row = berufsrisiken[key];

ToString

Bei der ToString()-Methode wird eine Zeichenfolge aus dem Namen der Schlüsseltabelle, gefolgt von den einzelnen Schlüsselwerten gebildet. Bei einer historisierten Schlüsseltabelle wird außerdem das Historisierungsdatum zwischen Klammern hinzugefügt. Beispiel:

AblaufManagGrenzwertBez '8.10' (01.01.2012)

RowType

Jede Schlüsseltabelle hat eine RowType-Eigenschaft, die den System.Type für die einzelnen Einträge der Tabelle liefert. Um den RowType zu liefern, wird die Tabelle intern nicht geladen.

IsHistorized

Alle Schlüsseltabellen besitzen eine Eigenschaft IsHistorized, der true zurück liefert, falls die Schlüsseltabelle über ein Historisierungsdatum und -Kennzeichen verfügt. Siehe Historisierte Schlüsseltabellen für spezielle Funktionalitäten einer historisierten Schlüsseltabelle.

Interfaces

Wie bei einem Datenobjekt wird auch bei einer Schlüsseltabelle geprüft, ob die in der Assembly vorhandenen Interfaces zu der Schlüsseltabelle passen. In einem solchen Fall wird dafür gesorgt, dass die einzelnen Rows der Schlüsseltabelle das Interface implementieren.

DataTable

Zur Vereinfachung des Übergangs von bestehendem Code zur Nutzung von generierten Schlüsseltabellen wird noch die Möglichkeit des Zugriffs über DataTable unterstützt 5).

Dieses Feature sollte nur für vorübergehende Umstellungszwecke verwendet werden.

Zur Kompilierzeit erscheint eine Warnung CS0618: 'DataTable' is obsolete: 'Use row structures directly'

Zur Laufzeit wird eine Oops-Meldung produziert: Using deprecated feature DataTable on <tabName>

Die gelieferte DataTable ist readonly. Alle Versuche, an der gelieferten Tabelle Änderungen vorzunehmen, werden mit einer System.Data.ReadOnlyException quittiert.

1)
Bei der Anlage der Instanz werden die Einstellungen des aktuellen GeVos verwendet, um den Inhalt der Schlüsseltabelle abzuleiten. So wird dafür gesorgt, dass der Inhalt zu der jeweiligen Anwendungsversion passt. Die Struktur der Tabelle basiert auf der Version, die bei der Generierung festgelegt wurde. Bei abweichenden Anwendungsversionen bzw. Änderungszeiten der Schlüsseltabellendefinition wird versucht, in der Struktur der Daten eine Auf- oder Abwärtskompatibilität herzustellen. Der Inhalt kann - je nach Einstellungen bei der Generierung - statisch in der generierten Assembly geladen sein, oder wird gemäß der benötigten Laufzeitversion beschafft. Auch die üblichen Varianten der Datenbeschaffung (Ressourcen-DLX, SQL-Datenbank, CSV-Dateien) können wie gehabt verwendet werden. Die Laufzeit wird die Daten passend zur Version vorhalten und kann auch mit mehreren Versionen im gleichen Prozess umgehen.
2)
Im Gegensatz zu anderen Stellen in der Infrastruktur ist das Anwendungskürzel kein Bestandteil des gebildeten Namens, da die Anwendung bereits in dem Namespace für die Klasse enthalten ist.
3)
Anhand der Anlage und Freigabe der Instanzen wird in der Infrastruktur darüber Buch geführt, zu welchem Zeitpunkt welche Schlüsseltabellen zuletzt benutzt wurden, um so ggf. seit längerem nicht mehr benutzte Tabellen aus dem Zwischenspeicher zu entfernen und damit Speicherplatz zu sparen. Wenn anwendungsseitig die Tabelle durch eigenes Caching vorgehalten wird, ist diese Optimierung nicht möglich.
4)
ab R10.00
5)
Dabei ist zu beachten, dass nicht nur die (erstmalige) Erstellung der DataTable zusätzlichen Aufwand bedeutet, sondern vor allem auch alle nachfolgenden Zugriffe auf die Zeilen und Spalten der DataTable um einen Faktor 100 oder mehr aufwändiger sind als vergleichbare Zugriffe auf die generierte .NET struct mit Indexer oder LINQ.
reftab:dotnet:func · Zuletzt geändert: 23.05.2022 11:09

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