Posts mit dem Label Development werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Development werden angezeigt. Alle Posts anzeigen

Sonntag, 7. April 2024

Feiertage berechnen


Eine Sache hatte ich noch offen mit den Feiertagen. Wenn man die Ostertage berechnet, kann auch der Rest der Feiertage berechnet werden.

Viele Feiertage sind auf ihr festgelegtes Datum fixiert. Bei einigen jedoch nicht und haben ihre Abhängigkeit zu den Ostertagen.

 

Benötigt

Rider, Visual Studio 2022 oder Visual Studio Code

Mindestens C# 10 (für TOP LEVEL Statement)

 

Feiertage berechnen

Mit der Eingabe des Jahres, sollen am Ende die Feiertage in der Console ausgegeben werden.

 

Wir feiern die Feste wie sie fallen

In meinem vorigen Blogeintrag habe ich bereits beschrieben, wie die Berechnung von den Osterfeiertagen Funktionen auf Gauß Osterformel setzt. Diese Berechnung wird für einige weitere ebenfalls verwendet. Bei den festen Feiertagen ist klar und unschön, wenn diese auf einen Samstag oder Sonntag fallen.

Zur Vollständigkeit habe ich auch die Berechnung des Ostertages mit reingenommen.

 Console.WriteLine("Calculate German Feiertage!");  
 Console.WriteLine("Enter year:");  
 var year = int.Parse(Console.ReadLine());  
 var feiertage = new List<PuplicHoliday>();  
 var ostermontag = CalculateOstermontag(year);  
 feiertage.Add(new PuplicHoliday(ostermontag, "Ostermontag", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // neujahr  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 1, 1), "Neujahr", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // heilige drei könige  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 1, 6), "Heilige Drei Könige", "BW, BY, ST"));  
 // internationaler frauentag  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 3, 8), "Internationaler Frauentag", "BE, MV"));   
 // karfreitag  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(-2), "Karfreitag", "SL"));  
 // ostersonntag  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(-1), "Ostersonntag", "BB"));  
 // tag der arbeit  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 5, 1), "Tag der Arbeit", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // christi himmelfahrt  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(39), "Christi Himmelfahrt", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // pfingstsonntag  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(49), "Pfingstsonntag", "BB"));  
 // pfingstmontag  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(50), "Pfingstmontag", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // fronleichnam  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(60), "Fronleichnam", "BW, BY, HE, NW, RP, SL"));  
 // mariä himmelfahrt  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 8, 15), "Mariä Himmelfahrt", "BY, SL"));  
 // tag der deutschen einheit  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 10, 3), "Tag der deutschen Einheit", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // reformationstag  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 10, 31), "Reformationstag", "BB, HB, HH, MV, NI, SN, ST, SH, TH"));  
 // allerheiligen  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 11, 1), "Allerheiligen", "BW, BY, NW, RP, SL"));  
 // buß- und bettag  
 feiertage.Add(new PuplicHoliday(ostermontag.AddDays(-47), "Buß- und Bettag", "SN"));  
 // 1. weihnachtstag  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 12, 25), "1. Weihnachtstag", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 // 2. weihnachtstag  
 feiertage.Add(new PuplicHoliday(new DateTime(year, 12, 26), "2. Weihnachtstag", "BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH"));  
 var orderedFeiertage = feiertage.OrderBy(x => x.HolidayDate);  
 foreach (var holiday in orderedFeiertage)  
 {  
   Console.WriteLine($"Datum: {holiday.HolidayDate.ToShortDateString()}, Beschreibung: {holiday.Description}, Bundesländer: {holiday.Bundeslaender}");  
 }  
 Console.ReadLine();  
 // statische methode berechnet den ostermontag  
 static DateTime CalculateOstermontag(int year)  
 {  
   var a = year % 19; // Zyklus  
   var b = year % 4;  // Schaltjahr  
   var c = year % 7;    
   var k = year / 100;   
   var p = (8 * k + 13) / 25;   
   var q = k / 4;   
   var m = (15 + k - p - q) % 30; // Korrektur Sonntag und Montag gleichung  
   var n = (4 + k - q) % 7;    // Korrektur Sonnengleichung  
   var d = (19 * a + m) % 30;   // Mondentfernung  
   var e = (2 * b + 4 * c + 6 * d + n) % 7;  
   var day = 22 + d + e;  
   return (new DateTime(year, 3, 1)).AddDays(day);  
 }  
 public class PuplicHoliday  
 {  
   public PuplicHoliday(DateTime holidayDate, string description, string bundeslaender)  
   {  
     this.HolidayDate = holidayDate;  
     this.Description = description;  
     this.Bundeslaender = bundeslaender;  
   }  
   public string Bundeslaender { get; }  
   public string Description { get; }  
   public DateTime HolidayDate { get; }  
 } 

 

Wird der Code ausgeführt, sollte am Ende in der richtigen Reihenfolge, die Feiertage im Consolen Fenster zu lesen sein.



 

Zum Schluss

Wenn man das einmal durch gegangen ist, ist die Berechnung der restlichen der Feiertage simple umzusetzen.

 

Referenzen

Gesetzliche Feiertage in Deutschland – Wikipedia

 

Montag, 4. März 2024

Tool Lagerverwaltung (Teil 8) Nuget Paket bereit gestellt

Nun habe ich endlich ein Template fertig, auf dem ich das Tool entwickeln kann und auch für andere Anwendungen, die ich noch schreiben werden mit .NET MAUI. Ich konnte mich wieder etwas Geistig Sammeln und jetzt kann der nächste Schritt getätigt werden.

 

Benötigt

Visual Studio 2022 oder Rider 2023.3.3

nuget Account (zum hochladen einges nuget Paket)

 

Frontend und .NET

Das bestehende Projekt soll auf .NET MAUI umgestellt werden und auf .NET 7.

 

Projekt Aktualisieren

Warum nicht .NET 8? Beim versucht auf die aktuelle Version, hatte ich noch Probleme, die ich noch nicht verstanden habe. Zeitlich bin ich allerdings auch nicht dazu gekommen, dies zu ergründen.

 

Fehler entdeckt

Beim Testen mit dem Template traten zunächst keine gleich auffallende Fehler auf. Aber nach dem ich das Template angewendet habe für die Solution des WarehouseManagement, musste ich feststellen, dass der Hover Effekt sich nicht zurück setzt auf den Normal zu stand.


Bevor der Fehler behoben wird

Einerseits habe ich nicht beschrieben, wie die Darstellung eines Buttons sich zeigen soll. Andererseits hat man als Benutzer ein gewisse Erwartung wie verwendbare Buttons reagieren soll oder suggerieren soll, ob der Button in der Auswahl liegt.

Also bevor ich zur Fehlerbehebung gehe, definiere ich die Grafischen Darstellung des Buttons und das Wiki für das Application Framework.

Folgende Zustände für den Button werden beschrieben: Normal, Disabled, Pressed, PointerOver


Die Beschreibung ist technisch und sehr auf die Wesentlichen Inhalt beschrieben.

Fertige Seite: Steuerelemente_ButtonPage · Codexzier/Codexzier.Maui.ApplicationFramework Wiki (github.com)


Was ist nun mit den Zuständen, die nicht definiert sind? Die gehe ich an, wenn weitere Zustände gebraucht werden. Das gilt natürlich auch für andere Steuerelemente.

 

Beben

Zuerst dachte ich, der Fehler wäre simple. Den im Style für Visual State 'Normal' habe ich nicht den BackgroundColor nicht festgelegt. War leider nicht das Problem

Das Problem war mit dem Kompilierten Ergebnis in der Release Version. Nachdem ich das Debug kompilierte variante  hochgeladen habe und das Projekt aktualisierte, funktionierte das zurückstellen der Grundfarbe des Buttons.

Warum hier ein Problem mit Release besteht, werde ich zu einem anderen Zeitpunkt heraus finden müssen.

 

Routine finden

Noch ist das keine Gängiger Vorgang. Im Business habe ich bisher keinen Gängigen Vorgang gefunden oder geschweige, dass Fehler in der Reihenfolge Anforderung prüfen, Dokumentation erweitern und dann erst behoben wird.


Links

Montag, 5. Februar 2024

Problem bei der Entwicklung mit MS Office Interop


So ganz habe ich nicht verstanden warum das Hinzufügen des Nuget Paket Microsift.Office.Interop.Word (andere Office Interop Pakete) nicht ausreicht. Nach dem ich das Paket hinzugefügt habe und versucht habe nur die Application zu instanziieren, bekam ich einen Ausnahmefehler.

 

Verwendete Umgebung

  • Windows 11
  • Visual Studio 2022
  • Office365 Installation

 

Nur zum laufen bringen

Die Consolen Anwendung soll eine Instanz von Word starten.

 

Die Variante, die nicht geht

Zur Vollständigkeit, wie ich zu dem Ausnahmefehler kam, bei der Verwendung des NuGet Pakets.

 


Und zum Testen ein kurzer Code Schnipsel, der eigentlich ausführbar sein sollte.

 using Microsoft.Office.Interop.Word;  
 Console.WriteLine("Start office application! Solution 1");  
 var app = new Application  
 {  
   Visible = true  
 };  
 Console.WriteLine("Office application started!");  
 Console.WriteLine("Press any key to close the application!");  
 Console.ReadLine();  
 // close the application  
 app.Quit();  

Die Assembly Datei konnte nicht gefunden werden, aber ich konnte nicht erkennen woran das lag. Die zu verwendete Assembly landet nach dem Kompilieren ebenfalls im selben Ordner.

 System.IO.FileNotFoundException  
  HResult=0x80070002  
  Message=Could not load file or assembly 'office, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'. Das System kann die angegebene Datei nicht finden.  
  Source=<Cannot evaluate the exception source>  
  StackTrace:  
 <Cannot evaluate the exception stack trace>  

 

Alternative Lösung

Statt das NuGet Packet zu verwenden, wird bereits auf eine installierte Bibliothek zugegriffen.



Kompiliert und gestartet wird wieder mit dem selben Code Schnipsel.

 using Microsoft.Office.Interop.Word;  
 Console.WriteLine("Start office application! Solution 2");  
 var app = new Application  
 {  
   Visible = true  
 };  
 Console.WriteLine("Office application started!");  
 Console.WriteLine("Press any key to close the application!");  
 Console.ReadLine();  
 // close the application  
 app.Quit();  

Wird die Anwendung ausgeführt, sollte sich die Word Anwendung ohne Seite starten.

 


Drück mit der Consolen Anwendung eine Taste, wird der Schließen Befehl ausgeführt und die Word Anwendung sollte sich dann schließen.

 

Warum geht das (nicht)?

Das habe ich mich auch gefragt und wird zunächst ein offener Punkt bleiben.

Die Installierte Assembly zu verwenden ist klar, dass muss funktionieren. Hat bereits vor 10 Jahren so funktioniert und geht auch heute noch.

Aber wenn man die Office Assembly per NuGet Paket einbinden kann, sollte dies auch gehen. Die Beschreibung von Microsoft (Link) beschreibt bzw. gibt einen Hinweis, dass die Implementierung abweichen kann von der Beschreibung. Dennoch frage ich mich, wo meine Consolen Anwendung schaut, wenn versucht wird das NuGet Paket zu verwenden.

 

Es geht doch (noch)

Im letzten Absatz habe ich mir die Frage schon fast beantwortet. Wenn man nicht gerade auf der aktuellsten .NET unterwegs ist, funktioniert die Beschreibung von Microsoft genau so wie beschrieben. Wenn das Projekt mit .NET Framework 4.8 eingestellt ist, also bevor die .NET Welt aufgeräumt wurde, dann funktioniert dies auch. Code ist praktisch das selbe, nur halt ohne TOP Level Statement.


Dienstag, 12. Dezember 2023

Ostersonntag und Ostermontag berechnen


Eine Aufgabe auf der Arbeit brachte mich dazu, daran zu denken, dass Offizielle Feiertage ja berechnet werden könnte. Somit kam ich auf einen Blog, welches die Feiertage mit SQL Sripts löste. Leider schien dies nicht ganz klar zu sein, für welche Datenbank. Ist jedoch nichts, was mich aufhält und nahm die Wikipedia Beschreibung über Gausche Osterformel (Wiki).

Verwunderlich ist, dass meine Suche im Internet keine Ergebnisse erbrachte für MS SQL. Manchmal glaube ich, dass mir die Suche absichtlich keine oder kaum brauchbare Ergebnisse zeigt.

 

Benötigt

Rider oder Visual Studio für C#

 

Wann ist Ostern für das Jahr

Mit Eingabe des Jahr, soll mir der Ostersonntag und Ostermontag berechnet werden.

 

Gaußsche Osterformel

Seien wir doch ehrlich, wie ist man auf so eine Formel nur gekommen. Bis letzte Woche dachte ich, dass wird irgendwie entschieden und fertig. Aber weit gefehlt. Im Gegensatz zu anderen Feiertagen, ist dieser immer an einem Sonntag und Montag. Oder schon mal was vom Osterdienstag gehört. Die anderen Feiertage sind an ihrem festen Datum platziert.

 

Folgender Code ermöglicht die Eingabe und Ausgabe einer C# Consolen Anwendung. Für die Berechnung werden nur Ganzzahlen verwendet und die Funktion Modulo.

 Console.WriteLine("Calculate Ostern");  
 Console.WriteLine("Enter year:");  
 var year = int.Parse(Console.ReadLine());  
 var a = year % 19; // Zyklus  
 var b = year % 4;  // Schaltjahr  
 var c = year % 7;    
 var k = year / 100;   
 var p = (8 * k + 13) / 25;   
 var q = k / 4;   
 var m = (15 + k - p - q) % 30; // Korrektur Sonntag und Montag gleichung  
 var n = (4 + k - q) % 7;    // Korrektur Sonnengleichung  
 var d = (19 * a + m) % 30;   // Mondentfernung  
 var e = (2 * b + 4 * c + 6 * d + n) % 7;  
 var day = 22 + d + e;  
 var ostersonntag = (new DateTime(year, 3, 1)).AddDays(day - 1);  
 var ostermontag = ostersonntag.AddDays(1);  
 Console.WriteLine($"Ostersonntag: {ostersonntag:dd.MM.yyyy}");  
 Console.WriteLine($"Ostermontag: {ostermontag:dd.MM.yyyy}");  
 Console.WriteLine("Press any key to exit"); 

 

Ausnahmen

Wie in jeder Sache kommen auch Ausnahmen vor, die selbst der Algorithmus nicht hergibt. An der Stelle kann ich kann ich nur auf die Wiki Seite oder einer anderen Lektüre verweisen.

Sonntag, 12. März 2023

Tool Lagerverwaltung (Teil 6) Umzug nach .NET MAUI


Irgendwann ist immer was Neues da und dann sind Runde Ecken dran. Ich las die aktuelle .NET Pro (2/23) und stellte fest, dass .NET MAUI, die neue Zukunft für Frontend Technologie wird. Oder zumindest vermute ich dies stark, da diese Technologie ein sehr guter Nachfolger zu WPF und Xamarin sein könnte.

Natürlich ist .NET MAUI noch sehr neu und sicherlich sind im Bereich für Plattformübergreifende Abdeckung noch das eine und andere Problem da.

Für mich bedeutet das, dass ich mein ‚Application Framework' umziehe in die neue Welt von .NET MAUI.

Benötigt

  • Visual Studio 2022
  • GitHub

 

Umzug

Eigentlich sag der Titel des Absatzes schon alles. Aber vielleicht gehen einige Sachen noch nicht so wie ich mir das Vorstelle. Daher ist das erste Ziel das alle Grundlegende Funktionen aus dem 'Codexzier's Application Framework' zu übernehmen. Die speziellen Benutzerdefinierten Steuerelemente werden später migriert.

 

Neues Projekt und Struktur aktualisieren

So ganz will ich die Inhalte nicht kopieren und ich bin mir nicht sicher, ob einige Vorgänge genau so funktionieren wie in WPF. Zudem sollen auch die Fehlenden Unit Tests kommen, was wiederum voraussetzt, dass ich für jede Komponente die Anforderungen und Erwartungen beschreibe. Stichwort: Test-Driven-Development oder auch kurz TTD. Ein Thema, dass man immer wieder hört, aber nie gemacht wird oder gefühlt nur von mir umgesetzt wird.

Zunächst erstelle ich das Projekt mit folgenden Einstellungen:

  • Projektvorlage: .NET MAUI Class Library
  • Projektname: Codexzier.Maui.ApplicationFramework


Die Ordner Struktur kann zum Teil übernommen werden. Allerdings ziehe ich vor, erst die Ordner zu erstellen, wenn eine Klasse darin erstellt wird. Damit soll vermieden werden, nicht genutzte Inhalte anzulegen.

Darüber hinaus wurden von der Vorlage Ordner angelegt mit den Namen zu verschiedenen Plattformen.


Aktuell belasse ich die Inhalte und entscheide erst mit späteren Tests auf den verschiedenen Betriebssystemen, ob ich die Ordner benötige.


Issues anlegen in GitHub

Eigentlich sollte das als erstes erfolgen, bevor überhaupt die erste Implementierung stattfindet. Ich habe nachträglich mich Anhand des bestehenden WPF Application Frameworks, meine Aufgaben definiert.

Dazu lernen

Bevor ich die weitere Hauptkomponenten implementieren kann, muss ich mehr über .NET MAUI Lernen. Denn zu diesem Zeitpunkt weiß ich nicht, ob meine Bisherigen Funktionen so weit übernommen werden kann und zum anderen will ich statt MVVM, das MVU Pattern verwenden, dass auf .NET MAUI besser geeignet ist.

Übersicht

Teil 1 - Wiki anlegen in GitHub

Teil 2 - Neue Solution

Teil 3 - Noch ein Wiki anlegen

Teil 4 - Datenbank und Schnittstelle

Teil 5 - Konzept ausschreiben

Teil 6 - Umzug nach .NET MAUI

Samstag, 14. Januar 2023

Legacy Dinge wie Windows Phone Entwicklung

Eins ist klar, dass Thema Windows Phone Entwicklung war, damals gestorben, als Microsoft gesagt hatte, dass Windows Phone abgekündigt hatte. Ärgerlich für alle, die ihr Wissen in dem Bereich aufgebaut haben.

Diesen Blog-Post musste ich zweimal schreiben. Grund ist, dass ich beim ersten Aufschreiben auf einer Falschen Version ausging und vergessen hatte, dass die letzte Version von Windows Phone, Windows 10 war. Seltsamerweise (oder auch nicht), wenn man im Internet sucht, findet man immer oft Hinweise zu Tools wie 'Visual Studio 2012 for Windows Phone'. Leider funktioniert diese Version für die Entwicklung gar nicht mehr, weil die Anmeldung zum Freischalten des Windows Phone für die Entwicklung nicht mehr funktioniert.


Aber warum sollte man sich damit noch beschäftigen? Für mich geht's um Machbarkeit und natürlich auch um das Basteln.

 

Benötigt

 

Ziel der Übung

Was muss gemacht werden, um eine Windows Phone App auf dem Smartphone bereitgestellt werden kann.

 

Windows Phone Betriebssystem

Für dieses Beispiel verwende ich ein Lumia 930, dass ich anfangs mit Windows 8.1 installiert war und mit späteren Updates auf Windows 10 bis Version 1607 (Anniversary Update, 14393, MS Windows 10 Mobile)

 

Windows Phone einrichten

Anfangs zu Windows Phone 7 bis 8, musste für das Freischalten des Smartphones in den Entwicklermodus, das Tool 'Windows Phone Developer Registration' verwendet werden. Für den Vorgang muss eine Anmeldung an Microsoft bestehen, um die Registrierung abzuschließen. Dies funktioniert jedoch nicht mehr. Nach der Anmeldung erscheint ein Fehler und nichts kommt.

Ich weiß nicht mit welchem Update die Änderung kam, aber das Umschalten in den Entwicklungsmodus erfolgt nun auf dem Smartphone selbst. In den Einstellungen unter 'Für Entwickler' oder einfach in den Einstellung nach 'entwickler' suchen, kann der Entwicklermodus gesetzt werden.


Neues UWP Projekt

Zuerst hatte ich versucht mit Visual Studio 2019 die 'Hello World!' auf das Smartphone zu laden. Schließlich kann die Anniversary Update Version 1607 eingestellt werden. Jedoch wird von VS 2019 die Verbindung zum Windows Phone nicht mehr unterstützt.

 

Für das Erstellen eines Neuen Projektes kann eine leere UWP Vorlage verwendet werden. Wichtig bei der Vorlage ist, die entsprechende Version des Betriebssystems vom Ziel Windows Phone einzustellen.


Nach dem anlegen kann in der MainPage.xaml ein 'TextBlock'-Steuerelement hinzugefügt werden, da die Vorlage nur eine leere Seite bietet. Ein traditionelles 'Hello World!' reicht für den Anfang. Nicht vergessen, die Farbe ebenfalls einzustellen.


Kommen wir zuletzt noch zu den Ziel Einstellung für die Bereitstellung. Ob Debug oder Release, ist hier zunächst egal. Nur Prozessor und Ziel System muss eingestellt werden.


Device ist euer Smartphone..


 

…und ARM ist der verwendete Prozessor (in meinem Fall).


Bereitstellung

Nun kann das Smartphone mit dem PC oder Notebook verbunden werden.

Bevor auf den Button 'Device' geklickt wird, muss der Bildschirm entsperrt sein. Andernfalls erscheint eine Fehlermeldung.

Ein paar Sekunden kann das Hochladen dauern, bis sich die APP auf dem Windows Phone öffnet. Fertig!


Fazit

Geht doch. Und was mache ich mit dem Wissen? Kleine Projekte lassen sich immer damit realisieren. Ich kann mir vorstellen, dass man z.B. alte Windows Tablets oder Windows Phones für Home-Automation verwenden kann.

 

Dienstag, 29. November 2022

Tool Lagerverwaltung (Teil 5) Konzept ausschreiben

Diesmal wieder nichts mit Programmieren, sondern um Planung und was für das Ziel gebraucht wird. Gut Vorgeplant ist halbe Arbeit. Viele Entwickler können davon ein Lied singen, wenn die Anforderung kleckerweise rein kommt. Da macht man was und die Anforderung sagt, dass man sich etwas anderes vorgestellt hat. Die nachträgliche Änderungen, die man durch Überlegungen hätte vermeiden können, ist das Tägliche Brot des Wahnsinn eines Softwareentwicklers.

 

Benötigt

  • GitHub (Der Eintrag aus Teil 3 wird fortgesetzt)

 

Handhabungskonzept

Die Eingabe einer Sache oder Artikel soll mit möglichst wenigen Klicks realisiert werden. Auch das aufrufen zu anderen Seiten wie Option und Vorlagen, soll in kurzen Wegen erreichbar sein. Die Verwendung suggeriert, welche Funktionen und Eingaben möglich sind.

 

See what you get

Einiges wird vom Application Framework bereits umgesetzt oder überhaupt aus dem Standard WPF. Aber kommen wir Grundlegend zu dem, was man als Anwender erwartet, wenn man einen neuen Artikel anlegen will.

Typisch ist, dass wenn man mit dem Mauszeiger über ein Steuerelement fährt, kommt ein Hover Effekt. Was auch sein soll, ist das nur Steuerelemente zu sehen sind, die benutzbar sind. Beim Ausfüllen von Formularen soll die Eingabeführung hervorgehoben wird.

  • Nur Steuerelemente Anzeigen die Benutzbar / Verwendbar sind
  • Verwendbar, aber nicht alles erfüllt, dann nur ausgrauen und deaktivieren

 

Benutzer Führung

Obwohl auf Intuitive Benutzer Oberfläche gesetzt wird, kommen zusätzliche Notification Information, die jeweils zu den Eingabe- und Ausgabefelder hinterlegt werden.

Die Pflichtfelder für einen neuen Eintrag, aus der Vorlage werden z.B. Farblich hervorgehoben.

  • Notification Info
  • Pflichtfelder hervorheben


Fehlermeldungen

Wer kennt das nicht. Generische erzeugte Fehlermeldungen, die einem nichts sagen. Die Ausgabe von Fehlermeldungen ist schwieriger als man denkt. Viele Fehler können meistens nicht eindeutig bestimmt werden und oft wirft eine Fehlernachricht mehr Fragen auf.

Was hilfreich sein könnte, ist davon abhängig, was im Programmcode steht. Also wenn gespeichert wird, dann ist das Umfeld und die Faktoren zu nennen, was möglichweise einen Ausnahmefehler verursachen kann. Zusätzlich kann dann die Fehlernachricht aus der Exception abgegriffen werden.

Für Benutzereingaben wie Pflichtfelder, ist die Ausgabe der Felder zu benennen, die halt auszufüllen sind.

  • Fehlernachrichten soweit von möglichen Komponenten beschreiben
  • Benutzerfehler genau wiedergeben


Wiki Beschreibungen anlegen

Kommen wir zum Wiki. Eine kurze Beschreibung zu jedem Thema ist leichter zu verstehen. Keiner mag lange Beschreibungen und daher können auch Bilder verwendet werden, um die Beschreibung einfach zu halten. Ein bis zwei Sätze reichen zunächst aus, nur kurz in Worten was hier getan werden kann. Manchmal kommt man von dort auch auf andere Ideen. Zum Beispiel kam ich erst beim Schreiben auf die Idee, wie ich mit Optionen die Anwendung Flexibler gestalten könnte.

 

GitHub - Wiki - WarehouseManagement (Kann sein, dass einige Inhalte noch nicht ausgeschrieben sind)

 

Die Bilder habe ich mit dem kostenlosen Programm Pencil zusammengestellt. Das geht relativ schnell und unkompliziert.

 

Einfach machen

Hauptsache bei Langzeit Projekten ist, dass man sich Notizen zu dem macht, was man machen möchte oder will. Gute Ideen notieren und ein Ordnen. Am besten erfasst ihr euer Ziel in einem Digitalen Notizbuch, dann lässt sich darin später suchen.

 

Übersicht

Teil 1 - Wiki anlegen in GitHub

Teil 2 - Neue Solution

Teil 3 - Noch ein Wiki anlegen

Teil 4 - Datenbank und Schnittstelle

Teil 5 - Konzept ausschreiben

Teil 6 - Umzug nach .NET MAUI

Montag, 31. Oktober 2022

Tool Lagerverwaltung (Teil 4) Datenbank und Schnittstelle

Bisher hatte ich nur erwähnt, dass als dritte Schicht die Datenbank abbildet. Klingt etwas übertrieben, macht aber einen deutlichen Performance unterschied gegenüber der Verwendung von Text Dateien in CSV Format.

 

Benötigt

  • Visual Studio 2022 oder anderen Compiler
  • .NET 6.0
  • Codexzier's Application Framework
  • GitHub
  • SQLite
  • MS SQL oder PostgreSQL (nicht zwingend für den weiteren Verlauf)

Einbinden

Das Einbinden einer Datenbank soll so entkoppelt sein, dass die Datenbank Auswechselbar sein soll. Spricht, entweder soll SQLite für den Lokalen Einsatz verwendbar sein oder eine Externe Datenbank auf einem anderen Server.

 

Kein OR-Mapper

Man könnte an der stelle einen OR-Mapper verwenden, der für die Größe des Projektes auch völlig ausreicht. Aber ich will auf diesen Komfort verzichten und schreibe die SQL-Statement aus. Das Thema möchte ich für einen anderen Blogeintrag nutzen, in dem ein Umzug zum OR-Mapper beschrieben wird.

 

Tabellen

In den vorigen Post habe ich von den Einträgen immer von einer Sache gesprochen. Das liegt daran, dass in Zukunft zu speicherndem Inhalt offen ist und durch den Benutzer selbst definiert werden kann durch Vorlagen. 

Nun soll die Haupttabelle nicht 'Sache' oder 'Thing' heißen, und 'Objekt' erst recht nicht. Passend ist 'Artikel' oder 'Item'. Fehlt also noch als Eigenschaft ID für die Eindeutigkeit des Datensatzes und 'Titel' soll in Kurzform beschreiben, was der Eintrag ist. Weitere Eigenschaften kommen später, denn jede weitere Eigenschaft muss sich aus dem Nutzerkontext ergeben.

 public class ArticleItem  
 {  
   public long Id { get; set; }  
   public string Title { get; set; } = "title";  
   public string Description { get; set; } = String.Empty;  
   public bool IsArchived { get; set; } = false;  
   public bool IsTemplate { get; set; }  
   IEnumerable<IArticleSubItem> ArticleSubItems { get; set; } = Array.Empty<IArticleSubItem>();  
 }  


Schnittstellen definieren

Grundlegend werden mindestens drei Methoden benötigt. Speichern, Abrufen und aktualisieren. An der Stelle muss ich noch betonen, dass noch kein komplettes Konzept steht, weshalb die Schnittstelle rudimentär bleibt.

 public interface IDatabaseConnector  
 {  
   void CreateTable<TTable>();  
   void Insert(ArticleItem articleItem);  
   IEnumerable<ArticleItem> GetAll();  
   ArticleItem GetById(long id);  
   IArticleSubItem SubItem_GetById(long id);  
   void Update(ArticleItem articleItem);  
   void Update(IArticleSubItem subItem);  
 } 

 

Nuget Pakete für SQLite

Hier reicht nur die Pakete zu installieren für SQLite. Die Installation MS SQL oder PostgreSql gehe ich nur ansatzweise an, um später zu zeigen, inwieweit ein Wechsel zu einer anderen Datenbank möglich ist. Deshalb werden bei den beiden Datenbank Technologien, dessen Nuget Pakete nicht installiert.

  • SQLite-net-standard 1.5.1
  • PostgreSQL
  • MS SQL

PoC - Proof of Concept

Der Wechsel zu einer anderen Datenbank. Die Funktionalitäten würde man zunächst in der Solution unter dem Projekt Komponenten hinterlegen. Das würde bedeuten, dass die Abhängigkeiten zu den drei Datenbanken in diesem Projekt liegen und später vorrausetzen, dass die Bibliotheken zu den anderen zwei immer gefordert werden, obwohl nur eine der möglichen Datenbank verwendet wird.

 

-> Option 1

Alles so lassen und später die Pakete rausnehmen, die man nicht braucht.

 

-> Option 2

Schnittstelle und Datenbanken in eigene Projekte verschieben. Über das Projekt ‚Componente' wird je nach Einstellung entschieden, welche Datenbank und Bibliotheken geladen werden soll.

 

Qual der Wahl

Bei einem Hobby Projekt reicht in der Regel die erste Option aus, solange man das für sich nur nutzen möchte. Die Anwendung hat keine großen Performance Ansprüche und wenn Probleme auftreten, kann man diese selbst schnell lösen.

Warum Option 2? Nicht wegen der Performance, sondern die Möglichkeit statt einer Lokalen Datenbank, auf eine zentrale Datenbank zu wechseln. Aber wenn diese Option nicht genutzt wird, sollten zumindest keine Probleme entstehen mit Referenzen, die nicht genutzt werden.

 

Erweiterung der Solution

Ein Projekt muss angelegt werden für die Datenobjekte und Schnittstelle, die ich hier 'SharedBasis'. Und für jede zu verwendeter Datenbank bekommt ein eigenes Projekt und erhält die Referenz zu dem Projekt 'SharedBasis'.



Was kommt in 'SharedBasis'

Hauptsächlich die Grunddaten Objekte und Schnittstellen, mehr sollte da nicht hineinkommen. Im Folgenden belasse ich mich auf die Schnittstelle für das Abrufen und Aktualisieren der Daten. Das Datenobjekt Artikel bekommt nur wesentliche Informationen, erst über dem ArticleSubItem wird der Umfang des Artikels ausgebaut. Darauf gehe ich in einen anderen Blogeintrag weiter darauf ein.

 public interface IArticleSubItem  
 {  
   long Id { get; set; }  
   long ArticleId { get; set; }  
 }  

Für den Ausbau der zu definierenden 'SubItems', ist für den aktuellen Stand noch nicht erforderlich. Letzten Endes gehe ich nur auf Grundlagen ein, die aber soweit reichen müssen, damit sich für mich die weiteren Details erschließen.

 

Ein Projekt pro Datenbank

Die Verbindung zu den Datenbanken sind eigentlich bei allen sehr ähnlich und doch werden diese getrennt behandelt in jeweils einem Projekt. In jedes Datenbank Projekt muss einmal die Reference 'WarehouseManagement.SharedBasis' hinzugefügt werden. Für ein wenig Ordnung habe ich die Projekte in einen Solution Ordner 'Data' angelegt.


Interface einsetzen

Wie bereits im Projekt 'WarehouseManagement.DatabaseSQLite' zu sehen ist, habe ich die Klasse DatabaseConnector.cs angelegt, bzw. wurden die vorhandenen Klassen mit den Namen 'Class.cs' umbenannt und das Interface 'IDatabaseConnector' implementiert.

 public class DatabaseConnector : IDatabaseConnector  
 {  
   #region interface methods  
   public void CreateTable<TTable>()  
   {  
     this.Execute(db => db.CreateTable<TTable>());  
   }  
   public IEnumerable<ArticleItem> GetAll()  
   {  
     throw new NotImplementedException();  
   }  
   public ArticleItem GetById(long id)  
   {  
     throw new NotImplementedException();  
   }  
   public void Insert(ArticleItem articleItem)  
   {  
     throw new NotImplementedException();  
   }  
   public IArticleSubItem SubItem_GetById(long id)  
   {  
     throw new NotImplementedException();  
   }  
   public void Update(ArticleItem articleItem)  
   {  
     throw new NotImplementedException();  
   }  
   public void Update(IArticleSubItem subItem)  
   {  
     throw new NotImplementedException();  
   }  
   #endregion  
 }   

Attribute einsetzen

Man kann den Namen der Datenbank Technologie in der Eigenschaft weglegen, erfordert jedoch, dass eine Instance vom 'DatabaseConnector' gestartet wird. Oder erweitert den Klassennamen zu 'DatabaseConnector' mit den Technologienamen. Beides ist nicht schön und deshalb passt die Verwendung von selbst erstellten Attributen, um dort den Technologienamen abzulegen.

 public class DatabaseConnectorNameAttribute : Attribute  
 {  
   public DatabaseConnectorNameAttribute(string name)  
   {  
     this.Name = name;  
   }  
   public string Name { get; }  
 }  

So bleibt der Grundname und dennoch können die Klassen von ihrer Technologie unterschieden werden.

 [DatabaseConnectorName("SQLite")]  
 public class DatabaseConnector : IDatabaseConnector  
 {  
 ...  
 [DatabaseConnectorName("PostgreSQL")]  
 public class DatabaseConnector : IDatabaseConnector  
 {  
 ...  
 [DatabaseConnectorName("MS SQL")]  
 public class DatabaseConnector : IDatabaseConnector  
 {  
 ...  

Test anlegen

Für das Erfassen der Datenbank Verbindungen (DatabaseConnector) wird im Projekt 'WarehouseManagement.Components' keine Reference der Datenbank Projekte hinzugefügt. Stattdessen soll das zur Laufzeit passieren und hierfür soll eine Klasse die Aufgabe übernehmen. Hier muss jedoch die 'WarehouseManagement.SharedBasis' Reference hinzugefügt werden, um später die Schnittstelle aus den Datenbank Projekten zu erkennen.

Der erste Unit Test soll die kompilierten DLLs aus den Datenbank Projekten einlesen. Beim ersten Mal und nach Änderungen der Datenprojekten, muss darauf geachtet werden, dass diese einen Aktuellen Bild hinterlegen. Oder einfach die gesamte Solution Inhalt neu kompilieren.

 [TestClass]  
 public class DatabaseConnectionTest  
 {  
   [TestMethod]  
   public void ReadDlls()  
   {  
     // arrange  
     // act  
     var result = DatabaseConnection.GetDatabaseConnectors();  
     // assert  
     Assert.IsNotNull(result);  
     Assert.AreEqual(3, result.Count());  
   }  
 }  

DLL Dateien Namentlich filtern

Da alle Projektinhalte in einem Ordner landen, werden beim Abrufen der DLL-Dateinamen auch die mitgenommen, die keine Verbindung haben zu einer Datenbank.

 

 

Deshalb sollte hier der einfach nach den Projektnamen 'WarehouseManagement.Database' gefiltert werden.

Nun läuft der Test durch, ohne dass die DLLs gefunden wurden. Das liegt daran, dass für das Unit Test Projekt die DLLs nicht als Referenz genannt sind. Deshalb müssen diese noch rüber kopiert werden aus den Bin Order des jeweiligen Datenbank Projektes. Damit man über den Dateiexplorer nicht immer händisch rüber kopieren muss, kann dies über den Test Projekt Eigenschaften angeordnet werden mit den Build -> Output -> Post-build event und Xcopy

xcopy $(SolutionDir)WarehouseManagement.DatabaseSQLite\bin\Debug\net6.0\WarehouseManagement.DatabaseSQLite.dll $(SolutionDir)WarehouseManagement.Components.Test\bin\Debug\net6.0 /Y  
 xcopy $(SolutionDir)WarehouseManagement.DatabaseMsSQL\bin\Debug\net6.0\WarehouseManagement.DatabaseMsSQL.dll $(SolutionDir)WarehouseManagement.Components.Test\bin\Debug\net6.0 /Y  
 xcopy $(SolutionDir)WarehouseManagement.DatabasePostgreSQL\bin\Debug\net6.0\WarehouseManagement.DatabasePostgreSQL.dll $(SolutionDir)WarehouseManagement.Components.Test\bin\Debug\net6.0 /Y  

Methode zu Ende ausschreiben

Nun sollte die Methode zum Einlesen der Datenbank Verbindungsstücke aus den DLLs gelesen werden können. Die Folgende Ausführung ist sehr kompakt geschrieben, welches durch Linq, Lamda-Ausdrücken und Reflection realisiert wird. Hier empfehle ich, die von euch gewohnte Ausschreibung des Codes zu verwenden. Dann fällt ein späterer Einblick in die einzelnen Codestellen leichter.

 public class DatabaseConnection  
 {  
   public static IEnumerable<string> GetDatabaseConnectors()  
   {  
     return Directory  
       .GetFiles(Environment.CurrentDirectory)  
       .Where(file => file.Contains("WarehouseManagement.Database"))  
       .Select(Assembly.LoadFrom)  
       .SelectMany(assembly => assembly.GetExportedTypes())  
       .Where(type => typeof(IDatabaseConnector).IsAssignableFrom(type))  
       .Select(type => type.Name);  
   }  
 }  

Ansatz fertig

Und lasst euch in euerer Programmierung nicht reinreden, wenn eure Lösung funktioniert. Ein besser gibt's nicht. Sondern nur andere Lösungsformen.

Außerhalb des Themas 'Tool Lagerverwaltung' beschreibe ich den weiteren Ausbau der Datenbank Verbindungen in einen für sich geschlossenen Blogpost. In diesen ist der Ansatz gezeigt, wie sowas aufgebaut sein könnte.


Codestand: Database and interface

 

Übersicht

Teil 1 - Wiki anlegen in GitHub

Teil 2 - Neue Solution

Teil 3 - Noch ein Wiki anlegen

Teil 4 - Datenbank und Schnittstelle

Teil 5 - Konzept ausschreiben

Teil 6 - Umzug nach .NET MAUI

 

Links

 

Gehäuseentwurf für Signalleuchten (Part 3)

Der ESP32-6 benötigt eine ausreichende Platzierung, so das die Verbindung zum USB Stecker möglich ist. Da kam mir in den Sinn, dass ein Aufl...