Fortuna Entwickler Blog

Hier wird Ihnen geholfen

Umstellung einer Assembly von .NET Framework auf.NET Standard

Zunächst wählt man ein projekt aus, dass man auf .NET Standard umstellen möchte und wechselt im Datei Explorer zu diesem Projekt:


Jetzt legt man sich eine Sicherheitskopie irgendwo von diesem Ordner an, in dem man sich befindet und wechselt anschließend wieder ins Visual Studio.

Dort entfernt man das Projekt aus der Solution:


Jetzt kann man das Projekt auch auf Datei-Ebene löschen:



Als nächstes wird das Projekt neu in der Solution angelegt. Auch hier muss die selbe Stelle genommen werden, wo das alte Projekt lag:



Als Typ nimmt man die Class Library für .NET Standard. Natürlich mit der Sprache C#:



Der Name muss der selbe sein, den das Projekt auch schon früher hatte und der Ort muss der selbe sein, wo das alte Projekt vorher auch schon lag:



Jetzt ist das Projekt in der Solution. Die enthaltene Klasse "Class1.cs" kann man direkt einfach löschen:



Nun muss man den Code wieder in das Projekt packen. Dazu klickt man mit rechts auf das projekt und sagt "Add existing Item":



Im darauf folgenden Dialog wählt man aus seiner Sicherheitskopie sämtliche Code-Dateien aus (*.cs). Sollte das Projekt früher eine Ordnerstrukur gehabt haben, so muss man darauf achten, dass diese 1:1 nachgebildet wird. Sie muss auch wieder so vorhanden sein wie früher. Bitte jetzt nicht umstrukturieren!!!!:



Man sieht jetzt, dass die Dateien wieder im Projekt sind und die CS Dateien sind auch nicht ausgecheckt, sondern eingecheckt, weil sie den selben Namen haben wie vorher und die selbe Struktur und den selben Inhalt:



Nun kompiliert man zum ersten Mal das neue Projekt. Es wird dann möglichweise zu Fehlern kommen, da das alte Projekt Referenzen auf andere Fortuna Projekte hatte oder NuGet Packages eingebunden waren. Die muss man dann natürlich wieder hinzufügen. Man kann allerdings nur Fortuna Projekte referenzieren, die bereits auf .NET Standard umgestellt sind. Daher ist es hier sehr wichtig, dass man auf die Reihenfolge achtet, wenn man Fortuna Projekte auf .NET Standard umstellt!!!! Dies prüft man am besten am Anfang!!!!

Wenn sich das Projekt selbst kompilieren lässt, muss man prüfen, ob sich auch noch die Solution kompilieren lässt. Also macht man ein Build auf die Solution. Man wird dann wieder einige Fehler bekommen, denn durch das Entfernen des Projektes am Anfang aus der Solution, wurden bei vielen anderen Projektten auch die Referenz entfernt. Man erkennt dies in der Solution auch gut daran, dass diese Projekte in der Solution jetzt ausgecheckt sind (roter Haken).

Bei all diesen Projekten muss man nun die Referenzb auf dieses Projekt wieder reinnehmen.

Lässt sich die Solution anschließend wieder komplett durchkompilieren, geht es an die Feinheiten. Dazu klickt an doppelt auf das neue Projekt drauf, so dass man die Config des Projektes als XML sieht:


In der zweitenPropertyGroup fügt man nun noch folgende Zeile ein:

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

Sieht dann so aus:



Jetzt kann man alles einechecken in der Solution, also auch die Solution selber und die Projekte bei denen die Referenzen neu gesetzt werden mussten. Am besten hat man sich für die Umstellung des Projektes ein eigenes Workite angelegt.

Die Umstellung auf .NET Standard ist damit abgeschlossen.


NLog in in ASP.NET Core einbinden

Um NLog in einer ASP.NET Core Webanwendung oder Sevice zu verwenden, müssen zunächst die entsprechenden NuGet Packages installiert werden in der Anwendung. Dies sind die folgenden Packages

  • NLog
  • NLog.Web.AspNetCore
Hierbei bitte darauf achten, dass die Versionen verwendet werden, die auch bereits bei anderen Fortuna Anwendungen verwendet werden, damit es einheitlich bleibt.

Als nächstes wird eine lokale Umgebungsvariable "IS_LOCAL" in der ASP.NET Core Anwendung angelegt. Der Wert wird auf "true" gesetzt:



Anschließend wird die "Program.cs" Datei angepasst. Diese sieht ursprünglich ungefähr so aus:


Die "main"-Methode (Kann von Fortuna.Web.Abschluss kopiert werden) muss so angepasst werden:



Und die "CreateWebHostBuilder"-Methode so:



Als nächstes müssen die CONFIG-Dateien für NLog hinzugefügt werden. Hier bietet es sich an, diese von einer anderen Fortuna Anwendung wie Fortuna.Web.Abschluss zu kopieren, damit das Logging in den Anwendungen einheitlich passiert. Es müssen dann lediglich die Pfade in den CONFIG Dateien angepasst werden. Ich habe die Dateien auch mal als Anhang an diesen Post gehangen:

nlog.config (1,5KB)nlog.Development.config (1,9KB)nlog.Production.config (1,9KB)nlog.Test.config (1,9KB)

Nun benötigt man noch auf den Servern Ordner, wo die Logs hingeschrieben werden sollen (aktuell machen wir mit NLog File Logging).

Man findet den allgemeinen Log-order wie folgt:
  • \\webentw4\Logs
  • \\webtest4\Logs
  • \\web4\Logs
Jede Anwendung bekommt dort drin einen eigenen Unterordner (z.B. Dashboard). Außerdem befindet sich in den Logs-Ordnern ein "_Archive"-Ordner, wo auch jede Anwendung einen eigenen Unterordner drin besitzt.

Um nun die neuen Ordner anzulegen muss man sich per RDP auf die einzelnen Server connecten und die Unterordner in Logs und _Archive anlegen, da nur der Admin Schreibrechte hat und die IT-User lediglich Leserechte. Das Anlegen der Unterordner sollte so gemacht werden, wie die bereits vorhanden Ordner, damit diese Struktur beibehalten wird. Die Pfade zu den Ordnern müssen dann in den NLog CONFIG-Files eingetragen werden.

Nun ist alles fertig konfiguriert und der Logger kann im Controller der ASP.NET Core Anwendung verwendet werden. Da durch die Anpassung der main-Methode die Dependency Injection aktiviert wurde, kann der Logger ganz einfach im Konstruktor des Controllers verwendet werden:


Man benötigt einfach eine globale Variable vom Typ ILogger im Controller und man nimmt den ILogger auch im Kopf des Konstruktors vom Controller mit auf. Falls es noch keinen Konstruktor gibt im Controller, leget man einfach einen an. Im Konstruktor selbst weist man nur der globalen Variable die lokale Konstruktor-Variable zu. Nun kann man den Logger überall im Controller verwenden. Dies macht man für jeden Controller der Anwendung:


Das Ergebnis des File Logging sieht dann wie folgt aus:



Models in Fortuna 5.0

Fortuna.models.Verwendung

Models werden bei Fortuna 5.0 in zwei zu nennenden Bereichen eingesetzt: 

  • Kommunikation zwischen Fortuna.Services und Fortuna.Clients. Hier beschreiben die Models die Objekte, die mittels REST übermittelt werden
  • Kommunikation zwischen Fortuna.Clients und der WebApplikation. Hier beschreiben die Services die Objekte, welche nach extern (z.B. HTML Frontend) geliefert werden.

Für die Kommunikation zwischen Service und Client bietet es sich an, zwischen Request- und Response-Models (Kommunikationswege aus Sicht des Clients) zu unterscheiden.

Bei der Kommunikation zwischen Client und WebApplikation wird man nur zum Teil gesonderte Models benötigen. Geht es um die reine Anzeige / Ausgabe von Daten, so ist möglicherweise das Response-Model aus der Service-Client Kommunikation direkt verwendbar. Bei der Datenpflege ist je nach Anwendungsfall zu entscheiden, ob die vorhandenen Models verwendet werden können, oder ob gesonderte Models benutzt werden und für die Weitergabe an die Services eine Transformation eingesetzt wird.

Ein Model beschreibt immer nur die Struktur eines Objektes und stellt keine eigene Funktionalitäten bereit. Datenmanipulationen sind nur in geringem Umfang im Model umzusetzen (Beispiel: aus den Eigenschaften Vorname und Nachname ein Feld Name bereitstellen, welches dann aus Nachname, Vorname oder auch Vorname Nachname besteht, oder eine IBAN in maschinenlesbarer und mit Leerstellen aufbereiteter Form zur Verfügung stellen).

Namespaces

Models liegen im Namespace Fortuna.Models. Applikationsspezifische Models entsprechend unter Fortuna.Models.MeineApp. Für Models, welche für Querschnittsfunktionalitäten genutzt werden, ist zu prüfen, ob diese entsprechend der Verwendung gruppiert werden können (also z.B. Fortuna.Models.Kommunikation.MeinModel). Ansonsten erfolgt die Zuordnung unter Fortuna.Models.Common (also z.B. Fortuna.Models.Common.MeinModel).

Bei der Unterscheidung nach Request- und Response-Models ist der Namespace Applikationsspezifisch entsprechend zu unterteilen (also Fortuna.Models.MeineApp.Request und Fortuna.Models.MeineApp.Response).

Da die gleichen Models möglicherweise in verschiedenen Projekten einer Applikation verwendet werden, sind diese in einem eigenen Projekt abzulegen. Das Projekt bekommt den Namen des Namespaces ungeachtet von möglicher Unterscheidung nach Request und Response (also im Beispiel Fortuna.Models.MeineApp.csproj). Models für Querschnittsfunktionalitäten werden in einem Projekt Fortuna.Models.Common abgelegt. Dies gilt auch bei einer vorgenommen Gruppierung.

Namespaces in Fortuna 5.0

Applikationsspezifische Namespaces

Clients konsumieren die von den Services zur Verfügung gestellten REST-Services. Ein Client konsumiert alle vom entsprechenden Service zur Verfügung gestellten Funktionalitäten. Ein Client konsumiert immer nur die Funktionalitäten genau eines Services. Der Client ist unter dem Namespace Fortuna.Client angeordnet. Ein Client für die Services aus Fortuna.Services.Vertrag wäre also unter Fortuna.Client.Vertrag zu finden. Der Client wird immer als eigenes Projekt implementiert; der Projektname entspricht dem Namespace (also im Beispiel Fortuna.Client.Vertrag.csproj). Die Daten zwischen Client und Service werden über Models ausgetauscht. Weiteres zu Models siehe unter http://webentw3.gutingia.local/blog/post/2017/01/26/models-in-fortuna-5-0.

Der Client selbst wird dann von der eigentlichen WebApplikation verwendet. Die WebApplikation ist im Namespace Fortuna.Web angeordnet (und wäre dann unter Fortuna.Web.MeineApp zu finden). Das Projekt der WebApplikation entspricht auch hier dem Namespace (also im Beispiel Fortuna.Web.MeineApp.csproj). Eine WebApplikation kann mehrere Clients benutzen um Funktionalitäten abzubilden. Genauso können Clients in verschiedenen WebApplikationen Verwendung finden.

Entities, Datalayer, Models und Services

Entities sind unter Fortuna.Entities eingeordnet. Entitäten sind unabhängig von einer Webapplikation und entsprechen letztlich einer Tabelle oder View im Datenbankuser 'Fortuna'. Um bereits in den Entities unterscheiden zu können, sind Tabellen bereits mit T_[Objekt], Views mit V_[Objekt] zu benennen. Entsprechend beginnen dann die Entitäten auf Tabellen mit 't' (t[Objekt]) und Views mit 'v' (v[Objekt]).
Beispiel: Personen liegen in der View V_Person und daraus ergibt sich die Entität (inkl. Namespace) Fortuna.Entities.vPerson

Bei der Anlage von Tabellen und Views ist entsprechend schon sorgfältig auf die Namensvergabe zu achten.

Entities werden in einem eigenen Projekt 'Fortuna.Entities' angelegt. Sollten im Laufe der Entwicklung sehr viele Entitäten anfallen, ist zu überlegen, ob man diese thematisch in mehrere Projekte aufteilt, der Namespace bleibt Projektübergreifend aber immer Fortuna.Entities.
Die Aufteilung sollte in keinem Falle anwendungsspezifisch sein, da der allergrößte Teil der Entitäten immer in verschiedenen Applikationen Verwendung finden wird. Wenn man bereits zu Beginn einer neuen Applikation ansehen kann, daß eine Reihe von Entitäten sehr Anwendungsspezifisch sein werden und aller Voraussicht nach nur für diese eine Anwendung zur Verfügung stehen müssen, so kann für diese ein gesondertes Projekt erstellt werden, welches dann aber auch unter dem Namespace Fortuna.Entities agiert.

Die Entities sind in einem Projekt mit einen Datenlayer abzulegen. Dieser Datenlayer sorgt dafür, daß die Informationen aus den Entities über Models weitergegeben werden. Weiteres zu Models siehe unten. Die Methoden des Datenlayers sind nach außen nicht sichtbar. Die Objekte, welche über den ORM Mapper für die Entities erzeugt werden, werden nicht über Services nach 'außen' verwendet; es ist immer eine Transformation in Models vorzunehmen.

Die Weitergabe der Models an die weiter oben liegende Clients erfolgt über Services, welche über REST ansprechbar sind. Services sind meist recht atomar und applikationsunabhängig. Sie wären also unter Fortuna.Services zu finden. Die Namensvergabe der Objekte sollte Aufschluss über die darin gebündelten/verwendeten Entities geben (Beispiel: Fortuna.Services.Vertrag bündelt Entitäten zu Vertrag, Vertragsteil, Vertragswerte etc.)

Entities, Datenlayer und Services können je nach Umfang in einem gemeinsamen Projekt abgelegt werden oder die Services von den Entities getrennt in unterschiedlichen Projekten. Bei einem Projekt für alles, ist das Projekt thematisch passend zu benennen (etwa Fortuna.Services.Vertrag.csproj). Bei getrennten Projekten werden als Projektnamen die Namen der Namespaces von Entitäten und Services verwendet (also Fortuna.Entities.Vertrag.csproj und Fortuna.Services.Vertrag.csproj)
Sollte es bei den Entitäten Applikationsspezifische Objekte geben, so erhalten diese auch einen eigenen Datenlayer, ggf. eigene Models und eigene Services. Gemeinsam sind diese dann auch in einem eigenen Projekt abzulegen. Der Applikationsname sollte sich im Projektnamen der Entities/des Datalayers/der Services wiederspiegeln.

Weitere Objekte

Eine ganze Reihe von Funktionen und Informationen sind unabhängige von einer bestimmten Applikation sondern mehrfach einsetzbar. In diesem Falle sind gesonderte Namespaces zu verwenden. Als Beispiele sind hier Fortuna.Globals und Fortuna.Configuration zu nennen. Bei der Vergabe von Namespaces ist darauf zu achten, daß diese nicht mit bestehenden Namespaces übereinstimmen. Vor allem die oben verwendeten Begriffe bei Anwendungsspezifischen Namespaces dürfen nicht verwendet werden.
So könnte es z.B. durchaus zu Problemen kommen, wenn eine Anwendung sowohl Fortuna.Services.MeineApp verwendet als auch Fortuna.MeinNamespace.Services.MeineApp. Insbesondere, wenn sich dann in den Namespace Klassen finden, die auch identische Namen haben.


Architektur Fortuna 5.0


Fortuna Anwendungen lassen sich grob in  drei Schichten teilen:

  • Datenbank
  • Daten- und sonstige Services
  • Webapplikation

Die Kommunikation zwischen den Schichten ist immer identisch, unabhängig davon, um welchen fachlichen Themenbereich es sich handelt.

Die Komponenten in den einzelnen Schichten folgen stets festgelegten Vorgehensweisen, Zugriffsarten und Namensgebungen, sodass man unabhängig der fachlichen Aspekte aus technischer Sicht schnell ein Zuordnung findet.

Alle weiteren Erläuterungen beziehen sich zwar auf die derzeitige Infrastruktur der Produktionsumgebung, sind aber auch in der Test- und Entwicklungsumgebung und bei Austausch einzelner Teile der Infrastruktur adaptiv anzuwenden. Die Beschreibungen beziehen sich - wenn nicht anders beschrieben - immer auf den Kontext Webapplikationen und Kommunikation nach Extern.

Datenbank

Alle Daten der Bestandsverwaltung sind auf DBPROD1 im User GUTINGIA abgelegt. Hinzu kommen noch Dokumente, die als Binärobjekte sind auf DBDOK1 im User DBDOK abgelegt sind. Letztere sind gesondert zu behandeln.

Neu hinzu kommt nun der User FORTUNA auf DBPROD1. Dieser User soll perspektivisch als einziger "Kontaktpunkt" für den Datenzugriff dienen und die Benutzer WEBDATA, WEBFRW und WEBSERVICES ersetzen. Webapplikationen verwenden letztlich nur noch diesen User zum Lesen und Schreiben von Daten. Hierbei gelten einige Regeln:

  • Der lesende Zugriff auf Daten geschieht ausschließlich über Views. Benötigt eine Anwendung also Informationen aus GUTINGIA, so ist auf Fortuna eine View zu erstellen, welche die Daten aus GUTINGIA holt und in FORTUNA bereitstellt. Die Webanwendung greift auf diese View zu.
  • Der schreibende Zugriff auf Daten geschieht ausschließlich im Datenbankuser FORTUNA. Daten, welche nachfolgend von GUTINGIA benötigt werden (Stichwort: Dunkelverarbeitung) werden von GUTINGIA aus diesen Tabellen gelesen.
  • Es findet kein direkter schreibender Zugriff auf Tabellen im Datenbankuser GUTINGIA statt.
  • Der lesende Zugriff auf Tabellen, welche unter FORTUNA liegen kann wahlweise direkt oder über eine View erfolgen. Hier wäre allerdings auch eine View zu bevorzugen, wodurch dann in den darüber liegenden Entitäten schon über die Entität selbst klar wäre, ob es sich um lesende oder schreibende Zugriffe handelt.

Daten- und sonstige Services

Jeglicher Datenbankzugriff (lesend und schreibend), Zugriff auf Dokumente, Mailversand, Versand von SMS und alle weiteren nicht fachlich definierten sind hier zu implementieren.

Der Zugriff (lesend und schreibend) auf die Datenbank erfolgt ausschließlich über das Entity Framework und der einzige zu verwendende Datenbankbenutzer ist FORTUNA.

Für die Kommunikation zu WebApplikationen werden in dieser Schicht REST-Services zur Verfügung gestellt welche über dedizierte Clientbibliotheken konsumiert werden können. Alle Funktionalitäten sich auf diese Weise zu kapseln.

In der Entwicklung von Klassen und Funktionen ist die korrekte Benennung von Namespaces zu beachten (Weiteres hierzu siehe unter http://webentw3.gutingia.local/blog/post/2017/01/26/namespaces-in-fortuna-5-0)

Webapplikationen

Jegliche Anwendung, die für eine Kommunikation nach Extern verwendet wird, ist in dieser Schicht angesiedelt. Dies betrifft natürlich vor allem Anwendungen mit einem HTML-Frontend, aber auch Services, welche für externe Partner zur Verfügung gestellt werden.

Der Zugriff auf die Daten- und sonstigen Services (siehe oben) erfolgt ausschließlich über Client Bibliotheken, welche dann über REST kommunizieren. Es gibt keinen direkten Zugriff auf Funktionalitäten dieser Schicht.