Benutzerdefinierte Tabellen und Objekte

MesoXPO Framework

Benutzerdefinierte Tabellen und Objekte

Inhalt

 

⚠️ Workaround für Belegtabelle T026

Wenn von T026 BestelldateiMitte abgeleitete Klassen verwendet werden, dann führt die dafür notwendige Spalte ObjectType in der aktuellen WinLine Version Edition 2025 dazu, dass die Inhalte der benutzerdefinierten Spalten nicht gespeichert werden

Sub ErmitteleSpaltenWerte() Dim sql, UserNumber, PaletteOriginal, Spaltennummer, VarNummer UserNumber = right( "000" & cwlstart.CurrentUser.Number, 3) Set myvars = CWLCompany.ModifiedVars sql = "Select * FROM G02W245" & UserNumber & " (NOLOCK) WHERE c062='" & myvars.Value(26,0) & "'" PaletteOriginal = myvars.value(26,506) Palette = 0 Set res = cwlstart.CurrentCompany.Connection.Select(sql) If res>=0 Then ' Benutzerdefinierte Spaltenwerte aus temporärer Grid-Tabelle laden ' Palette Spaltennummer = 156 VarNummer = 506 myvars.value(26,VarNummer) = res.Value("c" & Cstr(Spaltennummer)) ' Rabatttext 1 Spaltennummer = 153 VarNummer = 504 myvars.value(26,VarNummer) = res.Value("c" & Cstr(Spaltennummer)) ' Rabatttext 2 Spaltennummer = 154 VarNummer = 505 myvars.value(26,VarNummer) = res.Value("c" & Cstr(Spaltennummer)) ' Frachtkosten Spaltennummer = 155 VarNummer = 503 myvars.value(26,VarNummer) = res.Value("c" & Cstr(Spaltennummer)) End If res.close Set res = Nothing End Sub 'Ende von (Deklarationen) Sub CWLCompany_OnInsertTable(TableNo) If TableNo = 26 Then Call ErmitteleSpaltenWerte End If End Sub Sub CWLCompany_OnUpdateTable(TableNo) If TableNo = 26 Then Call ErmitteleSpaltenWerte End If End Sub

Datalayer XPDictionary (Metadaten)

Die DataLayer, welche vom DataLayerManager (siehe Herstellen einer Datenverbindung) erzeugt werden, enthalten ein um die benutzerdefinierten Tabellen und Spalten erweitertes XPDictionary (https://docs.devexpress.com/XPO/DevExpress.Xpo.Metadata.XPDictionary), sofern bei der Methode CreateDataLayer die Parameter loadUserdefinedTables (für neue Tabellen) und/oder loadUserdefinedColumns für neue Spalten in Standard-Tabellen gesetzt werden.

Zwar können naturgemäß keine Linq-Abfragen für dynamisch erweiterte Objekte erzeugt werden, jedoch können z.B. XpCollection (https://docs.devexpress.com/XPO/DevExpress.Xpo.XPCollection ) Klassen verwendet werden, die dann z.B. an ein GridControl (https://docs.devexpress.com/WindowsForms/DevExpress.XtraGrid.GridControl) gebunden werden können.

Dieses erzeugt dann automatisch eine Column je PersistentProperty - auch derjenigen, die zur Laufzeit erzeugt wurden.

Hier ein Beispiel für die Erzeugung einer XpCollection für die BestelldateiMitte (t026) welche um benutzerdefinierte Spalten erweitert wurde:

using var uow = MesoObjectLayer.GetDataUnitOfWork(); var classInfoT026 = uow.GetClassInfo(typeof(BestelldateiMitte)); var xpCollectionT026 = new XPCollection(uow, classInfoT026); gridControl1.DataSource = xpCollecionT026;

Im DataGrid stehen alle persistenten Properties - inkl. der benutzerdefinierten - zur Verfügung:

Filterung einer XPCollection mit benutzerdefinierten Spalten

Hier ein Beispiel für die Erzeugung einer XPCollection für den BestelldateiKopf (t025) welche um benutzerdefinierte Spalten erweitert wurde:

var connectionStringSystem = MSSqlConnectionProvider.GetConnectionString( "DEMOSRV01\\DEMO", "meso", "*********", "CWLSYS"); ; using var uowSystem = new SystemUnitOfWork(connectionStringSystem); // Benutzerdefinierte Spalten und Tabellen in das XPDictionary laden using var dal = DataLayerManager.CreateDataLayer(uowSystem, "500M",loadUserdefinedColumns:true, loadUserdefinedTables:true); // ObjectLayer für automatische Filterung auf aktuelles Wirtschaftsjahr var mesoObjectLayer = new MesoObjectLayer(dal, "500M"); using var uow = mesoObjectLayer.GetDataUnitOfWork(); var classInfoT025 = uow.GetClassInfo(typeof(BestelldateiKopf)); var xpCollectionT025 = new XPCollection(uow, classInfoT025, CriteriaOperator.Parse("StartsWith([Kennzeichen],'TS')")); xpCollectionT025.Load(); foreach (var beleg in xpCollectionT025.OfType<BestelldateiKopf>()) { Debug.WriteLine($"Beleg {beleg.Mesoprim} hat Kennzeichen {beleg.GetColumnValueDirect<string>("U000")}"); }

Weiterführende Links

Criteria Language Syntax | Cross-Platform Class Library | DevExpress Documentation

How to: Build Complex Criteria | eXpress Persistent Objects | DevExpress Documentation

DataLayerManager Hilfsmethoden

LoadUserDefiniedTablesAndColumns

Dadurch werden die in der angegebenen Datenbankverbindungszeichenfolge gespeicherten benutzerdefinierten Tabellen und Spalten aus der Tabelle T132 geladen und in das XPDictionary angefügt.

/// <summary> /// Lädt benutzerdefinierte Tabellen und Spalten in das vorhandene XPDictionary /// </summary> /// <param name="connectionString"></param> /// <param name="dict"></param> public static void LoadUserdefinedTablesAndColumns(string connectionString, XPDictionary dict)

Arbeiten mit abgeleiteten Klassen

Im Test in Version 1.2.4-beta-1 https://css-edv-support.atlassian.net/wiki/spaces/MXP/pages/54231096/Releasenotes#1.2.4-beta-1-(-Beta-%F0%9F%94%A7--)

Dies ist nur möglich indem die betroffenen Mesonic WinLine Tabellen um die Spalte ObjectType erweitert wird.

In der Mesonic Datenbank wird zusätzlich die Tabelle XpObjectType angelegt, in welcher die Klassen sowie derer abgeleiteten Klassen mit eindeutigen Id’s gespeichert werden. Diese Id ermöglicht dem Xpo-Framework den Objekttyp auf Datensatzebene zu erkennen.

Der Zugriff auf die Daten ist dann nur noch mit der abgeleiteten Klasse möglich, da der ObjectType für alle Datensätze auf den neuen Typ gesetzt wird (sofern die Methoden des MesoXPO-Frameworks verwendet werden)

Bevor im WinLine Admin weitere benutzerdefinierte Spalten oder Tabellen erzeugt werden, muss die ObjectType Spalte wieder entfernt werden.

Dies kann entweder manuell per SQL oder über die Methode DataLayerManager.DropObjectTypesForUserDefinedTypes erfolgen

Nicht gemeinsam mit Parameter loadUserdefinedTables vom DatalayerManager verwenden, da es sonst zu Konflikten bei den persistenten Klassen im Dictionary kommt, wenn diese auch über abgeleitete Klassen definiert werden.

Sollte man die abgeleiteten Klassen wieder entfernen möchten, müssen die Datensätze aus der Tabelle XPObjectType entfernt werden (entweder per SQL oder im Code, z.B. mit
uow.ExecuteNonQuery($"DELETE FROM XPObjectType WHERE AssemblyName='{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}'");)

Weiterführende Links

https://docs.devexpress.com/XPO/2632/create-a-data-model/when-and-why-xpo-extends-the-database-schema#inheritance-mapping-and-object-polymorphism

https://docs.devexpress.com/XPO/2125/create-a-data-model/inheritance-mapping?utm_source=SupportCenter&utm_medium=website&utm_campaign=docs-feedback&utm_content=T1136775&contactId=A2150915

Vorteile und Herangehensweise

Wenn benutzerdefinierte Spalten der Mesonic Standard-Tabellen über Vererbung implementiert werden, so hat man den Vorteil, dass die Klasse inkl. benutzerdefinierter Felder im Code vollständig zur Verfügung steht, z.B. für Linq.

Damit das Xpo-Framework jedoch erkennen kann um was für ein Objekt sich ein Datensatz handelt (also um eine Instanz der Basisklasse oder eine Instanz einer abgeleiteten Klasse) ist die Spezifikation mittels ObjectType Spalte erforderlich. Diese Spalte ist wiederum eine Referenz auf die Tabelle XpObjectType (siehe XPObjectType Class | eXpress Persistent Objects | DevExpress Documentation), welche die definierten persistenten Klassen speichert.

Um das Anlegen der notwendigen Tabelle und Spalte in der Mesonic Datenbank kann die Methode CreateObjectTypesForUserDefinedTypes der Klasse DataLayerManager verwendet werden (siehe Verwendete Methoden).

Darüber hinaus müssen die Typen bei der CreateDataLayer Methode im Parameter userdefinedTypes übergeben werden.

Beispiel Ableitungen

Klassendefinitionen
/// <summary> /// Von ArtikelPreise T023 abgeleitet /// </summary> [MapInheritance(MapInheritanceType.ParentTable)] public class ArtikelPreiseMitIstos : ArtikelPreise { public ArtikelPreiseMitIstos(Session session) : base(session) { } private double istosMaterialkosten = default; /// <summary> /// U000 - Istos Materialkosten (kalkulatorisch) /// (Länge 8, SQL Datentyp System.Data.SqlTypes.SqlDouble) /// </summary> [Persistent("U000")] [Display(Name = "Istos Materialkosten (kalkulatorisch)")] public double IstosMaterialkosten { get => istosMaterialkosten; set => SetPropertyValue(nameof(IstosMaterialkosten), ref istosMaterialkosten, value); } } /// <summary> /// Von BestelldateiKopf T025 abgeleitet /// </summary> [MapInheritance(MapInheritanceType.ParentTable)] public class BestelldateiKopfKennzeichen : BestelldateiKopf { public BestelldateiKopfKennzeichen(Session session) : base(session) { } private string kfzkennzeichen = default; /// <summary> /// U000 - KfZ Kennzeichen /// (Länge 20, SQL Datentyp System.Data.SqlTypes.SqlNvarchar) /// </summary> [Persistent("U000")] [Display(Name = "KfZ Kennzeichen")] [Size(20)] public string KfzKennzeichen { get => kfzkennzeichen; set => SetPropertyValue(nameof(KfzKennzeichen), ref kfzkennzeichen, value); } private string anhaenger = default; /// <summary> /// U001 - Anhänger /// (Länge 20, SQL Datentyp System.Data.SqlTypes.SqlNvarchar) /// </summary> [Persistent("U001")] [Display(Name = "Anhänger")] [Size(20)] public string Anhaenger { get => anhaenger; set => SetPropertyValue(nameof(Anhaenger), ref anhaenger, value); } } /// <summary> /// Neue benutzerdefinierte Tabelle T699 in der WinLine-Datenbank /// </summary> [Persistent("T699")] [IsUserdefinedTable] public class Testtabelle : MesoBase { public Testtabelle(Session session) : base(session) { } private string id = default; /// <summary> /// U000 - Id /// (Länge 20, SQL Datentyp System.Data.SqlTypes.SqlNvarchar) /// </summary> [Persistent("U000")] [Display(Name = "Eindeutiger Key")] [Size(10)] [DevExpress.Xpo.Key] public string Id { get => id; set => SetPropertyValue(nameof(Id), ref id, value); } private string wert = default; /// <summary> /// U001 - Wert /// (Länge 255, SQL Datentyp System.Data.SqlTypes.SqlNvarchar) /// </summary> [Persistent("U001")] [Display(Name = "Wert")] [Size(255)] public string Wert { get => wert; set => SetPropertyValue(nameof(Wert), ref wert, value); } }
Exemplarische Verwendung
var connectionStringSystem = MSSqlConnectionProvider.GetConnectionString( "DEMOSRV01\\DEMO", "meso", "meso@demo", "CWLSYS"); ; using var uowSystem = new SystemUnitOfWork(connectionStringSystem); // Abgeleitete persistente Klassen var newTypes = new Type[] { typeof(ArtikelPreiseMitIstos), typeof(BestelldateiKopfKennzeichen), typeof(Testtabelle) }; // Anlegen der ObjectTypes var dict = DataLayerManager.CreateObjectTypesForUserDefinedTypes(connectionStringSystem, newTypes, false); using var dal = DataLayerManager.CreateDataLayer(uowSystem, "500M", additionalTypes: newTypes); // ObjectLayer für automatische Filterung auf aktuelles Wirtschaftsjahr var mesoObjectLayer = new MesoObjectLayer(dal, "500M"); using var uow = mesoObjectLayer.GetDataUnitOfWork(); foreach (var artikelPreis in new XPQuery<ArtikelPreiseMitIstos>(uow).Take(10)) Debug.Print($"{artikelPreis.Preisartikelnummer}:{artikelPreis.IstosMaterialkosten}"); foreach (var beleg in new XPQuery<BestelldateiKopfKennzeichen>(uow).Take(10)) Debug.Print($"{beleg.Mesoprim}:{beleg.KfzKennzeichen}"); // Eigene Tabelle T699 var testobject = uow.GetObjectByKey<Testtabelle>("TestId1"); if (testobject != null) { uow.Delete(testobject); uow.CommitChanges(); } testobject = new Testtabelle(uow) { Id = "TestId1", Wert ="Mein Testwert" }; uow.CommitChanges();

 

Verwendete Methoden

Der DataLayerManager verwendet hierbei die Methoden DropObjectTypesForUserDefinedTypes und CreateObjectTypesForUserDefinedTypes; insbesondere die Methode DropObjectTypesForUserDefinedTypes ist für Projekte relevant, da die Spalte vor einem Update oder Datenbankupsize oder Erweiterung der Tabellen um eigene Spalten entfernt werden muss.

/// <summary> /// Löscht die Spalte ObjectType aus den Basisklassen der angegebenen benutzerdefinierten Typen. /// Ist der Typ nicht abgeleitet, wird auch nichts durchgeführt /// </summary> /// <param name="connectionStringMesoSystem"></param> /// <param name="userdefinedTypes"></param> public static void DropObjectTypesForUserDefinedTypes(string connectionStringMesoSystem, Type[] userdefinedTypes) /// <summary> /// Erzeugt die XPObjectType Datensätze in der Tabelle XPObjectType (und legt diese ggf. auch an) /// Erweitert bei abgeleiteten Klassen die Basistabellen um die Spalte ObjectType und setzt den ObjecType auf /// die vergebene ID in der Tabelle XPObjectType /// </summary> /// <remarks> /// ACHTUNG: Hierdurch wird die Mesonic Tabellenstruktur um eine Spalte erweitert, die NICHT im Repository bekannt ist /// Das bedeutet, wenn Datenbankupsizes gemacht werden geht diese Spalte ggf. verloren und muss neu erzeugt werden /// /// Um weitere benutzerdefinierte Spalten zu erzeugen MUSS die Spalte vorher entfernt werden. /// Hierfür den <paramref name="dropColumn"/> auf 'true' setzen /// </remarks> /// <param name="connectionStringMesoSystem"></param> /// <param name="userdefinedTypes"></param> /// <param name="dropColumn"></param> /// <returns>Das XPDictionary Objekt welches die benutzerdefinierten Typen enthält</returns> public static XPDictionary CreateObjectTypesForUserDefinedTypes(string connectionStringMesoSystem, Type[] userdefinedTypes, bool dropColumn = false)

CompanyBase - Methoden

Alle Mandantenobjekte leiten von der Klasse CompanyBase ab. Diese stellt folgende Methoden zum Zugreifen auf benutzerdefinierte Spalten bereit:

GetUserdefinedColumnValue

Ermittelt den Wert der in "columnNumber" angegebenen Spalte als Typ T

public T GetUserdefinedColumnValue<T>(int columnNumber)

Beispiel zum Ermitteln der benutzerdefinierten Spalte U000 für einen Artikel als int:

Assert.NotNull(artikel.GetUserdefinedColumnValue<int>(0), "Product 50001 has no user definied Column 0 of int");

SetUserdefinedColumnValue

Setzt den Wert der in "columnNumber" angegebenen Spalte als Typ T

public void SetUserdefinedColumnValue<T>(int columnNumber, T value)

Beispiel zum Setzen der benutzerdefinierten Spalte U000 für einen Artikel auf den Wert “1” als int:

artikel.SetUserdefinedColumnValue<int>(0, 1);

 

CSS EDV Support - Tobias Forbrich