Fortuna Entwickler Blog

Hier wird Ihnen geholfen

SonarCloud - Master Solution - Analyse

Für die Master Solution braucht es einen anderen Ablauf. Damit es manuell funktioniert müssen folgende Schritte erfolgen.

Vorbereitung

Bevor die eigentliche Analyse gestartet werden kann, müssen verschiedene Schritte vollzogen werden.

1. Herunterladen des Sonarscanners. 

Damit die Zeilen von Code analysiert werden können, brauchen wir den Client von SonarCloud dazu.

Dort sollte die net4.6 Version genommen werden, aufgrund des Mixes von C++ und C# in der Solution. 

Die .zip Datei nach dem Entpacken an einen geeigneten Platz legen (z.B. unter C:\\SonarCloud). Im späteren Schritt, werden wir den Pfad noch brauchen.

2. Herunterladen des Buildwrappers

Damit die Logdateien während der Analyse gefüllt werden, wird der Buildwrapper von SonarCloud benötigt.

Die .zip Datei nach dem Entpacken an einen geeigneten Platz legen (z.B. unter C:\\SonarCloud). Im späteren Schritt, werden wir den Pfad noch brauchen.

3. Setzen der Umgebungsvariablen

Damit verschiedene Einstellungen direkt gefunden werden müssen folgende Befehle in einer Powershell Konsole ausgeführt werden:

[Environment]::SetEnvironmentVariable("SONAR_SCANNER_OPTS", "-Dhttps.proxyHost=10.98.98.222 -Dhttps.proxyPort=8080",[System.EnvironmentVariableTarget]::Machine)
Kontrolliert werden kann dies mit dem Befehl:

[Environment]::GetEnvironmentVariable("SONAR_SCANNER_OPTS", [System.EnvironmentVariableTarget]::Machine)

Setzen der Path Variable mit allen vorherigen Pfaden (anpassen der Pfade beachten):

[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\SonarCloud\build-wrapper-win-x86" + ";C:\SonarCloud\sonar-scanner-msbuild-5.3.1.36242-net46",[System.EnvironmentVariableTarget]::Machine)

Kontrolliert werden kann dies mit diesem Befehl:

[Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::Machine)

3. Herunterladen des aktuellen Java JDKs

Für den Scanner wird Java mindestens in der Version 11 benötigt.


Analyse - Routine

Token können innerhalb der SonarCloud Oberfläche als Administrator erzeugt werden:



Zum Starten der Analyse muss zuerst auf der Kommandozeile in das Verzeichnis navigiert werden in der die Master Solution liegt. Folgend werden die Befehle ausgeführt:

SonarScanner.MSBuild.exe begin /k:"Master_MyLife" /o:"mylife-leben" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.login="bfe291ef26f056234f4782e8660fbefe541fc12f" /d:sonar.cfamily.build-wrapper-output="build_wrapper_output_directory"


build-wrapper-win-x86-64.exe --out-dir build_wrapper_output_directory MSBuild.exe Master.sln /t:Rebuild /p:Configuration=Release /p:Platform=x64
SonarScanner.MSBuild.exe end /d:sonar.login="bfe291ef26f056234f4782e8660fbefe541fc12f" 

Design Projekt .NET Core Web API

Um in der Zukunft einen einheitlichen Aufbau aller Projekte zu haben, wird hier ein Beispielaufbau einer Anwendung erklärt. Alle Angaben beziehen sich auf .NET Core Web APIs.

Aufbau Struktur

In jedem Projekt sollte es folgende Struktur geben:
  • Controllers => Beinhaltet jegliche Logik zu den Endpoints
  • Services => Beinhaltet jegliche Logik zur Verarbeitung der Daten (Anfragen an Datenbank, Manipulation von Daten, Anfrage an andere APIs usw.)
  • Mappings (wenn gebraucht) => Automapper Konfiguration Dateien
  • Resources (wenn gebraucht) => alle Projektspezifischen Ressourcen wie AppSettings Konfigurations Model 
  • Interfaces (wenn gebraucht) => Selbsterklärend
  • Extensions (wenn gebraucht) => Selbsterklärend
  • Models (wenn gebraucht)  => Selbsterklärend

Folgende Nuget-Pakete sollten in allen Anwendungen installiert sein:

Damit alle Pakete korrekt funktionieren müssen folgende Dinge in das Programm eingebaut werden.

Microsoft.AspNetCore.Mvc.NewtonsoftJson
Folgendes muss unter in der Startup.cs Datei in der Methode ConfigureServices reingeschrieben werden. Dies setzt die neue JSON Libary fest.


Swashbuckle.AspNetCore

Um Swagger nutzen zu können muss an zwei verschiedenen Stellen in der Startup.cs Datei Code eingefügt werden.

Zuerst in der ConfigureServices Methode nach der AddControllers Funktion muss Swagger hinzugefügt werden.

Danach muss Swagger noch aktiviert werden, dazu muss in der Configure Methode folgendes geschrieben werden.

Hier sind vor allem die Zeilen 35-39 wichtig. Der Aufbau der restlichen Zeilen kann aber übernommen werden. Hier gilt zu beachten, dass, dass der Name der API jeweils angepasst wird.

NLog NLog.Web.AspNetCore

http://webentw4/Blog/post/2020/05/12/nlog-in-in-asp-net-core-einbinden


ContentTypes

Die ContentTypes werden zentral in der Startup.cs eingestellt. Die meiste Zeit wird nur application/json benötigt. Sollten weitere benötigt werden, muss geschaut werden, ob diese zentral eingestellt werden oder nur für eine bestimmte Funktion. Zu beachten ist, wenn die ContentTypes überschrieben werden müssen an der Funktion dann alle angegeben werden.

Aufbau des Controllers

Ein Controller dient als Sammelstelle aller Endpoints. Hier ist nochmal zu erwähnen, jegliche Logik sollte in die Services geschrieben werden und nur der Zugriff auf die Services bzw. auf die Endpoints sollte im Controller beschrieben werden.

Im ersten Schritt sollte ein BaseController implementiert werden. Dieser beinhaltet alle allgemein gültigen Funktionen zur Verarbeitung von HTTP Requests bzw. Responses.


Der BaseController sollte zwingend die HandleError Methode besitzen, damit alle Fehler geloggt werden. Außerdem ist wichtig, dass der Controller mit dem Attribute ApiController ausgestattet wird, damit beim Kompilieren erkannt wird, dass alle zu erbenden Klassen bzw. Controller Teil der API sind.

Wird dann ein Blick auf die "richtigen" Controller geworfen, müssen ein paar Dinge erledigt werden. Zuerst muss die Route bestimmt werden. Diese wird mit api/[controller] angegeben. Daraus resultiert in dem Fall immer der Pfad api/vertrag/xxx. Dies hat den Vorteil, dass wir nicht in jeder Funktion gewisse Teile des Pfads doppelt schreiben müssen. 
Darüber hinaus muss der Logger und die benötigten Services per Dependency injection eingebunden werden.


Aufbau eines Endpoints

Alle Endpoints sollten folgenden Aufbau besitzen:

  • Http Request Methode mit Pfad => Funktionsname bzw. Abwandlung angeben und jeweilige Parameter mit Datentypen
  • Consume/Produces Types => Content-Types => In der Regel brauchen wir nur application/json (allgemein) => Diese werden vorher zentral in der Startup.cs eingestellt und nur genauer angegeben, wenn zusätzliche fehlen
  • ProducesResponseType => Rein zu Dokumentationszwecken gedacht, aber sinnvoll zum testen => Alle Rückgabetypen mit Statuscodes eintragen
  • Rückgabetyp => Sollte eigentlich immer IActionResult sein, da wir unterschiedliche Rückgabetypen haben wie unter ProducesResponseType zu sehen ist, außer bei asynchronen Aufrufen, dann sollte es Task<IActionResult> sein
  • Funktionsname => Titel der Funktion, wenn asynchron an den Namen immer ein Async anhängen
  • Try-Catch-HandleError => Für unerwartete Fehler wie z.B. in der Datenbank 

NPM Config - MSBuild Pfad & Version bearbeiten

Vor kurzem hat Microsoft .net Version 5 auf den Markt gebracht. Dadurch haben sich in der Verwaltung von Angular Anwendung in Bezug auf asp.net Core Anwendungen ein paar Einstellungen geändert. Beim ausführen der Paketinstallation in einer Angular Anwendung über npm mit "npm install" führte vermehrt zu dem Problem, dass die MSBuild.exe nicht gefunden werden konnte.
Um dieses Problem zu beheben muss npm mitgeteilt werden, wo die .exe liegt und welche Version es nutzen soll.

Um den Pfad einzustellen muss folgender Befehl ausgeführt werden (ggf. Pfad anpassen):

  • npm config set msbuild_path "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe"
Um die Version einzustellen muss folgender Befehl ausgeführt werden (ggf. Versionsnummer anpassen):

  • npm config set msvs_version 2017
Stand 01.12.2020 muss Version 2017 noch angegeben werden, da NPM 2019 als Keyword nicht registrieren kann.

Ggf. kommt dieses Problem auch durch das builden von dem Package node-gyp in Verbindung mit einer Node.js Version 12+.

Weitere Informationen:
  • https://github.com/nodejs/node-gyp/issues/1753

Was ist der Unterschied zwischen InProcess und OutOfProcess Hosting in asp.net Core?

Um eine asp.net Core Anwendung hosten zu können, gibt es zwei Möglichkeiten. Zum einen InProcess und zum anderen OutOfProcess. Im Folgenden wird der Unterschied erklärt:

  • InProcess: Das Hosting wird über den IIS-Server bereitgestellt.image-7.png (840×221)
  • OutOfProcess: Das Hosting wird über den Kestrel Web Server bereitgestellt.image-9.png (771×174)

Was ist der Unterschied zwischen den beiden Web-Servern? Der IIS ist ein seit über 15 Jahren aktiver Webserver, der gleichzeitig die größtmöglichste Abwärtskompatibiltät gewährt. Dies fällt zu Lasten der Performance. Um diesem Prozess entgegen zu wirken, wurde der Kestrel Web Server erstellt. Der Fokus liegt hier auf neueren Technologien, wie z.B. Async Unterstüztung.

Weitere Informationen:

UseStaticFiles() vs UseSpaStaticFiles() vs UseSpa()

Wenn eine asp.net-Core Anwendung mit einem Frontend wie z.B. Angular geschrieben wird, müssen meist statische Dateien bereitgestellt werden. Im Folgenden wird eine Erklärung dafür gegeben:

  • UseStaticFiles: Der Befehl gibt an, dass statische Dateien wie js, css, html usw. bereitgestellt werden. Typischerweise aus dem wwwrooot-Ordner, wobei dieser geändert werden kann. Die Dateien werden so wie sie erstellt worden sind dem Clienten zur Verfügung gestellt.
  • UseSpaStaticFiles: Der Befehl registriert eine Middleware, die dafür sorgt, dass Dateien aus dem Ordner bereitgestellt werden. Der Pfad wird dafür in de Funktion ConfigureServices => AddSpaStaticFiles konfiguriert.
  • UseSpa: Der Befehl registriert eine Middleware, welche alle Parameter enthält, um die Anwendung starten zu können. Z.B. aus welchem Ordner die Anwendung gestartet wird, in welcher Modus (Dev,Prod) und auch mit welchem Befehl die Anwendung gestartet wird (z.B. npm start).

Weitere Informationen:

Einbinden der Newtonsoft.Json Libary in einer .NET Core Anwendung

Seit .NET Core 3.0 hat Microsoft eine eigene Libary (System.Text.Json) freigegeben, um Operationen im JSON-Format durchzuführen. Darunter fällt auch das serialisieren und deserialisieren von Requests & Respones an eine API.

Leider hat die Libary noch ein paar Schwierigkeiten (Stand 09.10.2020) komplexere Datentypen, wie Dictionaries zu verarbeiten. Teilweise treten System.NotSupportExceptions auf, da die Libary, die Datentypen nicht unterstützt.

Damit das Problem umgangen werden kann, gibt es weiterhin die alt bekannte Newtonsoft.Json-Libary, die genutzt werden kann und auch mit komplexeren Datentypen umgehen kann.

Folgende Schritte müssen durchgeführt werden, um die Libary einzubinden:

  1. Newtonsoft.Json als Nuget Paket hinzufügen
  2. Als nächstes abhängig davon was für ein Projekt vorliegt, muss in der Startup.cs-Datei folgende Zeile geändert/hinzugefügt werden:

Sobald der Befehl abgeändert worden ist, wird das Paket automatisch genutzt in der Vearbeitung aller JSON-Operationen.

Ergebnisse Workshop 14./15. November 2017

nuGet packages

Es ist nicht sinnvoll, nuGet Packages in unsere Release-Zyklen einzubinden; dies widerspricht dem eigentlichen Sinn der nuGet Packages.

In einem Package sollen Funktionen umgesetzt werden, welche Produktunabhängig sind und allgemein Verwendet werden können, z.B. Mailversand, PDF Erstellung und Ähnliches

Empfehlung Herr Schmidt: 

Rückführung Produktspezifischer Packages in die jeweiligen Solutions

Neustrukturierung der Ordnerstruktur für Solutions

Alle Solutions in einem Ordner

Alle Webanwendungen in einem eigenen Ordner in einem Unterordner (‚web‘)

Alle Bibliotheken in einem eigenen Ordner in einigen wenigen Unterordnern (‚logic‘, ‚data‘)

Damit dann Kombination aus nuGet und Bibliotheken

Builds für Projekte automatisieren

Builds für nuGet Packages erstellen Symbole für Debugging, welche vorgehalten werden


Branches, Merges, etc.

Der aktuelle Stand kann so weiter geführt werden.

Das Verschieben von Branches kann möglicherweise in Zukunft wieder verfügbar sein. Derzeit gibt es keine andere Möglichkeit, als es so zu tun, wie wir es derzeit machen.

Herr Schmitt hat uns Alternativen zur derzeitigen Vorgehensweise aufgezeigt. Diese wären gggf. Zu diskutieren.


Event- und Fehlerlogging

Das Logging, so wie wir es derzeit betreiben ist weder performant noch sonst zu empfehlen.

Empfehlung Herr Schmidt: 

Einsatz von NLog

Umstellung aller Anwendungen auf einen Asynchronen Logger

Logging gegen einen WebService (in Azure)

Archivierung von Logs in Azure Storage

Ggf. Automatisiertes Herunterladen aus Azure Storage


Benutzer, Security

Eine eigene Benutzerverwaltung ist aufwendig, fehleranfällig und nur schwer abzusichern.

Empfehlung Herr Schmidt:

Microsoft Azure Active Directory

Vergleichsweise einfache Integration in bestehende Webanwendungen

Keine Verwendung mit VENTA möglich

Derzeit nur für Versicherungskunden

Integration in Azure Service Management, um APIs abzusichern (myPension z.B.)


Swagger

Der Einsatz von Swagger, so wie es derzeit bei uns verwendet wird, entspricht vorgesehenen Verwendung. Keine ToDos.


Oracle / Entities

Oracle ODP.NET ist nicht empfehlenswert, weil vergleichsweise langsam und schlechte Tool-Unterstützung.

DevArt for Oracle ist das Tool der Wahl für den Zugriff auf Oracle Datenbanken via Entities. Der fehlende Service seitens DevArt muss selbst geleistet werden: Neue Versionen als nuGet zur Verfügung stellen, eigene Versionshistorie vorhalten.


Sonstiges

Herr Schmitt empfiehlt die Anbindung unseres AD an Azure AD.

C# 7.0/8.0 bringt wenig sinnvolles Neues, was wir nicht schon kennen

ASP.NET Core bringt für uns keine Vorteile, solange wir nicht mit Docker und/oder anderen Betriebssystemen arbeiten.

Angular.js (oder React.js oder Vue) sind Frameworks, die interessant sein können und gesondert betrachtet werden müssten.

TypeScript als Ersatz für JavaScript ist empfehlenswert.

JavaScript in Webseiten selbst sollte überhaupt nicht benutzt werden, sondern immer in eigene Dateien (dann auch als TypeScript) ausgelagert werden.


Fotos


Themen_Workshop.pptx (578,3KB)

UnitTestDetector

Gelegentlich benötigt man in seinem Code eine Fallunterscheidung, ob die aktuelle Ausführung unter Realbedingungen, oder im Rahmen von UnitTests läuft. Beispiel wäre die Auswahl eines Verzeichnisses, welches im Realbetrieb unterhalb des Webverzeichnisses, bei UnitTests aber z.B. in C:\Temp\ verwendet wird:

if (UnitTestDetector.IsInUnitTest)
  return string.Format("C:\\Temp\\");
else
  return string.Format(@"{0}\App_Data\", HttpRuntime.AppDomainAppPath);

Die zugehörige Klasse "UnitTestDetector" findet man im Anhang

UnitTestDetector.cs (562,00 bytes)