Golo Roden: DDD und wolkenkit - live!

In dieser Woche finden verschiedene Veranstaltungen wie Meetups und Konferenzen statt, auf denen es um Domain-Driven Design und das wolkenkit geht. Im Folgenden ein kurzer Überblick für alle Interessierten.

AIT: Neu in TFS 2018: Variable Groups für das Release Management

Wer für große Umgebungen im Release Management bereits Variablen verwalten musste, weiß wie umfangreich diese werden können. Auch kann es vorkommen, dass Variablen über verschiedene Release Definitionen wiederverwendet werden. Dafür gibt es jetzt mit TFS 2018 Abhilfe in Form von Variable Groups. Eine Variable Group enthält dabei verschiedene Variablen. Diese können entweder im Klartext oder verschlüsselt abgelegt werden, wie man es aus einer Release Definition gewohnt ist. Somit erhält man eine Sammlung von Variablen, die später in einer Release Definition verwendet werden können.

Der Vorteil ist, dass man bei Änderung der Werte nur die Variable Group an einer zentralen Stellen anpassen muss (siehe Abbildung 1). Auch das Thema Sicherheit spielt hier eine wichtige Rolle. Denn wer eine Release Definition anlegt, hat noch lange nicht Zugriff auf die Passwörter und andere verschlüsselte Daten. In dem Fall können ausgewählte Personen mit Administrators-Berechtigung die Variable Group anpassen, Passwörter hinterlegen und Personen mit Users-Berechtigung können die Variable Group in ihren Release Definitionen verwenden. Somit kann ein neuer Releaseprozess einfach aufgesetzt werden, ohne dass der Ersteller direkt Zugriff auf alle Schlüssel und Passwörter haben muss.

image

Abbildung 1: Variable Group Web Dev

Verknüpfen einer Release Definition mit einer Variable Group

Um einer Release Definition eine oder mehrere Variable Groups hinzuzufügen, können ab TFS 2018 im Reiter Variables, zusätzlich zu den eigenen Variablen, jetzt auch Variable Groups verknüpft werden (siehe Abbildung 2). Durch die Verknüpfung werden automatisch alle Änderungen in der Variable Group auch mit in die Variablen der Release Definition übertragen und alle Variablen der Variable Group können innerhalb der Release Definition verwendet werden. Die Variablen sind dann für alle Environments in der Release Definition verfügbar.

Durch die Möglichkeit beliebig viele Variable Groups zu verknüpfen, kann man hier auch die Trennung der Variablen sehr feingranular definieren. Am Ende muss jedoch jeder für sich selbst entscheiden, welche Granularität für die Variable Groups angewendet wird, um möglichst eine große Wiederverwendbarkeit zu erreichen.

Wichtig ist, eine Konvention für die Namensgebung der Variablen festzulegen. Die Variablen gleich zu benennen in unterschiedlichen Variable Groups ergibt Sinn, wenn man sich für eine dieser Gruppen in der Release Definition entscheiden muss. Wenn Variable mit dem gleichen Namen in unterschiedlichen Variable Groups eingebunden werden, so entscheidet, die Reihenfolge, in welcher die Variable Groups verknüpft wurden.

Variable Groups mit einzelnen Environments zu verknüpfen, ist aktuell nicht möglich. Variable, die nur in einer bestimmten Umgebung Verwendung finden, können über ein Prefix oder Suffix erkenntlich gemacht werden. So kann man beispielsweise den Application Insights Key für alle Development-Umgebungen als AppInsightsKeyDev ablegen, um direkt zu erkennen, für welche Umgebung dieser gedacht ist.

image

Abbildung 2: Verknüpfen einer Variable Group mit einer Release Definition

Verknüpfen eines Azure Key Vault mit einer Variable Group

Ein kleiner Vorgeschmack was die Zukunft bringt, bietet das VSTS-Feature Link secrets from Azure key vault (siehe Abbildung 3). Mit dieser Option können in einer Variable Group Variable aus einem Azure Key Vault verknüpft werden. Dadurch liegen Passwörter nicht mehr im TFS oder in VSTS sondern werden in einem Azure Key Vault verwaltet. Die Vorteile hiervon sind die Sicherheit der abgelegten Passwörter und die Möglichkeit, die Verwendung der Passwörter zu überwachen.

Variable group with Azure key vault integration

Abbildung 3: Verknüpfen eines Azure Key Vault mit einer Variable Group

Holger Schwichtenberg: Video zur PowerShell Core 6.0 auf Windows, Linux und Mac

Die Aufzeichnung eines Dotnet-Doktor-Vortrags von der CIM Lingen 2017 gibt es bei YouTube zu sehen.

Martin Richter: Sicherheitsupdates von Microsoft führen zu Problemen mit dem ODBC Export nach Excel

Eigentlich habe ich nichts gegen Sicherheitsupdates. Genaugenommen bin ich ein Verfechter, dass Sicherheitsupdates sofort und umgehend installiert werden sollen.

Eigentlich ging das die letzten Jahre bei unserer Software ziemlich gut. Aber heute hat es uns voll erwischt. Dazu auf allen Plattformen von Windows 7, Windows 8.1 bis Windows 10.

Es kommt jetzt beim Anlegen einer Excel Datei über ODBC zu einer Fehlermeldung in der Form:

Reservierter Fehler (-5016); es gibt keine Meldung für diesen Fehler.
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Ungültiges Attribut für die Verbindungszeichenfolge. CREATE_DB
Allgemeine Warnung Registrierungsschlüssel ‚Temporary (volatile) Jet DSN for process 0x2c44 Thread 0x37f4 DBC 0x5b84d54 Excel‘ kann nicht geöffnet werden.
Ungültiges Attribut für die Verbin

Aktuell kann ich die Ursache nur in den folgenden Windows Updates sehen:

Windows 7 KB4041681
Windows 8.1 KB40416393
Windows 10 KB4040724
KB4041676

Einen entsprechenden Anfrage an andere Entwickler habe ich über Stackoverflow gestellt.

Als exMVP oder rMVP hat man jedoch nicht mehr die schönen Kanäle von früher… <seufz/> 🙁

Ich frage mich, wie solche Fehler durch eine Qualitätskontrolle schlüpfen können…?


Copyright © 2017 Martin Richter
Dieser Feed ist nur für den persönlichen, nicht gewerblichen Gebrauch bestimmt. Eine Verwendung dieses Feeds bzw. der hier veröffentlichten Beiträge auf anderen Webseiten bedarf der ausdrücklichen Genehmigung des Autors.
(Digital Fingerprint: bdafe67664ea5aacaab71f8c0a581adf)

AIT: Neu in TFS 2018: Secure Files Library

In unserer Blog-Serie Neu in TFS 2018 wurden bereits Änderungen und auch Neuerungen präsentiert. In die Kategorie neuer Features reiht sich die Secure Files Library ein.

Secure Files Library

Bisher ließen sich im Build-Prozess ausschließlich die hinterlegten Variablen wie z.B. Passwörter verschlüsselt ablegen und waren ausschließlich im Build- und Release-Prozess verfügbar. Mit der Einführung vom TFS 2018 gibt es nun die Möglichkeit, dies auch um verschlüsselte Dateien zu erweitern. Das bietet den Vorteil, dass u.a. Signierungszertifikate und Provisierungsprofile nicht mehr in der Quellcodeverwaltung vorgehalten werden müssen.

Secure Files Library

 

Build Tasks mit Secure Files

Die hinterlegten Dateien lassen sich nun direkt in den Build-Prozess integrieren. Hierzu gibt es die zwei neuen Build Tasks: „Install Apple Certificate“ und „Install Apple Provisioning Profile“. Für die Integration in eigene Custom Build Tasks gibt es hierfür nun den neuen Input Typ secureFile.

Build Task mit Secure Files

 

Hinweis

Zu beachten gilt, dass der hinterlegte Build Agent mindestens in der Version 2.116.0 laufen muss. Zudem ist der Upload von Dateien auf 10 MB je Datei begrenzt.

André Krämer: Speicherplatz auf dem Mac optimieren für Xamarin Entwickler

Wer eine Weile auf dem Mac mit Xamarin gearbeitet hat, wird irgendwann feststellen, dass der Festplattenplatz knapp wird. Dann heißt es natürlich Aufräumen. Die Frage ist nur: Was kann weg?

Bei mir war es gestern mal wieder so weit. Um herauszufinden, welche Dateien und Verzeichnisse besonders viel Platz benötigen, habe ich Daisy Disk genutzt. Das kleine Tool, das aktuell ca. 11 € kostet, zeigt übersichtlich an, welche Ordner und Dateien besonders speicherhungrig sind.

Screenshot von Daisy Disk

Daisy Disk hilft beim Aufräumen der Fesplatte

In meinem Fall konnte ich die Übeltäter Dank Daisy Disk schnell ausfindig machen:

  • Im Ordner ~/Library/Developer/CoreSimulator/Devices/ befanden sich ca. 11 GB
  • Unter ~/Library/Developer/Xcode/iOS DeviceSupport steckten ca. 20 GB

Beide Ordner gehören zu Xcode, welches als Voraussetzung für die Xamarin Entwicklung installiert sein muss. Bei mehr als 30 GB verbrauchtem Platz stellte sich natürlich schnell die Frage, ob die Inhalte der beiden Ordner tatsächlcih benötigt werden, oder ob ich sie löschen kann.

Eine kurze Internetrecherche brachte mich zu zwei Stackoverflow Posts:

~/Library/Developer/Xcode/iOS DeviceSupport

Im ersten der beiden Einträge wurde beschrieben, dass der Inhalt des Ordners ~/Library/Developer/Xcode/iOS DeviceSupport gefahrlos gelöscht werden kann. Der Ordner beinhaltet CrashLog Symbole für verschiedene angeschlossene iOS Geräte und Simulatoren. In meinem Fall waren viele der Inhalte bereits mehrere Jahre alt und stammten wohl von Simulatoren alter iOS Versionen, die ich schon lange nicht mehr im Einsatz habe.

Viel passieren kann beim Löschen nicht, da die Symboldateien beim nächsten Anschließen eines Geräts bzw. starten eines Simulators erneut vom Gerät auf den Mac heruntergeladen werden. Wichtig ist lediglich, dass man den Inhalt des Ordners ~/Library/Developer/Xcode/iOS DeviceSupport löscht und nicht den Ordner selbst.

~/Library/Developer/CoreSimulator/Devices/

Der zweite Stackoverflow Post verriet mir, dass im Ordner ~/Library/Developer/CoreSimulator/Devices/ die Daten der verschiedenen iOS Simulatoren liegen.

Da ich schon einige Xcode Updates hinter mir habe, befinden sich in diesem Ordner natürlich auch Daten von Simulatoren zu alten iOS Versionen, die ich schon lange nicht mehr nutze. Leider bestehen die Verzeichnisnamen der Unterverzeichnisse aus GUIDs und sind demnach wenig aussagekräftig. Wer nun also wissen möchte, welche GUID zu welchem Gerät gehört, der könnte im Terminal über

xcrun simctl list devices

eine Liste generieren, die vor jede GUID einen Klartextnamen schreibt:

== Devices ==
-- iOS 11.0 --
    iPhone 5s (4C215788-A8B3-4F41-B368-57908753E144) (Shutdown)
    iPhone 6 (824C8018-0F7E-418E-86AD-1DC84C7F2F93) (Shutdown)
    iPhone 6 Plus (BC4D7E42-24D4-4C8B-9C06-20A30B1C1E04) (Shutdown)
    iPhone 6s (8E4666F1-FF1B-401E-B4B7-F7A452CCCD1D) (Shutdown)
...

Aus dieser Liste könnte man sich dann die Geräte heraussuchen, die man nicht mehr im Einsatz hat und diese wie folgt löschen:

xcrun simctl delete <GUID>

Diese Vorgehensweise wäre natürlich recht aufwändig. Einfacher ist es Xcode selbst herausfinden zu lassen, welche Geräte nicht mehr verfügbar sind und die entsprechenden Daten zu löschen. Dies funktioniert recht komfortabel über folgenden Befehl:

xcrun simctl delete unavailable

~/Library/Caches/VisualStudio/7.0/TempDownload

Ein weiterer Ordner, der mit 2 GB relativ viel Platz auf meiner Festplatte belegte war der Ordner ~/Library/Caches/VisualStudio/7.0/TempDownload. Laut James Montemagnos Blog Post: Cleaning Up Space on Your Xamarin Development Machine kann auch der Inhalt dieses Ordners gefahrlos gelöscht werden.

~/Library/Caches/XamarinInstaller/Universal/downloads

Mehr also doppelt so viel Platz wie der Ordner ~/Library/Caches/VisualStudio/7.0/TempDownload benötigte mit 4,6 GB der Ordner ~/Library/Caches/XamarinInstaller/Universal/downloads auf meiner Festplatte. Laut den Kommentaren in James Blog Post handelt es sich auch hierbei um einen Ordner, der lediglich heruntergeladene Installationspakete cached. Der Ordnername sowie die Inhalte lassen darauf schließen, dass dem auch tatsächlich so ist. Da mein Visual Studio for Mac auch nach dem Löschen noch problemlos startete, gehe ich davon aus, dass auch dieser Ordner gefahrlos gelöscht werden kann.

bin, obj, packages oder node_modules Ordner

Ein weiterer Speicherfresser können bin, obj, packages oder node_modules Ordner sein, die unterhalb der eigenen Programmierprojekte angelegt werden. Diese lösche ich regelmäßig auf dem Mac mit folgendem Befehl im Terminal (innerhalb des Visual Studio Projektordners), den ich bei Stackoverflow gefunden habe.

find . -iname "bin" -o -iname "obj" -o -iname "packages" -o -iname "node_modules" | xargs rm -rf

Aus dem gleichen Stackoverflow Post habe ich auch folgenden Befehl, den ich mir unter Windows in einer .bat Datei abgelegt habe

for /d /r . %%d in (bin,obj,packages,node_modules) do @if exist "%%d" rd /s/q "%%d"

Die Batch Datei führe ich einfach unter Windows im Projektordner aus und lösche somit rekursiv alle bin, obj, packes und node_modules Ordner.

Fazit

Wer mit Xamarin und somit auch Xcode arbeitet hat schnell mehr Platz auf seiner Festplatte belegt als einem lieb ist. Glücklicherweise lässt sich mit Werkzeugen wie Daisy Disk schnell herausfinden, was die Speicherfresser im System sind. Sind es - wie in meinem Fall - gecachte oder veraltete Daten von Xcode oder Xamarin, dann lässt sich das Speicherproblem schnell und einfach mit ein paar Löschvorgängen beheben. Selbstverständlich sollte man beim Löschen vorsichtig vorgehen. Wer im Ordner ~/Library/ die falschen Ordner oder Dateien löscht zerschießt sich im schlimmsten Fall sein komplettes System.

Golo Roden: DDD & Co., Teil 10: Hallo wolkenkit

Seit der vergangenen Folge ist der fachliche Code für TodoMVC vollständig. Allerdings fehlt der gesamte technische Rahmen, der die Funktionalität über eine REST- und WebSocket-API anbietet, die fachlichen Ereignisse persistiert und die Anwendung bei Bedarf skaliert. Das Open-Source-Framework wolkenkit schafft Abhilfe.

AIT: Neu in TFS 2018: Task Groups für Build und Release

Mit TFS 2015 wurde ein komplett neues Build- und Releasemanagement-System eingeführt. Über einen Teil der enthaltenen Neuerungen, wie z.B. das Importieren und Exportieren von Builddefinitionen konnten Sie bereits in vorhergehenden Beiträgen unserer Blogserie lesen. In diesem Blogpost möchten wir Ihnen nun einen weiteren großen Schritt vorstellen, den Microsoft im Ausbau des neuen Systems geht: Task Groups.

Jede Build- und jede Releasedefinition besteht aus einer Menge an Tasks, welche nacheinander ausgeführt werden. Jeder dieser Tasks definiert dabei eine auszuführende Aktion, wie z.B. das Bauen einer Anwendung, das Ausführen von Tests, u.v.m. In vielen Fällen wird die gleiche Abfolge an Tasks in mehreren Build- oder Releasedefinitionen verwendet um z.B. firmenspezifische Abläufe abzubilden, und unterscheidet sich dabei nur durch die Parameterwerte, welche als Input an den einzelnen Tasks spezifiziert werden. In diesem Fall bieten Task Groups eine einfache Möglichkeit, die Definition und Eigenschaften von solchen Abfolgen zentral zu verwalten. Task Groups fassen also eine Reihe von Tasks zusammen und stellen diese dann als einen einzigen, wiederverwendbaren Task den Build- und Releasedefinitionen zur Verfügung. Dies birgt folgende Vorteile:

  • Zentralisierung von wiederkehrenden Abläufen
  • Verringerung von Redundanzen
  • Änderungen an Task Groups werden auf alle Definitionen angewendet, welche die Task Group referenzieren

Um eine neue Task Group anzulegen, kann innerhalb einer Build- oder Releasedefinition eine Abfolge an Tasks ausgewählt werden und anschließend im Kontextmenü der Eintrag Create task group gewählt werden (vgl. Abb. 1).

SNAGHTML11d7c79f

Abbildung 1: Erstellen einer Task Group

Beim Anlegen sind folgende Punkte zu beachten:

  • Task Groups sind Team Projekt-spezifisch.
  • Für die zu erstellende Task Group muss ein Name spezifiziert werden (vgl. Abb. 2 (1)) sowie die Gruppe des Task Katalogs, in welchem die Task Group enthalten sein soll (vgl. Abb. 2 (3)). Des Weiteren kann eine Beschreibung angegeben werden. (vgl. Abb. 2 (2)).
  • Alle Parameterwerte, welche innerhalb der einzelnen Tasks verwendet werden und durch Variablen spezifiziert sind, werden automatisch auch als Variablen an der Task Group abgebildet. Der zugehörige Variablenwert wird als Default-Wert gesetzt, kann jedoch im Nachhinein noch geändert werden (vgl. Abb. 2 (4)).
  • Alle Parameterwerte, welche innerhalb der einzelnen Tasks verwendet werden und durch fest Werte (d.h. keine Variablen spezifiziert sind) werden auch als feste Werte für die Task Group übernommen. Diese fixen Werte können beim Referenzieren der Task Group nicht von außen angepasst werden.
  • Aus den beiden vorhergehenden Punkten folgt: Alle Parameterwerte, welche beim Referenzieren der Task Group veränderbar sein sollen, müssen als Variable in den enthaltenen Tasks definiert sein.

image

Abbildung 2: Eigenschaften beim Erstellen einer Task Group

Nach der Erstellung können Task Groups zentral im Task Groups-Tab des Build & Release-Hubs verwaltet und editiert werden. Jede Task Group verfügt darin vier Tabs:

  • In den Properties können die allgemeinen Eigenschaften Name, Description und Category angepasst werden. Darüber hinaus sind alle Task Group Variablen mitsamt ihrer Defaultwerte und Beschreibungen enthalten.
  • Der Tab Tasks enthält die Abfolge der Tasks. Hier können fixe Parameterwerte zu Variablen geändert werden, neue Tasks hinzugefügt werden, …
  • Im Tab History können alle Änderungen, welche an der Task Group vorgenommen wurden, nachverfolgt und untereinander verglichen werden (vgl. Abb. 3).
  • Der Tab References enthält die Liste alle Build- und Releasedefinitionen sowie alle Eltern-Task Groups, welche die Task Group referenzieren.

image

Abbildung 3: Änderungsnachverfolgung in Task Groups

Der History-Tab einer Task Group bietet schon einige Sicherheit bei Änderungen. Da sich Änderungen an einer Task Group jedoch sofort in allen Build- und Releasedefinitionen durchschlagen ist das Risiko von unerwünschten Effekten immer noch relativ hoch. Dies kann durch die Verwendung von Versionierung weiter reduziert werden: Änderungen an Task Groups können zunächst als Draft abgespeichert werden (vgl. Abb.4 (1)). Damit kann die letzte stabile Version zunächst weiter in den bestehenden Prozessen verwendet werden und die Änderungen nur in ausgewählten Definitionen getestet werden. Nach erfolgreichem Testen der Änderungen können diese dann in die ursprüngliche Task Group gepublished werden (vgl. Abb. 4 (2)). Änderungen, welche nicht abwärtskompatibel sind, können dabei als Preview gepublished werden. Dies erzeugt eine neue Version der Task Group. Damit können verschiedene Versionen einer Task Group zur selben Zeit im Einsatz sein. Die Preview-Version kann dann zu einem späteren Zeitpunkt wiederum als offizielle neue Version gepublished werden.

image

Abbildung 4: Versionierung von Task Groups via Drafts

Abgerundet wird das Konstrukt durch die Möglichkeit Task Groups zu exportieren (vgl. Abb. 5 (1)) und Importieren (vgl. Abb. 5 (2)). Oftmals werden Änderungen an Prozessen zunächst in einer separaten Testinstanz durchgeführt und getestet. Es existieren auch Build- und Releaseabläufe, welche über den Team Projekt-Kontext hinweg gleich sind. Durch den Export und Import von Task Groups entfällt in solchen Szenarien die Notwendigkeit dieselben Änderungen an verschiedenen Stellen machen zu müssen und eliminiert damit auch das Risiko von Fehlern beim manuellen Übertragen von Änderungen.

image

Abbildung 5: Export und Import von Task Groups

Fazit

Das Konzept von Task Groups bietet eine schöne und durchdachte Möglichkeit wiederkehrende Abfolgen in Build- und Releasedefinitionen zentral zu definieren und zu verwalten. Das mit TFS 2015 neu eingeführte System macht damit einen weiteren großen Schritt auf dem Weg zum Erwachsenwerden.

Weitere Schritte auf diesem Weg stellen wir Ihnen auch in nachfolgenden Beiträgen unserer Blogserie vor. Bleiben Sie also dran und freuen Sie sich auf spannende Neuerungen mit TFS 2018.

Golo Roden: Einführung in React, Folge 2: Ein tieferer Einblick

React basiert auf vielen Konzepten der funktionalen Programmierung. Besonders wichtig ist die Unveränderlichkeit von Datentypen, die in React die Grundlage für das effiziente Rendern der Anzeige bildet. Wie funktioniert das für eigenen Code, und worauf gilt es in JSX außerdem zu achten?

AIT: Berechtigungen für Fortgeschrittene – Berechtigungen in der Quellcode- verwaltung für Admins einschränken

Bei Entwicklungsteams, welche im stark regulierten Umfeld tätig sind, entstehen an das Berechtigungskonzept für die Quellcodeverwaltung aus diversen Gründen spezielle Anforderungen. Eine Anforderung aus diesen Umfeld lautet beispielsweise:

Ist es möglich einem Teil der TFS-Administratoren des Leserecht auf den Quellcode in einem besonders zu schützenden Bereich der Quellcodeverwaltung zu entziehen?

Die Kurzfassung der Antwort lautet: Ja, es ist möglich. Kommt noch der Faktor Revisionssicherheit der Lösung hinzu, dann müssen noch zusätzliche Schritte eingeplant werden.

Zu Beginn steht die Frage im Raum: Git oder Team Foundation Version Control (TFVC)? Für das beschriebene Szenario ist Git weniger gut geeignet, da hier nur eine Berechtigungsvergabe auf das komplette Repository möglich ist. TFVC hingegen unterstützt diverse Berechtigungsarten und eine feingranulare Steuerung bis auf Dateiebene herab.

Im Folgenden wird die Umsetzung der eingangs genannten Anforderung Schritt für Schritt auf Basis von TFVC beschrieben.

Um die Berechtigungen für den zu schützenden Quellcode-Ordner einzugrenzen, wird im ersten Schritt die einzugrenzende TFS‑Berechtigungsgruppe (z.B. TFS_Admins_Deny) hinzugefügt (siehe Screenshot 1).

Als nächster Schritt wird dieser Gruppe dann das Leserecht explizit verweigert (siehe Screenshot 2)

Mit einem „Verweigern“(Deny) werden den Mitgliedern einer Gruppe die Rechte explizit entzogen, auch wenn sie an anderer Stelle (z.B. durch eine andere Gruppe) gegeben wurden. Es gilt der Grundsatz: Verweigern ist stärker als zulassen.

Ein Sonderfall sind die Gruppen „Team Foundation Administrators“ und „Project Collection Administrators“. Diese Gruppen haben grundsätzlich automatisch überall in der Quellcodeverwaltung das Leserecht.

Für TFS Admin-Gruppen gilt das explizite Verweigern in den meisten Bereichen des TFS nicht. Es wird damit verhindert, dass sich Administratoren selbst aussperren können. Von dieser Regelung gibt es aber Ausnahmen. Eine dieser Ausnahmen betrifft die Quellcodeverwaltung. Es ist sozusagen die Ausnahme von der Ausnahme.

In version control permissions, explicit deny takes precedence over administrator group permissions.” Quelle: https://www.visualstudio.com/de-de/docs/setup-admin/permissions#tfvc-permissions-object-level

Die Folge ist deshalb, dass ein Administrator als Mitglieder der TFS Gruppe TFS_Admins_Deny kein Read-Recht auf den schützenswerten Quellcode-Ordner hat.

Im Fall von z.B. Anwendersupport, kann ihm aber das Leserecht auf den Quellcode, kurzfristig wieder gewährt werden. Eine Möglichkeit wäre, den Administrator temporär aus der Gruppe TFS_Admins_Deny zu entfernen.

Damit ist die Hälfte der Anforderung umgesetzt. TFS Administratoren können tatsächlich aus der Quellcodeverwaltung ausgeschlossen werden. Das ist aber noch nicht revisionssicher, da ein eingeschränkter Administrator jederzeit wieder der TFS-Gruppe das Read-Recht geben oder die Gruppenmitgliedschaft ändern kann.

Es gibt zwei Möglichkeiten die fehlende Anforderung umzusetzen. Die zwei Möglichkeiten sind in nachfolgender Tabelle dargestellt:

Legende:

  1. Bei der ersten Lösung werden alle Administratoren der TFS-Gruppe TFS_Admins_Deny hinzugefügt. Dieser Gruppe muss das Read-Recht auf den zu schützenden Quellcode verweigert werden.
  2. Bei der zweiten Lösung wird auch die Gruppe TFS_Admins_Deny erstellt, aber nur die Limited-Administrators hinzugefügt. Dieser Gruppe muss auch das Recht „Manage permissions“ verweigert werden. Es wird noch eine weitere Administratoren Gruppe benötigt, die TFS_Limited_Admins. Dieser Gruppe wird der entsprechende Personenkreis hinzugefügt und alle Rechte wie der originalen Gruppe Team Foundation Administrators gegeben, bis auf die Rechte „Edit collection-level information“ und „Edit project-level information“. Im Anschluss werden alle Benutzer der TFS_Limited_Admins aus der Gruppe Team Foundation Administrators entfernt. Mit dieser Lösung ist sichergestellt, dass beide Personenkreise die benötigten Berechtigungen für die tägliche Arbeit besitzen und mit minimalem Aufwand auch für den geschützten Quellcodebereich temporär berechtigt werden können.

Für die erste Lösung ist es unabdingbar, dass Berechtigungsänderungen und Änderungen der Gruppenmitgliedschaften protokolliert werden. Nur dann kann nachvollzogen werden, wer wann wem welches Recht eingeräumt hat. Dies lässt sich nicht mit Board-Mitteln bewerkstelligen. Für diese Anforderung ist es notwendig eine serverseitige TFS-Erweiterung zu entwickeln. Die Sicherheits-Events müssen am serverseitigen Eventhandler (TFS Requestfilter) über die TFS API revisionssicher in einer extra Datenbank protokolliert werden.
Bei der zweiten Lösung ist eine Protokollierung der Gruppenmitgliedschaften nicht zwingend erforderlich. „Echte“ Administratoren besitzen hier sowieso nur noch als einzige das Recht Berechtigungen ändern zu dürfen und verfügen aufgrund des TFS Designs sowieso über einen Vollzugriff auf alle TFS Daten.

Das besprochene Szenario kann man noch erweitern. Am Ende dieses Blogposts soll dies aber lediglich als Anregung dienen: Unabhängig ob die Lösung 1) oder 2) gewählt wurde bleibt noch die Frage offen:

Auf welche Teile des Quellcodes haben eingeschränkte Administratoren während ihres Anwendersupportfalls zugegriffen?

An diesen Stellen bietet sich auch wieder der Weg über die TFS API in Kombination mit serverseitigen Eventhandler (TFS Requestfilter) an, damit die entsprechenden Zugriffe protokolliert werden können. Dies ist hier schon fast zwingend notwendig, damit das ganze Szenario rund wird und die Protokollierung lückenlos und durchgängig ist.

Fazit

Es ist mit ein paar Handgriffen möglich Administratoren aus definierten Bereichen der Quellcodeverwaltung (TFVC) auszuschließen. Die notwendigen Zugriffe lassen sich sowohl bei der Rechtevergabe, als auch beim Zugriff der Quellcodeverwaltung protokollieren. Der Aufwand darf nicht unterschätzt werden, da für einige Teile der Lösung eigene Erweiterungen (serverseitiger TFSRequestfilter) geschrieben werden müssen.
Unabhängig von dem aktuellen Blog-Post sollte bei dieser extremen Art von Anpassungen immer kritisch hinterfragt werden, ob der Aufwand in einem angemessenen Verhältnis zum Schutzbedürfnis steht. Anpassungen können schnell zu neuen Problemen, erhöhten Aufwänden für andere Anpassungen (Bsp. Builds) und unnötigen Fehleranalysen führen.
Für ein umfassendes Sicherheitskonzept ist die Problemstellung aus diesen Blog-Post nur ein kleiner Baustein einer Sicherheitsarchitektur. Weitere Themenfelder für eine solche Architektur und ungewollte Zugriffe auf TFS Informationen sind exemplarisch: direkter Remotezugriff auf den Server via Remote Desktop, bekannte Servicekonten und -Passwörter oder direkter Datenbankzugriff.

Der Nutzen von einem Sicherheitstor bleibt schnell auf der Strecke, wenn links und rechts davon nur ein gewöhnlicher Gartenzaun um das Haus gebaut wurde.

Uli Armbruster: Workshop: Conquer your Codebase – Bewährter Clean Code aus der Praxis

Am Developer Open Space 2017 halt ich einen 1-tägigen Workshop zu obigem Thema. In reduzierter, kompakter Form werde ich dazu bewährte Inhalte aus meinem 3-tägigen Seminar nehmen und die Ursachen für folgende Probleme adressieren:

  • Unverständlicher bzw. schlecht wartbarer Code
  • Bugs
  • Skalierungsprobleme
  • Go Live Probleme
  • Verpasste Deadlines und lange Entwicklungszeiten

 

Wir werden uns anschauen wie es dazu kommen kann, z.B. weil

  • die Infrastruktur nicht wiederverwendbar ist,
  • die Domänenlogik nicht erweiterbar ist,
  • eine falsche Nutzung der API möglich ist,
  • der Code nicht ausdrucksstark ist,
  • oder starke Abhängigkeiten bestehen.

 

Wir werden das Open Closed Principle genauer besprechen und Seperation of Concerns am konkreten Beispiel umsetzen. Speziell für Freunde der Objektorientierung werde ich je nach verfügbare Zeit praktische Lösungen zur Vermeidung von If-Else-Zweigen und NULL-Checks zeigen.

 

Ein Laptop mit Visual Studio oder Visual Studio Code und .NET 4.6 wären wünschenswert. Prinzipiell ist der Workshop aber für alle Entwickler des objektorientierten Paradigmas geeignet, da bis auf Delegaten (Action/ Func) und Erweiterungsmethoden kaum Sprachspezifika verwendet werden. Darüber hinaus können Teilnehmer auch ohne Hardware beiwohnen, weil wir beim Live Coding am Präsentations-PC mit Code Monkey Runden arbeiten werden.

Gerne dürfen die Teilnehmer mir vorab Fragen und Probleme z.B. hier in Form von Kommentaren oder per E-Mail zukommen lassen.


Einsortiert unter:Architecture, Development, Events, German Tagged: Clean Code, Open Space, Workshop

Uli Armbruster: Git History Commit Redos für Workshops

Mit diesem Snippet, das ich in meinen Workshops verwende, könnt ihr die Commits eines Branches vom ersten bis zum letzten Interaktiv durchsteppen, um so jede Änderung mit den Teilnehmern zu besprechen. Das ist vor allem dann nützlich, wenn das Live Coding mehr ablenkt oder die Zeit ein wenig knapp ist:

 

Beim ersten Commit gibt es noch eine kleine Fehlermeldung, da versucht wird auf den vorherigen Commit zuzugreifen, den es beim 1 Commit logischerweise nicht geben kann.

Git History Commit Redo

Git History Commit Redo- First Commit

 

Ab dem zweiten Commit werden dann sowohl die Commit Message als auch alle alle geänderten Dateien angezeigt.

Git History Commit Redo

Git History Commit Redo – Next Commit

 

Am Schluss kommt noch diese Meldung.

Git History Commit Redo

Git History Tracking – Finished

 

Beachtet bitte, dass dies nur bei einer linearen Historie funktioniert, da es sich um einen Rebase handelt und dass ihr hierfür am besten einen Test-Branch abzweigt, um nicht ggf. Änderungen noch einzubauen, die dann im Master-Branch landen.


Einsortiert unter:Development, German Tagged: Git

Golo Roden: Ein nie vollendetes Spiel

Am vergangenen Wochenende begann auf der Webseite nevercompletedgame.com ein Spiel, das die Internet-Community über zahlreiche Stunden auf Trab hielt. Was hat es mit dem Spiel auf sich, und wie lässt es sich lösen?

Robert Meyer: Git: Add .gitignore after commit

I had created a new repository in GitHub and forgot to add the .gitignore file to ignore all my Visual Studio and debugging files. However, I did not realize it until after I created the first commit.

These are the steps to apply the gitignore file later:

  1. Create a .gitignore file in the root of your local repository.
  2. Run this command. This removes everything from the index:
    git rm -r --cached .
  3. Re-add all files. Now the files and folders from gitignore are ignored.
    git add .
  4. Then commit the changes again and push it to your GitHub repository
    git commit -m ".gitignore is now working" 
    git push

AIT: Neu in TFS 2018: Kleine aber feine Neuerungen im Test Management

In der Major Version 2017 des TFS waren eine Vielzahl neuer Features und wesentliche Prozess-Änderungen für den Bereich Testing enthalten. Im Gegensatz dazu konzentriert sich TFS 2018 eher auf kleinere Optimierungen und Neuerungen für bestehende Features. Diese möchten wir Ihnen nicht vorenthalten und laden Sie daher ein, in diesem Beitrag unserer Blogserie „Neu in TFS 2018“, diese kleinen aber feinen Neuerungen näher kennenzulernen. Zudem enthält TFS 2018 eine doch etwas größere Veränderung, die wir Ihnen gleich zu Beginn erläutern wollen.

 

Umbruch für Lab Management und Ausführung von automatisierten Tests

Zusätzlich zu den sonst eher kleineren Neuerungen für den Bereich Testing gibt es doch eine wesentliche Änderung mit TFS 2018. Diese stellt einen Breaking Change für die Verwaltung von Laborumgebungen und die Ausführung von automatisierten Tests dar.

Mit dem im Microsoft Test Manager (MTM) enthaltenen Lab Management können Testumgebungen aufgesetzt und verwaltet werden. Damit ist es möglich auch komplexe Szenarien, wie z.B. eine mehrstufige Anwendung mit verschiedenen Rollen (Desktopclient, Webserver, Datenbankserver, …) oder auch die Integration von SCVMM, abzubilden.

Mit bisherigen TFS-Versionen gab es weiterhin die Möglichkeit, Test Controller am TFS zu registrieren und mit deren Hilfe automatisierte Tests in den Testumgebungen des Lab Management auszuführen. Dies konnte manuell aus MTM oder auch aus einem XAML-Build heraus initiiert werden. Diese Integration von TFS und Lab Management bzw. automatisiertem Testen im MTM wird in TFS 2018 nicht länger unterstützt. Es können also keine neuen Test Controller am TFS angemeldet werden und auch bestehende Test Controller können zusammen mit TFS 2018 nicht mehr verwendet werden. Diese Änderung ist sehr eng verknüpft mit der Abkündigung des XAML-Buildsystems. In Konsequenz entfällt damit auch die Ausführung von automatisierten Tests über den vorher beschriebenen Weg.

Lassen Sie sich von diesem Umstand aber bitte nicht davon abschrecken auf TFS 2018 zu migrieren. Zwar entfällt der gewohnte Weg, jedoch bietet TFS 2018 alternative Wege, die bekannten Ziele weiterhin zu erreichen. So kann beispielsweise die Integration von TFS und SCVMM mit der SCVMM Integration-Extension erreicht werden und auch die Ausführung von automatisierten Tests während eines Builds ist natürlich weiterhin gegeben.

 

Die kleinen Helfer für Test Management und Ausführung

Wie Sie bereits im Beitrag Neu in TFS 2018: Neues für Work Item Tracking lesen konnten, erhält die Testfallverwaltung gemeinsam mit den Ansichten des Work Item Trackings (Backlogs etc.) einen neuen Anstrich in Bezug auf die Filterung. Somit ist es nun nicht nur wie bisher möglich nach Testfeldern wie z.B. dem Ergebnis zu filtern, sondern darüber hinaus auch nach allgemeinen Work Item Feldern wie beispielsweise dem Titel und dem Status (vgl. Abb. 1). Damit können Test Cases nun noch einfacher und schneller gefunden werden.

image

Abbildung 1: Filterung von Test Cases

Auch die weiteren Neuerungen stehen ganz im Zeichen der erhöhten Übersichtlichkeit und Nachverfolgbarkeit. So ist es nun möglich im Nachgang an die Testausführung Informationen in Kommentaren von Test Runs und Test Results mit Hilfe von Markdown besser aufzubereiten. Einstiegspunkt hierfür ist zum einen die Option Update comment für ganze Test Runs und zum anderen die Option Update analysis für einzelne Test Results (vgl Abb. 2).

image

Abbildung 2: Verwendung von Markdown im Kommentar eines Test Results

Darüber hinaus war es bisher nur im MTM möglich Dateien, wie z.B. Screenshots, als Anhänge zu Test Runs und Test Results hochzuladen. Diese Option steht nun auch im Web zur Verfügung (vgl. Abb. 3). Damit beseitigt Microsoft eine weitere Hürde im Bereich Testing für den vollständigen Umstieg vom MTM auf den TFS Web Access.

Abbildung 3: Anhängen einer Datei an einen Test Run

Eine weitere Hilfe ist die neue Möglichkeit fehlgeschlagene Tests mit einem bereits existierenden Bug zu verknüpfen. Somit kann nun auch die Verbindung von fehlgeschlagenen Tests zu bereits bekanntem und dokumentiertem Fehlverhalten hergestellt werden (vgl. Abb. 4).

Abbildung 4: Verlinkung von fehlgeschlagenen Tests mit existierenden Bug

 

Verbessertes Test Reporting durch Widgets

TFS 2018 setzt einen besonderen Fokus auf den Ausbau des Build- und Releasemanagements. Dieser Ausbau spiegelt sich auch im Testbereich wieder. Das in TFS 2017 eingeführte Test Results Trend-Widget, welches bisher nur den Verlauf der Testergebnisse ausgeführter Builds einer bestimmten Builddefinition visualisieren konnte, wird mit TFS 2018 um die Möglichkeit erweitert, auch Testergebnisse innerhalb von Releases darzustellen. Damit kann der jeweils aktuelle Teststatus von Umgebungen einer bestimmten Releasedefinition auf einen Blick angezeigt werden (vgl. Abb. 4).

image

Abbildung 4: Test Results Trend-Widget für Releasedefinition und –umgebung

Hinzu kommt außerdem das neue Widget Chart for Test Plans. Dieses ermöglicht, ähnlich wie beim Erstellen von Test Case oder Test Result Charts im Test-Hub, die Abbildung des aktuellen Testdesign- oder des Testausführungsstatus von Test Plänen und Suiten (vgl. Abb. 5). Im Vergleich zu den bekannten Charts, birgt das neue Widget den Vorteil, dass größere Datenmengen besser dargestellt werden können.

Abbildung 5: Chart for Test Plans-Widget

 

Fazit

TFS 2018 bringt keine weltbewegenden Neuerungen für das Testing mit sich. Nichtsdestotrotz bringen die kleinen aber feinen Erweiterungen für bereits existierende Features Vereinfachungen für den Projektalltag mit sich. Den Ausreißer bildet jedoch die Abkündigung der Integration von TFS und Lab Management Umgebungen im MTM. Dies stellt zwar ein Breaking Change dar, gibt damit aber auch den Anstoß, alte Pfade zu verlassen und die Vorzüge des neuen Build- und Releasemanagement-Systems auch für den Bereich Testing zu nutzen.

Golo Roden: Einführung in React, Folge 1.1: Release von React 16

Vor einigen Tagen hat Facebook eine neue Version von React veröffentlicht, die unter einer anderen Lizenz steht als die bisherigen Versionen. Außerdem wurde die zugrunde liegende Architektur vollständig überarbeitet. Was ist beim Umstieg zu beachten?

Dmitrij Doberstein: Convert all words first letter to upper case (RegEx & Notepad++)

From time to time we need to replace the first letter of many words to the upper case.

String: "word1" "word2" "word3 and/or word4"

Find part: "\b(\w)(\w+)"

Replace part: "\u$1\E$2"

Result: "Word1" "Word2" "Word3 And/Or Word4"


If we want to exclude any word, e.g and, or, so we can make this so:

String: "word1" "word2" "word3 and/or word4"

Find part: "\b(?!and|or)(\w)(\w+)"

Replace part: "\u$1\E$2"

Result: "Word1" "Word2" "Word3 and/or Word4"

Code-Inside Blog: dnSpy - a OSS IL decompiler and debugger

My colleague was fighting against a nasty bug, that only occures on one machine. Unfortunatly this machine was not a development machine (no VS installed) and we didn’t want to mess with VS remote debugging, because (AFAIK) his would need some additional setup but we were not allowed to install anything.

Soooo… he searched around and found this:

dnSpy - a .NET assembly editor, decompiler, and debugger

The title contains the major points. It is a decompiler, like IL Spy, but addionaly it has a super nice debugger and it looks like a small Visual Studio.

Some pictures how I just decompile Paint.NET and attach the debugger:

x

x

x

I think this is just awesome and it helped my colleague alot.

OSS & Free

The complete project is hosted on GitHub and is “Open Source (GPLv3) and Free Forever”

Checkout the GitHub project page - it contains a lot more information. The tool itself was just 18mb zipped and can be run everywhere.

Its a decompiler!

And just to make sure you keep this in mind: The debugging works with every .NET application (at least in theory), because it decompiles the .NET IL language to C#. It is not a 1:1 debugger, but maybe it can help you.

Check out the dnSpy GitHub Site

Robert Meyer: Self Hosted ASP.NET Core: TagHelper doesn’t work

I have implemented a simple TagHelper in a ASP.NET Core 2.0 Website, which replace the

My custom TimeTagHelper:

[HtmlTargetElement("time")]
public class TimeTagHelper : TagHelper
{
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        output.Content.SetHtmlContent($"<h1>{DateTime.Now.ToShortTimeString()}</h1>");
    }
}

If I start the website project directly, the TagHelper works without problems. Launched from the Console / Service, the

The solution: PreserveCompilationContext

These are the steps that helped me:

  1. Add the following ItemGroup definition to my Selhosted.Website.csproj
 <ItemGroup>
    <EmbeddedResource Include="wwwroot\**\*;Views\**\*;Areas\**\Views" />
  </ItemGroup>

 

  1. Add the PreserveCompilationContext property to the PropertyGroup in Selhosted.Website.csproj
 <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <AssemblyName>SelfHosted.Website</AssemblyName>
    <RootNamespace>SelfHosted.Website</RootNamespace>
    <TypeScriptToolsVersion>2.3</TypeScriptToolsVersion>
    <PreserveCompilationContext>true</PreserveCompilationContext>
  </PropertyGroup>

 

  1. Include the generated SelfHosted.Website.deps.json from the websites bin folder to the SelfHosted.Console project and set the Copy to Output Directory to Copy always.64fcb891c62c3a149c6ff350ebe0b1d3

You can find the sample project in my GitHub Repo: roeb/DotNetCoreSamples


AIT: Hilfe, meine Testergebnisse stimmen nicht – was nun?

Seit Einführung des neuen JSON-Build Systems im TFS steht auch der Build Task Run Functional Tests zur Verfügung. Dieser ermöglicht die Ausführung von Selenium – und anderen funktionalen Test-Frameworks durch einen Test Agent auf einer Menge von Remote-Maschinen (d.h. auf Rechnern ohne Build Agent). Mit diesem ist es möglich nicht nur Tests auf Basis von Test Assemblies auszuführen, sondern auch Test Suiten zu verwenden. In letzterem Fall werden alle darin enthaltenen Test Cases, welche mit einer automatisierten Test Methode verknüpft sind und somit den Automation Status = Automated aufweisen, ausgeführt und die Ergebnisse der Test Cases im TFS automatisch gesetzt. Für datengetriebene Tests jedoch kann es vorkommen, dass Test Cases fälschlicherweise als Passed markiert werden. Damit verlieren die Ergebnisse der automatisierten Testläufe innerhalb der ausgeführten Test Suiten ihre Aussagekraft.

Understand the problem

Um zu verstehen was im Detail passiert und wie mit dem oben beschriebenen Bug umgegangen werden kann, gehen wir einen Schritt zurück: Was sind eigentlich datengetriebene Tests? Bei datengetriebenen Tests handelt es sich um Tests, welche in so genannten Iterationen mit unterschiedlichen Datensätzen ausgeführt werden. Die Datensätze können dabei aus verschiedenen Quellen wie z.B. XML-Files, Datenbanken, SharePoint, etc. ausgelesen werden. Ein Test Case soll also nur dann als Passed markiert werden, wenn alle ausgeführten Iterationen erfolgreich ausgeführt wurden.

Der Run Functional Test-Task jedoch beinhaltet einen Fehler: Das Ergebnis der ersten Iteration eines Test Cases wird als Gesamtergebnis gesetzt. Dies führt im Fall, dass die erste Iteration erfolgreich ausgeführt wurde Folge-Iterationen aber nicht, zu falschen Ergebnissen.

Fix it

Da es nicht möglich ist den Build Task selbst derart anzupassen, dass die richtigen Testergebnisse im TFS widergespiegelt werden, wird ein Workaround benötigt um dieses Ziel zu erreichen. Der hier vorgestellte Ansatz basiert auf der Verwendung von PowerShell und der TFS API um die Ergebnisse eines Test Runs zu überprüfen und gegebenenfalls anzupassen. Das PowerShell-Skript kann dann im Anschluss an den Run Functional Test-Task in der jeweiligen Build oder Release Definition eingehängt werden. Was ist also im Skript zu tun?

In groben Schritten muss folgender Ablauf implementiert werden:

  1. Identifikation des zum Build / Release zugehörigen Test Runs
  2. Identifikation des TRX-Test Run Attachments, welches die Ergebnisse der einzelnen Iterationen eines Test Cases enthält
  3. Parsen des TRX-Files um die korrekten Testergebnisse zu erhalten
  4. Abgleichen des aktuellen Ergebnis  mit dem korrekten Ergebnis pro Test Case
  5. Bei Bedarf Korrektur der Ergebnisse einzelner Test Cases

Der auf diese Weise beschriebene Ablauf kann im Detail folgendermaßen umgesetzt werden:

Um den zu einem Build bzw. Release zugehörigen Testlauf zu identifizieren (1.) werden zunächst alle Tasks per REST API angefragt. Generell kann in Powershell für den Aufruf einer REST-Methode das Commandlet Invoke-RestMethod verwendet werden. Der Aufruf zum Anfragen der Tasks am Beispiel eines Releases sieht folgendermaßen aus:

GET https://{instance}/{project}/_apis/release/releases/{releaseId}/environments/{environmentId}/deployPhases/{releaseDeployPhaseId}/tasks?api-version={version}

Alle benötigten Parameter können aus den vordefinierten Release-Umgebungsvariablen ausgelesen werden. Das vollständige PowerShell-Code Snippet lautet dann wie folgt :

# Initialize variables

$instance = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI

$project = $env:SYSTEM_TEAMPROJECT

$releaseId = $env:RELEASE_RELEASEID

$environmentId = $env:RELEASE_ENVIRONMENTID

$releaseDeployPhaseId = $env:RELEASE_DEPLOYPHASEID

$tasksUrl = $instance + $project + ”/_apis/release/releases/” + $releaseId + “/environments/” + $environmentId + “/deployPhases/” + $releaseDeployPhaseId + “/tasks?api-version=3.0-preview”

$userName = “bgoeller”

$password = “superSecretPassword”

$securePassword = ConvertTo-SecureString $password –AsPlainText –Force

$credentials = New-Object System.Management.Automation.PSCredential ($username, $securePassword)

# Get Release Tasks

$tasks = Invoke-RestMethod Uri $tasksUrl –Credential $credentials –Method Get

Hinweis: Bitte beachten Sie, dass Passwörter nicht im Skript enthalten sein sondern per Skript-Parameter übergeben werden sollten. In vorhergehendem Code Snippet ist das Passwort zu Demozwecken enthalten.

Aus dem Ergebnis dieser Abfrage kann dann die Id des Test Tasks herausgesucht werden, indem auf den Task Name RunVisualStudioTestsusingTestAgent gefiltert wird. Im Anschluss kann mithilfe der Test Task Id und der REST API-Methode

GET https://{instance}/{project}/_apis/release/releases/{releaseId}/environments/{environmentId}/deployPhases/{releaseDeployPhaseId}/tasks/{taskId}/logs?api-version={version}

das Log-File des Run Functional Test-Tasks angefragt werden. Aus diesem kann dann mittels Regex die Test Run Id identifiziert werden.

Analog zu Schritt 1 können dann mittels REST API alle dem Test Run zugehörigen Attachments abgerufen, das TRX-File identifiziert und heruntergeladen werden (2.).

Die letzten drei Schritte (3., 4., und 5.) lassen sich folgendermaßen sinnvoll in einem Ablauf unterbringen:

Zunächst werden die aktuellen Ergebnisse für die im Test Run enthaltenen Test Cases per REST API abgerufen. Diese enthalten zu jedem Test Case sowohl die ID des zugehörigen Test Results, das Ergebnis selbst sowie weitere Metainformationen. Im Folgeschritt findet nun ein Technologiewechsel statt: Bisher wurden alle Anfragen an den TFS per REST API ausgeführt. Diese ist jedoch noch nicht vollständig ausgebaut. Der Zugriff auf Teile der tiefergehenden Informationen sowie schreibende Operationen auf dem TFS können bisher nicht per REST API ausgeführt werden. Darum werden alle weiteren Schritte mittels TFS Client API ausgeführt. Dazu muss zunächst eine Verbindung zum TFS und insbesondere zum TFS Test Management Service aufgebaut werden:

$tfs = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($instance)

$testManagementService = $tfs.GetService([Microsoft.TeamFoundation.TestManagementClient.ITestManagementService])

Hierbei ist zu beachten, dass die für die TFS Client API-Aufrufe benötigten Assemblies im Vorhinein geladen werden müssen um im Skript zur Verfügung zu stehen. Hat man diese Hürde genommen kann dann die Verbindung zwischen den Test Cases und den Methodennamen der automatisierten Tests, welche im TRX-File aufgeführt sind, hergestellt werden. Dazu kann pro Test Case das detaillierte Test Result abgerufen werden:

$testProject = $testManagementService.GetTeamProject($project)

$detailedTestResult = $testProject.TestResults.Find($testRunId, $TestCaseResult.Id)

An diesem Test Result findet man dann alle benötigten Informationen um einen Test Case auf die entsprechende Methode im TRX-File zu mappen. Somit kann das TRX-File geparsed werden, die Ergebnisse abgeglichen werden und gegebenenfalls die falschen Ergebnisse überschrieben werden:

if(-not (Has-TestCaseCorrectOutcome)

{

   $detailedTestResult.Outcome = $correctOutcome

   $detailedTestResult.ErrorMessage = “You’ve fixed it!”

   $detailedTestResult.Save($true)

}

Challenges & Conclusion

Das Problem der falschen Testergebnisse ist mit oben beschriebenem Ansatz gelöst. Doch was gibt es dabei zu beachten?

Zunächst sei darauf hingewiesen, dass für die Verwendung der beschriebenen API-Aufrufe ein User angegeben werden muss, welcher über ausreichend Berechtigungen verfügt. Dies bedeutet zum einen, dass der User lesend Zugriff auf alle Teile des TFS Test Management (für das entsprechende Team Projekt) benötigt. Desweiteren benötigt der User ausreichend Berechtigungen auf der Build bzw. Release Definition, in welchem die Tests sowie das PowerShell-Skript ausgeführt werden.

Eine weitere Herausforderung ist, dass wenn das Skript zum Korrigieren der Test Ergebnisse direkt nach Ausführung der Tests ausgeführt wird, das Problem entstehen kann, dass die zum Run Functional Test-Task zugehörigen Informationen noch nicht am Build bzw. Release abrufbar sind. Diese Hürde kann gemeistert werden, indem eine Warteschleife eingebaut wird, in welcher die Task Informationen solange angefragt werden bis sie verfügbar sind.

Hat man also die beschriebenen Hürden gemeistert, so bietet der vorgestellte Ansatz mittels PowerShell und TFS API einen leichtgewichtigen Ansatz, um trotz fehlerhafter Auswertung bei datengetriebenen Tests die Vorzüge des Run Functional Test-Tasks zu nutzen.

AIT: Neu in TFS 2018: Jetzt auch mit Wiki

Wohin mit all den projektspezifischen Informationen? Wohin mit dem Know-how zu firmeninternem Entwicklungswissen?

Für dieses Problem ist die Verwendung eines Wikis ein guter Ansatz. In den Visual Studio Team Services (VSTS), der Cloud-Variante des Team Foundation Servers (TFS), ist bereits seit einigen Wochen ein eigenes Wiki enthalten. Mit TFS 2018 bekommt nun auch der Team Foundation Server dieses neue Feature. Dieser Blogbeitrag dreht sich um die folgenden Fragen:

  • Wie ausgereift ist das Wiki?
  • Welche Features sind enthalten?
  • Wie ist das Handling?
  • Welche weiteren Features sind bereits geplant?

Ein vollausgereiftes Wiki kann und darf man noch nicht erwarten. Microsoft hat zunächst erste Grundfunktionalitäten eines Wikis in VSTS bereitgestellt und diese mit jedem Release erweitert. Das Ergebnis sehen wir jetzt im TFS 2018.

Aktuell sind folgende Features u.a. implementiert:

  • Einfache Formatierung mittels Markdown
    Die verschiedenen Möglichkeiten zur Gestaltung einer Wikiseite sind in Abbildung 1 dargestellt. Dabei ist insbesondere die intuitive Verwendung lobenswert, wobei als Beschreibungssprache Markdown zum Einsatz kommt. Auch die Möglichkeit per Drag-and-Drop Anhänge und Bilder direkt in eine Wikiseite integrieren zu können hat uns besonders gut gefallen.

    (Abbildung 1: verschiedene Designmöglichkeiten)
  • Vorschaumodus
    Mit dem Vorschaumodus bekommt man eine komfortable Unterstützung beim Editieren von Wikiseiten. Man kann sofort sehen wie sich Änderungen auswirken und muss nicht erst jedes Mal speichern und die Seite öffnen (siehe Abbildung 2 und 3).


    (Abbildung 2: Vorschaumodus)

    (Abbildung 3: Editiermodus)
  • Versionierung
    Ähnlich wie bei jeder Git-Datei-Revision oder auch bei Builddefinitionen, gibt es auf der Detailseite eine „Side-By-Side-Vergleichsansicht“ und eine „Inline-Vergleichsansicht“ (siehe Abbildung 4 und 5). Damit können Veränderungen im Zeitverlauf im Vorschaufenster betrachtet und bei Bedarf auch rückgängig gemacht werden.

    (Abbildung 4: Side-By-Side-Vergleichsansicht)

    (Abbildung 5: Inline-Vergleichsansicht)

  • Seitenverwaltungsfenster
    Mit einem leistungsstarken Seitenverwaltungsfester lassen sich Seiten sortieren und Hierarchiestrukturen abbilden.
  • Filterung
    Gerade wenn Wikis größer werden ist die schnelle Auffindbarkeit von Informationen besonders wichtig. Mit der Filterfunktion können Seiten nach deren Titel gefiltert werden.

Für alle beschriebenen Features ist das Handling durchwegs als selbsterklärend und flüssig zu bezeichnen. Auch Bugs konnten von uns bis jetzt keine entdeckt werden.

Desweiteren sind bereits folgende Features geplant:

  • Wikiseiten projektübergreifend durchsuchen
  • Wikiseiten mit Workitems verknüpfen
  • Wikistartseite auf der Projekt-Homepage anzeigen
  • Vereinfachtes Speichern mit Strg + S
  • Tags

Fazit:

Das TFS-Wiki macht in seiner ersten Version eine gute Figur. Auf die angekündigten Erweiterungen darf man gespannt sein. Aber wie bei jedem neuen Tool ist es wichtig, dass zu Beginn das Handling stimmt, damit die Einstiegshürde niedrig ist. Eine Strategie und ein Prozess, welche Informationen durch wen und in welcher Qualität im Wiki gespeichert werden sollen, sind aber mindestens genauso wichtig. Werden diese Faktoren beachtet, kann das eigentliche Ziel eines Wikis, nämlich die einfache Wiederauffindbarkeit von Informationen, mit einfachen Mitteln erreicht werden.

 

 

André Krämer: Kostenfreier Xamarin Workshop in Koblenz

Seit einigen Jahren müssen Entwickler einer neuen Herausforderung begegnen. Während die eigene Anwendung in der Vergangenheit in der Regel nur auf einer Plattform, zum Beispiel dem Windows Desktop, oder dem Webbrowser ausgeführt werden musste, besteht heute häufig die die Anforderung auch Clients für mobile Endgeräte auf der Basis von Android, iOS oder Windows zu entwickeln.

Dieser Herausforderung kann man mit verschiedenen Lösungsansätzen begegnen:

  • Native Entwicklung mit den Hersteller SDKs, Werkzeugen und Programmiersprachen, also Objective C / Swift und Xcode für iOS, Java / Kotlin und Android Studio für Android und C# / VB.NET / C++ und Visual Studio für Windows.
  • Hybride App-Entwicklung auf der Basis von Apache Cordova mit HTML und JavaScript
  • Nativ mit C# und Xamarin in Visual Studio

Auf meinen Vorträgen bei verschiedenen Konferenzen, so wie gerade erst kürzlich auf der BASTA, habe ich immer wieder festgestellt, dass gerade für .NET Entwickler der letzte Ansatz - die Entwicklung mit Xamarin - sehr attraktiv ist.

Nun ist es zwar so, dass man unter Xamarin zwar .NET und C# nutzen kann, dies bedeutet aber nicht, dass man die nativen APIs nicht mehr kennen und verstehen muss. In der Praxis ist die Einarbeitung in Xamarin häufig aufwändiger als erwartet.

Aus diesem Grund freue mich, dass wir mit der .NET User Group Koblenz am 24. Oktober im Rahmen der Xamarin Dev Days einen eintägigen kostenfreien Workshop zu Xamarin veranstalten werden.

Betreut wird das ganze von mir sowie Jannik Weyrich und Eric Berres von der BRICKMAKERS GmbH

Das ganze findet von 9:00 - 16:00 Uhr im TZK - TechnologieZentrum Koblenz, Universitätsstraße 3, 56070 Koblenz statt.

Im Detail wird der Ablauf wie folgt sein:

  • 09:00 - 09:20: Registrierung
  • 09:30 - 10:10: Intro to Xamarin
  • 10:20 - 11:00: Xamarin Forms
  • 11:10 - 11:50: Xamarin + Azure
  • 12:00 - 13:00: Mittagessen
  • 13:00 - 16:00: Hands on Labs

Die Registrierung erfolgt unter: https://ti.to/xamarin/dev-days-koblenz-2017. Dort gibt es auch Tipps zur Konfiguration der eigenen Hardware für die Hand on Labs.

Die Teilnehmerzahl ist auf 30 beschränkt. Daher als Tipp: so schnell wie möglich registrieren!

Golo Roden: DDD & Co., Teil 9: Code für das Read Model

In einer der vergangenen Folgen ist bereits Code entstanden, der den Entwurf auf der Basis von Kommandos und fachlichen Ereignissen widerspiegelt. Nachdem nun auch die Konzepte CQRS und Eventual Consistency bekannt sind, ist es an der Zeit, die Leseseite vorzubereiten.

Jürgen Gutsch: Unit Testing an ASP.​NET Core Application

ASP.NET Core 2.0 is out and it is great. Testing worked well in the previous versions, but in 2.0 it is much more easier.

Xunit, Moq and FluentAssertions are working great with the new Version of .NET Core and ASP.NET Core. Using this tools Unit Testing is really fun. Even more fun with testing is provided in ASP.NET Core. Testing Controllers wasn't easier in the previous versions.

If you remember the old Web API and MVC version, based on System.Web, you'll probably also remember how to write unit test for the Controllers.

In this post I'm going to show you how to unit test your controllers and how to write integration tests for your controllers.

Preparing the project to test:

To show you how this works, I created a new "ASP.NET Core Web Application" :

Now I needed to select the Web API project. Be sure to select ".NET Core" and "ASP.NET Core 2.0":

To keep this post simple, I didn't select an authentication type.

In this project is nothing special, except the new PersonsController, which is using a PersonService:

[Route("api/[controller]")]
public class PersonsController : Controller
{
    private IPersonService _personService;

    public PersonsController(IPersonService personService)
    {
        _personService = personService;
    }
    // GET api/values
    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var models = _personService.GetAll();

        return Ok(models);
    }

    // GET api/values/5
    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id)
    {
        var model = _personService.Get(id);

        return Ok(model);
    }

    // POST api/values
    [HttpPost]
    public async Task<IActionResult> Post([FromBody]Person model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var person = _personService.Add(model);

        return CreatedAtAction("Get", new { id = person.Id }, person);
    }

    // PUT api/values/5
    [HttpPut("{id}")]
    public async Task<IActionResult> Put(int id, [FromBody]Person model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _personService.Update(id, model);

        return NoContent();
    }

    // DELETE api/values/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(int id)
    {
        _personService.Delete(id);
        return NoContent();
    }
}

The Person class is created in a new folder "Models" and is a simple POCO:

public class Person
{
  public int Id { get; set; }
  [Required]
  public string FirstName { get; set; }
  [Required]
  public string LastName { get; set; }
  public string Title { get; set; }
  public int Age { get; set; }
  public string Address { get; set; }
  public string City { get; set; }
  [Required]
  [Phone]
  public string Phone { get; set; }
  [Required]
  [EmailAddress]
  public string Email { get; set; }
}

The PersonService uses GenFu to auto generate a list of Persons:

public class PersonService : IPersonService
{
    private List<Person> Persons { get; set; }

    public PersonService()
    {
        var i = 0;
        Persons = A.ListOf<Person>(50);
        Persons.ForEach(person =>
        {
            i++;
            person.Id = i;
        });
    }

    public IEnumerable<Person> GetAll()
    {
        return Persons;
    }

    public Person Get(int id)
    {
        return Persons.First(_ => _.Id == id);
    }

    public Person Add(Person person)
    {
        var newid = Persons.OrderBy(_ => _.Id).Last().Id + 1;
        person.Id = newid;

        Persons.Add(person);

        return person;
    }

    public void Update(int id, Person person)
    {
        var existing = Persons.First(_ => _.Id == id);
        existing.FirstName = person.FirstName;
        existing.LastName = person.LastName;
        existing.Address = person.Address;
        existing.Age = person.Age;
        existing.City = person.City;
        existing.Email = person.Email;
        existing.Phone = person.Phone;
        existing.Title = person.Title;
    }

    public void Delete(int id)
    {
        var existing = Persons.First(_ => _.Id == id);
        Persons.Remove(existing);
    }
}

public interface IPersonService
{
  IEnumerable<Person> GetAll();
  Person Get(int id);
  Person Add(Person person);
  void Update(int id, Person person);
  void Delete(int id);
}

This Service needs to be registered in the Startup.cs:

services.AddScoped<IPersonService, PersonService>();

If this is done, we can create the test project.

The unit test project

I always choose xUnit (or Nunit) over MSTest, but feel free to use MSTest. The used testing framework doesn't really matter.

Inside that project, I created two test classes: PersonsControllerIntegrationTests and PersonsControllerUnitTests

We need to add some NuGet packages. Right click the project in VS2017 to edit the project file and to add the references manually, or use the NuGet Package Manager to add these packages:

<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
<PackageReference Include="FluentAssertions" Version="4.19.2" />
<PackageReference Include="Moq" Version="4.7.63" />

The first package contains the dependencies to ASP.NET Core. I use the same package as in the project to test. The second package is used for the integration tests, to build a test host for the project to test. FluentAssertions provides a more elegant way to do assertions. And Moq is used to create fake objects.

Let's start with the unit tests:

Unit Testing the Controller

We start with an simple example, by testing the GET methods only:

public class PersonsControllerUnitTests
{
  [Fact]
  public async Task Values_Get_All()
  {
    // Arrange
    var controller = new PersonsController(new PersonService());

    // Act
    var result = await controller.Get();

    // Assert
    var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
    var persons = okResult.Value.Should().BeAssignableTo<IEnumerable<Person>>().Subject;

    persons.Count().Should().Be(50);
  }

  [Fact]
  public async Task Values_Get_Specific()
  {
    // Arrange
    var controller = new PersonsController(new PersonService());

    // Act
    var result = await controller.Get(16);

    // Assert
    var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
    var person = okResult.Value.Should().BeAssignableTo<Person>().Subject;
    person.Id.Should().Be(16);
  }
  
  [Fact]
  public async Task Persons_Add()
  {
    // Arrange
    var controller = new PersonsController(new PersonService());
    var newPerson = new Person
    {
      FirstName = "John",
      LastName = "Doe",
      Age = 50,
      Title = "FooBar",
      Email = "john.doe@foo.bar"
    };

    // Act
    var result = await controller.Post(newPerson);

    // Assert
    var okResult = result.Should().BeOfType<CreatedAtActionResult>().Subject;
    var person = okResult.Value.Should().BeAssignableTo<Person>().Subject;
    person.Id.Should().Be(51);
  }

  [Fact]
  public async Task Persons_Change()
  {
    // Arrange
    var service = new PersonService();
    var controller = new PersonsController(service);
    var newPerson = new Person
    {
      FirstName = "John",
      LastName = "Doe",
      Age = 50,
      Title = "FooBar",
      Email = "john.doe@foo.bar"
    };

    // Act
    var result = await controller.Put(20, newPerson);

    // Assert
    var okResult = result.Should().BeOfType<NoContentResult>().Subject;

    var person = service.Get(20);
    person.Id.Should().Be(20);
    person.FirstName.Should().Be("John");
    person.LastName.Should().Be("Doe");
    person.Age.Should().Be(50);
    person.Title.Should().Be("FooBar");
    person.Email.Should().Be("john.doe@foo.bar");
  }

  [Fact]
  public async Task Persons_Delete()
  {
    // Arrange
    var service = new PersonService();
    var controller = new PersonsController(service);

    // Act
    var result = await controller.Delete(20);

    // Assert
    var okResult = result.Should().BeOfType<NoContentResult>().Subject;

    // should throw an eception, 
    // because the person with id==20 doesn't exist enymore
    AssertionExtensions.ShouldThrow<InvalidOperationException>(
      () => service.Get(20));
  }
}

This snippets also shows the benefits of FluentAssertions. I really like the readability of this fluent API.

BTW: Enabling live unit testing in Visual Studio 2017 is impressive:

With this unit test approach the invalid ModelState isn't tested. To get this tested we need to test more integrated. Also the ModelBinder should be executed, to validate the input and to set the ModelState

One last thing about unit test: I mentioned Moq before, because I propose to isolate the code to test. This means, wouldn't use a real service, when I test a controller. I also wouldn't use a real repository, when I test a service. And so on... That's why you should work with fake services instead of real ones. Moq is a tool to create such fake objects and to set them up:

[Fact]
public async Task Persons_Get_From_Moq()
{
  // Arrange
  var serviceMock = new Mock<IPersonService>();
  serviceMock.Setup(x => x.GetAll()).Returns(() => new List<Person>
  {
    new Person{Id=1, FirstName="Foo", LastName="Bar"},
    new Person{Id=2, FirstName="John", LastName="Doe"},
    new Person{Id=3, FirstName="Juergen", LastName="Gutsch"},
  });
  var controller = new PersonsController(serviceMock.Object);

  // Act
  var result = await controller.Get();

  // Assert
  var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
  var persons = okResult.Value.Should().BeAssignableTo<IEnumerable<Person>>().Subject;

  persons.Count().Should().Be(3);
}

To learn more about Moq, have a look into the repository: https://github.com/moq/moq4

Integration testing the Controller

But let's start with the simple cases, by testing the GET methods first. To test the integration of a web API or any other web based API, you need to have a web server running. Even in this case a web server is needed, but fortunately there is a test server which can be used. With this host it is not needed to set-up a separate web server on the test machine:

public class PersonsControllerIntegrationTests
{
  private readonly TestServer _server;
  private readonly HttpClient _client;

  public PersonsControllerIntegrationTests()
  {
    // Arrange
    _server = new TestServer(new WebHostBuilder()
                             .UseStartup<Startup>());
    _client = _server.CreateClient();
  }
  // ... 
}

At first, the TestServer will be set up. This guy gets the WebHostBuilder - known from every ASP.NET Core 2.0 application - and uses the Startup of our project to test. At second, we are able to create a HttpClient out of that server. We'll use this HttpClient in the tests to create request to the server and to receive the responses.

[Fact]
public async Task Persons_Get_All()
{
  // Act
  var response = await _client.GetAsync("/api/Persons");
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();

  // Assert
  var persons = JsonConvert.DeserializeObject<IEnumerable<Person>>(responseString);
  persons.Count().Should().Be(50);
}

[Fact]
public async Task Persons_Get_Specific()
{
  // Act
  var response = await _client.GetAsync("/api/Persons/16");
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();

  // Assert
  var person = JsonConvert.DeserializeObject<Person>(responseString);
  person.Id.Should().Be(16);
}

Now let's test the POST request:

[Fact]
public async Task Persons_Post_Specific()
{
  // Arrange
  var personToAdd = new Person
  {
    FirstName = "John",
    LastName = "Doe",
    Age = 50,
    Title = "FooBar",
    Phone = "001 123 1234567",
    Email = "john.doe@foo.bar"
  };
  var content = JsonConvert.SerializeObject(personToAdd);
  var stringContent = new StringContent(content, Encoding.UTF8, "application/json");

  // Act
  var response = await _client.PostAsync("/api/Persons", stringContent);

  // Assert
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();
  var person = JsonConvert.DeserializeObject<Person>(responseString);
  person.Id.Should().Be(51);
}

We need to prepare a little bit more. Here we create a StringContent object, which derives from HttpContent. This will contain the Person we want to add as JSON string. This get's sent via post to the TestServer.

To test the invalid ModelState, just remove a required field or pass a wrong formatted email address or telephone number and test against it. In this sample, I test against three missing required fields:

[Fact]
public async Task Persons_Post_Specific_Invalid()
{
  // Arrange
  var personToAdd = new Person { FirstName = "John" };
  var content = JsonConvert.SerializeObject(personToAdd);
  var stringContent = new StringContent(content, Encoding.UTF8, "application/json");

  // Act
  var response = await _client.PostAsync("/api/Persons", stringContent);

  // Assert
  response.StatusCode.Should().Be(System.Net.HttpStatusCode.BadRequest);
  var responseString = await response.Content.ReadAsStringAsync();
  responseString.Should().Contain("The Email field is required")
    .And.Contain("The LastName field is required")
    .And.Contain("The Phone field is required");
}

This is almost the same pattern for the PUT and the DELETE requests:

[Fact]
public async Task Persons_Put_Specific()
{
  // Arrange
  var personToChange = new Person
  {
    Id = 16,
    FirstName = "John",
    LastName = "Doe",
    Age = 50,
    Title = "FooBar",
    Phone = "001 123 1234567",
    Email = "john.doe@foo.bar"
  };
  var content = JsonConvert.SerializeObject(personToChange);
  var stringContent = new StringContent(content, Encoding.UTF8, "application/json");

  // Act
  var response = await _client.PutAsync("/api/Persons/16", stringContent);

  // Assert
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();
  responseString.Should().Be(String.Empty);
}

[Fact]
public async Task Persons_Put_Specific_Invalid()
{
  // Arrange
  var personToChange = new Person { FirstName = "John" };
  var content = JsonConvert.SerializeObject(personToChange);
  var stringContent = new StringContent(content, Encoding.UTF8, "application/json");

  // Act
  var response = await _client.PutAsync("/api/Persons/16", stringContent);

  // Assert
  response.StatusCode.Should().Be(System.Net.HttpStatusCode.BadRequest);
  var responseString = await response.Content.ReadAsStringAsync();
  responseString.Should().Contain("The Email field is required")
    .And.Contain("The LastName field is required")
    .And.Contain("The Phone field is required");
}

[Fact]
public async Task Persons_Delete_Specific()
{
  // Arrange

  // Act
  var response = await _client.DeleteAsync("/api/Persons/16");

  // Assert
  response.EnsureSuccessStatusCode();
  var responseString = await response.Content.ReadAsStringAsync();
  responseString.Should().Be(String.Empty);
}

Conclusion

That's it.

Thanks to the TestServer! With this it is really easy to write integration tests the controllers. Sure, it is still a little more effort than the much simpler unit tests, but you don't have external dependencies anymore. No external web server to manage. No external web server, which is out of control.

Try it out and tell me about your opinion :)

Norbert Eder: Screenshots und Screenrecording mit ShareX

Im Laufe der Jahre habe ich viele unterschiedliche Tools für das Erstellen von Screenshots bzw. Screenrecordings ausprobiert und verwendet. Nun bin ich seit längerer Zeit bei ShareX und sehe keinen Grund, zu wechseln.

Warum ist ShareX empfehlenswert?

ShareX hat so viele Funktionen, dass es tatsächlich recht schwer ist, die richtige Auswahl zu finden. Deshalb erwähne ich die Funktionen, die ich verwende bzw. die absolut nützlich sind:

  • Screenshots und Aufnahmen per Hotkey bzw. per Klick auf das Tray-Icon – dabei kann der Bereich frei gewählt werden bzw. werden automatisch Bereiche vorgeschlagen (das funktioniert überraschend gut und zielsicher)
  • Dateien können automatisch (oder manuell) auf unterschiedliche Dienste hochgeladen werden (80 unterschiedliche Ziele sind möglich)
  • Es können Aktionen konfiguriert werden. Diese bestimmen, was z.B. nach der Aufnahme oder nach einem Upload passieren soll (Öffnen in einem speziellen Programm, Drucken, Ausführen eines OCR usw.). Somit lässt sich einiges automatisieren.
  • Viele hilfreiche Tools á la Color Picker, Thumbnail Generator (Bild und Video), Bildeditor, Bildeffekte, ein Tool zum Verbinden von Bildern, usw.

ShareX ist eine freie Software. Wer sich einbringen möchte, kann das auf Github tun.

Welche Grundeinstellungen sollte ich vornehmen?

Das hängt natürlich durchaus von deinen Wünschen und Anforderungen ab. Da ich keine automatischen Uploads durchführen möchte, wird diese Funktion von mir deaktiviert:

ShareX - Uploads deaktivieren

Zudem möchte ich den Mauszeiger bei Screenshots nicht am Bild haben. Dies kann ebenfalls deaktiviert werden:

ShareX - Mauszeiger deaktivieren

Schlussendlich empfiehlt es sich, den Pfad in dem sämtliche Screenshots und Aufnahmen abgelegt werden, anzupassen:

ShareX - Pfade anpassen

Viel Spaß mit diesem Tool.

The post Screenshots und Screenrecording mit ShareX appeared first on Norbert Eder.

AIT: Neu in TFS 2018: Umgang mit Build Definitionen

Dies ist der erste Beitrag zu unserer Blogserie “Neu in TFS 2018”, der sich mit konkreten Erweiterungen des Buildsystems im TFS beschäftigt. Dabei geht es nicht um grundlegende Veränderungen, vielmehr um nützliche Erweiterungen, die wir in diesem Blogpost vorstellen.

Das Buildsystem, welches in TFS 2015 eingeführt wurde, erfüllte vielen Anwendern einen sehnlichen Wunsch, nämlich historische Aufzeichnungen, wer eine Build Definition wann und wie verändert hat. Dafür wurde eine auf JSON basierte Repräsentation gewählt. Schnell kam der Wunsch auf, manuell Änderungen im JSON vorzunehmen oder Builds auf der JSON-Basis zu ex- und importieren.

Insbesondere beim Wechsel auf ein neues Major-Release oder auch bei technologisch ähnlichen Projekten möchte man gern eine ggf. über lange Zeit hinweg gewachsene Build Definition wiederverwenden. Dies ist nun leicht möglich, da es im User Interface eine einfache Ex- und Import-Funktionalität gibt (siehe folgende Abbildung).

image

Im Ergebnis erhält man eine JSON-Datei, die wie folgt aussieht:

image

Die dazu verfügbare REST API stellt einen weiteren interessanten Aspekt dar. So kann man mittels eines einfachen REST-Aufrufes während der Buildausführung die Build Definition exportieren und als JSON, beispielsweise mit in der Drop Location ablegen. Dadurch wird die Reproduzierung eines einst erstellten Builds sehr viel einfacher, da man auf einfache Weise, das beim Zeitpunkt des Builds entstandene JSON-File importieren kann. Dieser Unterbau ist nicht neu, vielmehr ist die neue Export Funktionalität “nur” das User Interface dazu. Die REST API lässt sich jedoch wie zuvor beschrieben hervorragend für den automatisierten Export verwenden.

Eine weitere interessante Neuigkeit ist die Möglichkeit Build Templates als Extension zur Verfügung zu stellen. Wenn man, wie im Beispiel oben beschrieben, immer wieder ähnliche Build Definitionen benötigt, sind auch eigene Build Templates eine Option. Dabei kann man, ähnlich wie die bereits vordefinierten Build Templates, eigene Templates anlegen, die beim Erstellen einer neuen Build Definition zur Auswahl stehen. Nachfolgend ist der Auszug eines Beispiels zu sehen, welches vollständig auf GitHub zu finden ist.

image

Im Bereich Build und Release Management stecken in TFS 2018 noch mehr Neuigkeiten, von denen wir in weiteren Blog Posts berichten.

Golo Roden: Einführung in React, Folge 1: Erste Schritte

Die von Facebook entwickelte Open-Source-Bibliothek React dient dem effizienten Rendern der Anzeige in Webanwendungen. Sie verwendet zahlreiche Konzepte der funktionalen Programmierung und basiert auf Komponenten. Wie gelingt der Einstieg?

Norbert Eder: ASPNET Core: Prerendering failed – Cannot find module main-server

Nach dem Ziehen eines .NET Core/Angular-Projektes, basierend auf das .NET Core Angular Starter Template, konnte dieses nicht korrekt ausgeführt werden. Die Datei main-server.js wurde nicht erstellt und somit natürlich nicht geladen werden:

fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]
      An unhandled exception has occurred: 
      Call to Node module failed with error: 
      Prerendering failed because of error: 
      Error: Cannot find module '\..\main-server'

Die Ursache dieses Fehlers liegt darin, dass .NET Core für dieses Projekt nicht im Development-Modus betrieben wird und für diesen Modus entsprechende Tasks konfiguriert sind. Damit alle notwendigen Dateien vollständig generiert werden, ist für dieses Projekt in dessen Eigenschaften die Umgebungsvariable ASPNETCORE_ENVIRONMENT auf Development umzustellen:

.NET Core ASPNETCORE_ENVIRONMENT

Insgesamt stehen die Modi Development, Staging und Production zur Verfügung.

The post ASPNET Core: Prerendering failed – Cannot find module main-server appeared first on Norbert Eder.

AIT: Neu in TFS 2018: Keine XAML Builds mehr unterstützt

In diesem kurzen Beitrag möchten wir darauf aufmerksam machen, dass mit dem TFS 2018 eine große Änderung bzgl. der Abwärtskompatibilität des Build Systems verbunden ist. Nachdem mit TFS 2015 ein komplett neues Buildsystem eingeführt wurde (siehe auch: Neu in TFS 2015 – Das Build-System), haben beide Systeme – alt und neu – eine Zeit lang parallel existiert. Dies hatte zum Ziel, dass zunächst das neue Buildsystem weiter reifen und sich die Benutzer daran gewöhnen konnten.

Mit dem TFS 2018 ist es nicht mehr möglich einen XAML Build Controller anzuschließen und auf dem XAML Workflow basierte Builds zu verwalten. Das heißt ganz konkret: Solange Sie noch (teilweise) XAML Builds produktiv in Verwendung haben, sollten Sie kein Upgrade auf TFS 2018 durchführen. Sie sehen im TFS 2018 jedoch nach wie vor Ihre XAML Builddefinitionen und können dort die gespeicherten Buildergebnisse abrufen, jedoch eben keine Builds mehr starten.

Wenn Sie also noch XAML Builds verwenden, heißt es jetzt Abschied nehmen und den Weg in die neue Welt antreten. Wir wurden in unseren Projekten oft nach Migrationswerkzeugen gefragt. Die schlechte Nachricht zuerst: Es gibt keine Werkzeuge, die per Knopfdruck XAML Builds auf die neue webbasierte Buildengine migrieren. Aber auch diese Me­dail­le hat zwei Seiten. Denn dieser harte Einschnitt ist nicht nur eine gute Gelegenheit alte Zöpfe abzuschneiden sondern er hat auch einiges zu bieten. MVP Jeff Bramwell hat in seinem Blogpost Why Should I Leave XAML Builds? bereits 2016 die wesentlichen Motivationspunkte für den Umstieg aufgezeigt. Das neue Konzept, die Offenheit und die Erweiterbarkeit einschließlich des Marketplaces ermöglichen es viele selbsterstellte Lösungsbausteine zu hinterfragen und abzulösen.

Eine detailliertere Darstellung und konkrete Hilfestellung zum Umstieg finden Sie in unserem kostenfreien Artikel Von TFS XAML Build nach TFS Build, der auch in der Windows Developer 3.2017 erschienen ist. Dort finden Sie auch Beispiele an typischen Anpassungen im alten XAML-Buildsystem zusammen mit unseren Vorschlägen wie diese im neuen Buildsystem umgesetzt werden können. Die nachfolgende Tabelle, die aus dem Artikel entnommen ist, zeigt zusammengefasst Beispiele von XAML-Anpassungen und ihre Alternativen.

Bitte klicken Sie zum Vergrößern der Tabelle darauf.

Gerne unterstützen wir Sie bei Ihrem konkreten Umstieg auf das neue Buildsystem oder generell bei der Einführung von TFS Builds. Sprechen Sie uns einfach an.

Jürgen Gutsch: Current Activities

Wow! Some weeks without a new blog post. My plan was to write at least one post per week. But this doesn't always work. Usually I write in the train, when I go to work to Basel (CH). Sometimes I write two or three posts in advance, to publish it later on. But I had some vacation, worked two weeks completely at home, and I had some other things to prepare and to manage. This is a short overview of the current activities:

INETA Germany

The last year was kinda busy, even from the developer community perspective. The leadership of the INETA Germany was one, but a pretty small part. We are currently planning some huge changes with the INETA Germany. One of the changes is to open the INETA Germany for Austrian and Swiss user groups and speakers. We already have some Austrian and Swiss speakers registered in the Speakers Bureau. Contact us via hallo@ineta-deutschland.de, if you are a Austrian or Swiss user group and if you wanna be a INETA member.

Authoring articles for a magazine

Also the last twelve months was busy, because almost every month I wrote an article for one of the most popular German speaking developer Magazine. All this articles were about ASP.NET Core and .NET Core. Here is a list of them:

And there will be some more in the next months.

Technical Review of a book

For the first time I do a technical review for a book about "ASP.NET Core 2.0 and Angular 4". I'm a little bit proud of getting asked doing it. The author is one of the famous European authors and this book is awesome and great for Angular and ASP.NET Core beginners. Unfortunately I cannot yet mention the Authors name and link to the book title, but I will definitely if it is finished.

Talks

What else? Yes I was talking and I will talk about various topics.

In August I did a talk at the Zurich Azure meetup about how we (the yooapps.com) created the digital presentation (web site, mobile apps) for one of the most successful soccer clubs in Switzerland on Microsoft Azure. It is also about how we maintain it and how we deploy it to Azure. I'l do the talk at the Basel .NET User Group today and in October at the Azure meetup Karlsruhe. I'd like to do this talk at a lot more user groups, because we are kinda proud of that project. Contact me, if you like to have this talk in your user group. (BTW: I'm in the Seattle (WA) area at the beginning of March 2018. So I could do this talk in Seattle or Portland too.)

Title: Soccer in the cloud – FC Basel on Azure

Abstract: Three years ago we released a completely new version of the digital presentation (website, mobile apps, live ticker, image database, videos, shop and extranet) of one of the most successful soccer clubs In Switzerland. All of the services of that digital presentation are designed to run on Microsoft Azure. Since then we are continuously working on it, adding new features, integrating new services and improving performance, security and so on. This talk is about, how the digital presentation was built, how it works and how we continuously deploy new feature and improvements to Azure.

At the ADC 2017 in Cologne I talked about customizing ASP.NET Core (sources on GitHub) and I did a one day workshop about developing a web application using ASP.NET Core (sources on GitHub)

Also in October I do an introduction talk about .NET Core, .NET Standard and ASP.NET Core at the "Azure & .NET meetup Freiburg" (Germany)

Open Source:

One Project that should be released for a while is LightCore. Unfortunately I didn't find some time the last weeks to work on that. Also Peter Bucher (who initially created that project and is currently working on that) was busy too. We Currently have a critical bug, which needs to be solved first, before we are able to release. Even the ASP.NET Core integrations needs to be finished first.

Doing a lot of sports

Since almost 18 Months, I do a lot of running and biking. This also takes some time, but is a lot of addictive fun. I cannot live two days without being outside running and biking. I wouldn't believe that, if you told me about it two years before. That changed my life a lot. I attend official runs and bike races and last but not least: I lost around 20 kilos the last one and a half years.

Stefan Henneken: IEC 61131-3: Extend UNION via inheritance

In the post IEC 61131-3: Additional language extensions, I briefly described the UNION. A reader comment has pointed out to me the possibility, that a UNION can also be expanded by EXTENDS. Since this simplifies the handling of a UNION and the norm does not indicate that, I would like to introduce this possibility in a (very) short post.

As already described in a post, a UNION makes it possible to define your own data types, all elements of which share the same storage space. Here is once more the example, where a 16-bit variable can be accessed individually to the low byte and the high byte. First, a structure consisting of two bytes is created.

TYPE U_Test :
UNION
  nVar1   : WORD;
  stVar2  : ST_Bytes;
END_UNION
END_TYPE
TYPE ST_Bytes :
STRUCT
  nLSB   : BYTE;
  nMSB   : BYTE;
END_STRUCT
END_TYPE

This example shows how the lower byte (LSB) and the higher byte (MSB) are determined from a variable of type WORD without bit operations.

PROGRAM MAIN
VAR
  uVar    : U_Test;
  nA, nB  : BYTE;
END_VAR

uVar.nVar1 := 16#1234;
nA := uVar.stVar2.nLSB; // Value: 16#34 (LSB)
nB := uVar.stVar2.nMSB; // Value: 16#12 (MSB)

EXTENDS for UNIONS

The same task can also be solved with inheritance, which is a bit more elegant. In this case, the union U_Test inherits from ST_Bytes.

TYPE U_Test EXTENDS ST_Bytes :
UNION
  nVar1 : WORD;
  // stVar2 : ST_Bytes; // Not necessary, is inherited from ST_Bytes.
END_UNION
END_TYPE

Access to nLSB and nMSB no longer needs to be done via a variable explicitly declared in the UNION.

uVar.nVar1 := 16#1234;
nA := uVar.nLSB; // Value: 16#34 (LSB)
nB := uVar.nMSB; // Value: 16#12 (MSB)

Holger Schwichtenberg: Änderungen in ASP.NET Core 2.0 und veraltete Dokumentation

Microsoft ändert - auch nach einer mehr als zweijährigen Betaphase - zwischen Hauptversionen immer noch Konzepte in ASP.NET Core. Selbst einen Monat nach Erscheinen von ASP.NET Core 2.0 ist die Dokumentation an vielen Stellen auf dem Stand von Version 1.x.

Robert Meyer: SelfHosted ASP.NET Core 2.0 Application

Auf Grund einer Projektanforderung musste ich mich die Tage damit auseinandersetzen, wie man eine ASP.NET Core 2.0 Website und Web API in einem eigenen Prozess als Windows Service bereitstellt.
Hierbei gibt es einige Besonderheiten zu beachten, auf welche ich in diesem Post gern näher eingehen möchte.

Folgende Technologien kommen zum Einsatz:

  • Alle Projekte basieren auf .NET Core 2.0 oder .NET Standard 2.0
  • Autofac als IoC Container
  • NLog für das Logging
  • Peter Kottas WindowsService als ServerFabric (GitHub)

Grundlegende Architektur der Anwendung

Architecture

Zu finden ist dieses Beispiel auf GitHub.

Grundlegendes Vorgehen

Die Konsolenapplikation kann als WindowsService oder direkt ausgeführt werden und hostet die Website und die API. Die Website und die API sind beides .NETStandard 2.0 Projekte, während die Console eine .NET Core 2.0 Anwendung darstellt.

Aufbau der SelfHosted.Console

IApplication definiert ein Interface zum Starten und Stoppen der Applikationen. Eine Applikation kann eine Website oder eine WebApi sein.

IApplication

Diese Applikationen werden in Autofac registriert und einem gekapselten MicroService geladen und gestartet.

	public class SelfHostedWindowService : MicroService, IMicroService
    {
        private IServiceProvider _provider;

        public void Start()
        {
            this.StartBase();

            var builder = new ContainerBuilder();
            builder.RegisterType<WebApplication>().As<IApplication>();
            builder.RegisterType<WebApiApplication>().As<IApplication>();
            var applicationContainer = builder.Build();

            _provider = new AutofacServiceProvider(applicationContainer);

            foreach (var app in _provider.GetServices<IApplication>())
            {
                app.Start();
            }

            System.Console.WriteLine("Windows services started.");
        }

        public void Stop()
        {
            this.StopBase();

            foreach (var app in _provider.GetServices<IApplication>())
            {
                app.Stop();
            }

            System.Console.WriteLine("Windows services stopped.");
        }
    }

Beim Starten der Konsole oder des Services, wird der MicroService registriert und in einer ServiceFactory geladen. Dadurch starten alle Applikationen, welche in den jeweiligen MicroService definiert sind.

	ServiceRunner<SelfHostedWindowService>.Run(config =>
	{
		var serviceName = "SelfHosted.WindowsService";

		config.SetName(serviceName);
		config.Service(serviceConfig =>
		{
			serviceConfig.ServiceFactory((service, extraArguments) =>
			{
				return new SelfHostedWindowService();
			});

			serviceConfig.OnStart((service, extraArguments) =>
			{
				System.Console.WriteLine("Service {0} started", serviceName);
				service.Start();
			});

			serviceConfig.OnStop((service) =>
			{
				System.Console.WriteLine("Service {0} stopped", serviceName);
				service.Stop();
			});

			serviceConfig.OnError(e =>
			{
				System.Console.WriteLine($"Service '{serviceName}' errored with exception : {e.Message}");
			});
		});
	});

Besonderheiten in der ASP.NET Core 2.0 Website

Es gibt jedoch beim hosten seiner ASP.NET Core 2.0 Website in einer Console noch drei wichtige Dinge zu beachten.

      1. Alle Views müssen Embedded werden. Dafür habe ich folgende Extension Methode geschrieben, welche im Startup bei AddRazorOptions aufgerufen wird.
        	public static RazorViewEngineOptions AddViews(this RazorViewEngineOptions options)
        	{
        		options.FileProviders.Add(new EmbeddedFileProvider(typeof(ServiceCollectionExtensions).GetTypeInfo().Assembly, "SelfHosted.Website"));
        		return options;
        	}
        
      2.  Danach stellte sich heraus, dass noch einige Assemblies fehlten. Diese werden ebenfalls per Extension Methode im Startup geladen.
        	public static RazorViewEngineOptions AddCompilationAssemblies(this RazorViewEngineOptions options)
        	{
        		var myAssemblies = AppDomain
        		   .CurrentDomain
        		   .GetAssemblies()
        		   .Where(x => !x.IsDynamic)
        		   .Concat(new[] { // additional assemblies used in Razor pages:
        					typeof(HtmlString).Assembly, // Microsoft.AspNetCore.Html.Abstractions
        					typeof(IViewLocalizer).Assembly, // Microsoft.AspNetCore.Mvc.Localization
        					typeof(IRequestCultureFeature).Assembly // Microsoft.AspNetCore.Localization
        		   })
        		   .Select(x => MetadataReference.CreateFromFile(x.Location))
        		   .ToArray();
        
        		var previous = options.CompilationCallback;
        
        		options.CompilationCallback = context =>
        		{
        			previous?.Invoke(context);
        			context.Compilation = context.Compilation.AddReferences(myAssemblies);
        		};
        
        		return options;
        	}
        
      3. Jetzt müssen noch alle statischen Files, welche z.B. im wwwroot liegen embedded werden. Auch hier gibt es wieder eine passende Extension Methode.
        	public static IServiceCollection AddStaticFiles(this IServiceCollection collection)
        	{
        		// static files are embedded resources in the "wwwroot" folder
        		collection.Configure<StaticFileOptions>(options =>
        		{
        			options.FileProvider = new EmbeddedFileProvider(typeof(Startup).Assembly, typeof(Startup).Namespace + ".wwwroot");
        		});
        
        		return collection;
        	}
        

Aufgerufen werden die Extension Methods im Startup der Website, in der Funktion ConfigureServices wie folgt:

	public IServiceProvider ConfigureServices(IServiceCollection services)
	{
		services.AddSingleton<IConfiguration>(Configuration);
		services.AddMvc()
			.AddRazorOptions(options =>
			{
				options.AddViews();
				options.AddCompilationAssemblies();
			});
		services.AddStaticFiles();

		var builder = new ContainerBuilder();
		builder.Populate(services);
		this.ApplicationContainer = builder.Build();

		return new AutofacServiceProvider(this.ApplicationContainer);
	}

 

Zusammenfassung

Auf diesem Weg erreicht man eine sehr leichtgewichtige Anwendung, welche komplett in einem eigenen Prozess unabhängig von dem Betriebssystem und Webserver installiert werden kann. Somit erreicht man bei seiner Produktentwicklung, welchen bei Kunden vor Ort installiert werden muss, eine sehr hohe Flexibilität und ich unabhängig der Umgebung.


AIT: Neu in TFS 2018: Neues für Work Item Tracking

In diesem Beitrag unserer Blogserie „Neu in TFS 2018“ nehmen wir die Neuerungen, die TFS 2018 für das Work Item Tracking mit sich bringt, unter die Lupe. Zwar enthält das neueste Release keine grundlegenden Änderungen in diesem Bereich, jedoch sind es die kleinen Dinge, die einem den Projektalltag erleichtern. Lesen Sie also in den folgenden Abschnitten über die kleinen Helfer im Bereich Work Item Tracking.

Backlogs, Boards und Abfragen besser filtern

Die teils verwirrende und unterschiedliche Verwendung von Filtermechanismen wird mit TFS 2018 endlich vereinheitlicht. Damit ist das Filtern über Backlogs, Kanban-Boards, Sprints-Backlogs und Abfragen sowie die Testfallverwaltung konsistent (Bild 1). Des Weiteren kann das Backlog nun zusätzlich zu den Tag- und Schlüsselwort-Filtern auch nach Work Item Typen, Zuständen und zugewiesenen Personen weiter eingeschränkt werden.

Bild 1: Backlogs, Boards und Abfragen besser filtern

Bild 1: Backlogs, Boards und Abfragen besser filtern

 

Kanban-Board-Element erweitern, um leere Felder auf Karten anzuzeigen

Im Kanban-Board war es bisher nur möglich, leere Felder auf den Karten auszublenden. Wenn man diese dann aber füllen wollte, musste man umständlich das Element öffnen und die Daten dort eingeben.

Ab TFS 2018 ändert sich dieser Umstand durch die Einführung des Chevrons im Kanban-Board (Bild 2). Dieses klappt automatisch die leeren Felder ein, zeigt aber durch einen Klick, diese auch wieder an. Nun kann man also auch in der Board-Ansicht fehlende Werte leicht und schnell nachtragen (Bild 3).

Bild 2: Kanban-Board-Element erweitern, um leere Felder auf einem anzuzeigen

Bild 2: Kanban-Board-Element erweitern, um leere Felder auf einem anzuzeigen

Bild 3: Kanban-Board-Element erweitert

Bild 3: Kanban-Board-Element erweitert

Sicherstellung der Eingabe von wichtigen Informationen

In bestimmten Kundenumfeldern, zum Beispiel stark regulierten Bereichen, ist es nötig, dass ein TFS-Anwender, bei der Bearbeitung von Work Items, Felder nicht leer lässt. Um dies sicherzustellen können nun per Work Item Form Service Methode IPromise<void> setError(errorMessage) der REST API benutzerdefinierte Steuerelemente, Gruppen und Seiten von Formularen das Speichern explizit verhindern, bis entsprechende Werte eigegeben wurden.

Erweiterte Projekterstellung im Web Access

Das Erstellen eines Teamprojekts im TFS Web Access enthält nun den Großteil der Features, die auch beim Erstellen eines Teamprojekts in Visual Studio verfügbar sind. Der Unterschied zu Visual Studio ist, dass in der Webvariante keine SSRS-Berichte automatisch mit angelegt werden. Jedoch können diese im Nachgang über das Kommandozeilen-Tool tfsconfig bereitgestellt und auch aktualisiert werden. Weitere Informationen finden Sie unter Hinzufügen von Projektberichten.

Prozess Template-Manager im Web

Um Ihre Prozess Templates hochzuladen, können Sie ab TFS 2018 auch den Webzugriff verwenden. Dadurch entfällt der Zwang Visual Studio für diese administrative Tätigkeit nutzen zu müssen. Bis Visual Studio 2017 Update 4 ist die Möglichkeit aber weiterhin enthalten. Ab Visual Studio 2017 Update 5 und höher jedoch erfolgt eine automatische Weiterleitung aus dem Visual Studio-Prozess Template Manager in den Web Access.

Mobiles Arbeitselementformular

Mit der neuen mobilen Integration und Navigation können Sie nun alle Teile des TFS auch von unterwegs erreichen. Hinzu kommt, dass alle Felder und Elemente zur mobilen Verwendung überarbeitet wurden.

Bild 4: Mobiles Arbeitselementformular

Bild 4: Mobiles Arbeitselementformular

Fazit

Wie Sie in den vorhergehenden Abschnitten lesen konnten, gibt es einige kleine aber durchaus feine Verbesserungen im TFS 2018 für den Bereich Work Item Tracking. Insbesondere die einheitliche Filterung in den verschiedenen Ansichten erfüllt einen lang gehegten Wunsch vieler User.

Im nächsten Beitrag dieser Reihe geht es um die Änderungen im Build- & Release- Management. Seien Sie also gespannt, welche neuen Features TFS 2018 noch mit sich bringt.

Holger Schwichtenberg: .NET Conf 2017 heute kostenfrei im Internet

Heute um 17:00 Uhr startet die vierte Ausgabe von Microsofts virtueller .NET-Entwicklerkonferenz.

Marco Scheel: SharePoint Online Device Access Option fehlt - Lösung: First Release for everyone

In meinen Projekten (Office 365 / 100% Cloud) ist Conditional Access (CA) nicht mehr wegzudenken. Für uns “SharePoint“ Leute hat CA eine spezielle Geschmacksrichtung. Eine der ersten Session-Based CA Policies arbeitet mit SharePoint und ermöglicht so zusätzliche Kontrollen beim Zugriff auf SharePoint. Microsoft hat das ganze in einem Blogpost beschrieben. Jetzt wurde angekündigt, dass es in die nächste Runde geht und die Konfiguration nicht mehr auf den ganzen Tenant zieht, sondern einzelne Site Collections adressiert. In diesem Zuge wollte ich mal wieder meine AAD + SPO Konfiguration gerade ziehen, aber siehe da… es fehlt was! So sieht es im SharePoint Admin Center unter dem Punkt “Device Access” aus:

image

Eigentlich sollte der Dialog so aussehen:

image

Es fehlt der entscheidende Teil:
“Control access from devices that aren’t compliant or joined to a domain”
oder auf deutsch:
“Zugriff von Geräten steuern, die nicht konform oder einer Domäne beigetreten sind”
Jetzt stellt sich die Frage warum? Mein Tenant ist auf “First Release”, also sollte es doch funktionieren. Eine kurze Recherche hat mich zum überraschenden Ergebnis geführt:

image

Quelle: Microsoft Support - Control access from unmanaged devices

Mein Tenant war tatsächlich für einen Test auf die Option “First release for selected users” eingestellt:

image

Lösung

Wie im Support Dokument beschrieben habe ich das Setting geändert auf die Option “First release for everyone”:

image

Kurze Zeit später (es zieht nicht sofort), konnte ich über das SharePoint Admin Center die gewünschte “Device Access” Konfiguration abschließen.

Ich habe in der Vergangenheit immer nach den Unterschieden der beiden “First Release” Einstellungen gesucht. Dieser Fall ist der erste mir bekannte.

Holger Schwichtenberg: Übersichtliche Konfiguration in Entity Framework Core mit IEntityTypeConfiguration<T>

Entity Framework Core 2.0 bietet nun die Schnittstelle IEntityTypeConfiguration, mit der man eine getrennte Konfigurationsklasse für einen einzelnen Entitätstyp implementieren kann.

Holger Schwichtenberg: Infotag: Softwareentwickler-Update 2017/2018 für .NET- und Webentwickler

Eine ganztägige Infoveranstaltung bietet ein Potpourri aktueller Themen für Softwareentwickler im Umfeld von .NET- und Webanwendungen: .NET, .NET Core, C#, Visual Studio, TypeScript, Angular, TFS/VSTS, DevOps, Docker und Cloud.

Stefan Henneken: IEC 61131-3: Parameter transfer via parameter list

Parameter lists are an interesting alternative for transferring parameters to PLC libraries. Strictly speaking, these are global constants (VAR_GLOBAL CONSTANT) whose initialization values can be edited in the Library Manager.

When declaring arrays, their boundaries must be defined as constants. At the time of compilation, it must be known how large the array should be. The attempt to define the array boundaries by a variable is rejected with an error message.

PROGRAM MAIN
VAR
  nUpperBound      : INT := 5;  
  aTest            : ARRAY [1..nUpperBound] OF INT;
END_VAR
 
// Compiler error: Border 'nUpperBound' of array is no constant value

Only the use of constants solves the problem.

PROGRAM MAIN
VAR
  aTest            : ARRAY [1..nUpperBound] OF INT;
END_VAR
VAR CONSTANT
  nUpperBound      : INT := 5;
END_VAR

This can lead to problems, especially in PLC libraries. For example, if you want to map a data buffer to an array, it is not necessarily known at the time of library development how large this data buffer has to be in the respective applications.

The solution here is a parameter list, which is inserted when creating a PLC library.

Pic01

A parameter list can be added at any point in the PLC project (the PLC project from which the PLC library originates). Parameter lists are usually created for the global variable lists (GVLs).

Pic02

The parameters are declared as constant global variables.

{attribute 'qualified_only'}
VAR_GLOBAL CONSTANT
  cUpperBound    : INT := 3;
  cString        : STRING := 'Hello';
  cIntArray      : ARRAY [1..cUpperBound] OF INT := [1, 2, 3];
  cStructArray   : ARRAY [1..3] OF ST_Test := [3( (nValue := 1,
                                                   fValue := 3.1,
                                                   wsValue := "abc") )];
  cEnum          : E_Test := E_Test.eValue02;
  cStruct        : ST_Test := (nValue := 1, fValue := 3.1, wsValue := "abc");
END_VAR

The variables can be pre-initialized by initialization values. Even more complex variable structures can be initialized with values.

These parameters can be edited in the Library Manager in the application in which the library was integrated. The Library Manager is opened by double-clicking on the respective library reference.

Pic03

The fields in the column Value (editable) can now be changed as required. These changeable constants can be used within the respective PLC library or in the application that references them.

FUNCTION F_AddArrayValues : INT
VAR_INPUT
  aTest      : REFERENCE TO ARRAY[1..Param.cUpperBound] OF INT; 
END_VAR
VAR
  nIndex     : INT;
END_VAR
 
FOR nIndex := 1 TO Param.cUpperBound DO
   F_AddArrayValues := F_AddArrayValues + aTest[nIndex];
END_FOR

The changed initialization values are stored in the respective PLC project file (*. plcproj). Thus, each application that references a library with a parameter list has its own values. The library itself remains unchanged.

The values set in this way also withstand a PLC reset, both a cold reset and resetting to the original values. The values are not reset until the library is removed and reinserted. This means that parameter lists can also be used to transfer general parameters.

All parameters in a parameter list are usually accessible via ADS. The fact that parameters always have to be declared with CONSTANT does not prevent them from being written via ADS.

If a parameter is used as an array boundary, changing these variables will have serious consequences. Especially if the value of the parameter is increased.

aTest   : ARRAY [1..MyLibrary.Param.cUpperBound] OF INT;

Even if the parameter is increased at runtime of the PLC program, the array retains its original size. If this parameter is also used in the loops, accesses outside the allowed array boundaries can occur.

FOR nIndex := 1 TO MyLibrary.Param.cUpperBound DO
  aTest[nIndex] := nValue;
END_FOR

To prevent this, the attribute tc_no_symbol can be used to prevent ADS symbol generation. Access via ADS using the symbol name is then no longer possible for this parameter.

VAR_GLOBAL CONSTANT
  {attribute 'tc_no_symbol'}
  cUpperBound             : INT := 5;
END_VAR

Manfred Steyer: angular-oauth2-oidc 2.1 released

Over the last weeks, I've worked on version 2.1 of my OpenId Connect (OIDC) certified library angular-oauth2-oidc which allows for implementing Authentication in Angular using external Identity Providers that support OAuth 2 or OIDC.

Here are the added features:

  • New Config API (the original one is still supported). This allows putting the whole configuration in an own file:

     // auth.config.ts
     
     import { AuthConfig } from 'angular-oauth2-oidc';
     
     export const authConfig: AuthConfig = {
     
       // Url of the Identity Provider
       issuer: 'https://steyer-identity-server.azurewebsites.net/identity',
     
       // URL of the SPA to redirect the user to after login
       redirectUri: window.location.origin + '/index.html',
     
       // The SPA's id. The SPA is registerd with this id at the auth-server
       clientId: 'spa-demo',
     
       // set the scope for the permissions the client should request
       // The first three are defined by OIDC. The 4th is a usecase-specific one
       scope: 'openid profile email voucher',
     }

    After defining the configuration object, you can pass it to the libraries configure method:

     import { OAuthService } from 'angular-oauth2-oidc';
     import { JwksValidationHandler } from 'angular-oauth2-oidc';
     import { authConfig } from './auth.config';
     import { Component } from '@angular/core';
     
     @Component({
         selector: 'flight-app',
         templateUrl: './app.component.html'
     })
     export class AppComponent {
     
         constructor(private oauthService: OAuthService) {
           this.configureWithNewConfigApi();
         }
     
         private configureWithNewConfigApi() {
           this.oauthService.configure(authConfig);
           this.oauthService.tokenValidationHandler = new JwksValidationHandler();
           this.oauthService.loadDiscoveryDocumentAndTryLogin();
         }
     }
  • New convenience methods in OAuthService to streamline default tasks:

    • setupAutomaticSilentRefresh()
    • loadDiscoveryDocumentAndTryLogin()
  • Single Sign out through Session Status Change Notification according to the OpenID Connect Session Management specs. This means, you can be notified when the user logs out using at the login provider:

    Single Sign out via Session Checks

    To use this feature, your Identity Provider needs to support it. You also have to activate it in the configuration:

     import { AuthConfig } from 'angular-oauth2-oidc';
     
     export const authConfig: AuthConfig = {
       [...]  
       sessionChecksEnabled: true,
       sessionCheckIntervall: 3 * 1000
     }

    The optional configuration option sessionCheckIntervall which defaults to 3000 msec defines the interval that is used to check whether the user has logged out at the identity provider.

  • Possibility to define the ValidationHandler, the Config as well as the OAuthStorage via DI

     [...],
     providers: [
         {provide: AuthConfig, useValue: authConfig },
         { provide: OAuthStorage, useClass: DemoStorage },
         { provide: ValidationHandler, useClass: JwksValidationHandler },
     ],
     [...]
  • Better structured documentation:

    Documentation

Uli Armbruster: Programmieraufgaben für Bewerber

Ich habe in diesem Post beispielhaft eine unserer Programmieraufgaben herausgezogen, die kürzlich unser neuer Mitarbeiter Hr. Jörg Weißbecker gelöst hat. In der Regel ist das Ziel ein möglichst hochwertiges Ergebnis zu programmieren, das z.B. die Prinzipien OCP und SoC solide umsetzt.

Zeitdruck ist an der Stelle völlig sekundär, sodass die Bewerber die Aufgaben immer von zuhause in aller Ruhe entwickeln können. Sie erhalten dann von mir nach jeder Iteration die nächste Anforderung übermittelt.

Am Ende besprechen wir das Ergebnis, sprich sie stellen ihren Gedankengang vor, reflektieren Probleme und stellen sich meinen Fragen.

 

Allgemeines vorab:

  • Es ist keine Oberfläche notwendig, es genügt eine Konsolenanwendung
  • Welche Bibliotheken genutzt werden, ist freigestellt
  • Das Schreiben von Tests ist optional
  • Aber: Setzte das objektorientierte Paradigma so gkonsequent als möglich um (!)

 

Iteration 1: Entwickeln Sie ein Programm, welches prüft, ob 2 Dateien identisch sind. Beispiel für den Aufruf:

Dublettenfinder.exe file1.txt file2.txt

 

Iteration 2: Statt 2 Dateien sollen jetzt 2 Ordnerpfade übergeben werden, für welche das Programm prüft, ob das Ordnerpaar Dubletten enthält. Wenn ja, soll es pro Dublette die Dateipfade gruppiert ausgeben. Beispiel:

Hash: xyxzasdfadsf

  • Datei1: c:\abc.pdf
  • Datei2: d:\efg.pdf

 

Iteration 3: Es soll möglich sein eine Liste von Verzeichnissen zu übergeben, sodass beliebig viele Verzeichnisse verglichen werden können. Außerdem sollen, sofern vorhanden, auch die Unterverzeichnisse geprüft werden.

 

Iteration 4: Fügen Sie bitte eine optionale Konfigurationsmöglichkeit hinzu, um nur bestimmte Dateitypen vergleichen zu können, sodass entweder alle Dateitypen, genau einen Dateityp oder n-Dateitypen verglichen werden können.

 

Iteration 5: Machen Sie den Algorithmus austauschbar, sodass der Identitätscheck z.B. anhand von Sha256 Hashwerten oder Bildanalyse durchgeführt werden kann.

 

Iteration 6: Nutzen Sie einen IoC Container, um dynamisch beim Start der Anwendung alle vorhandenen Algorithmen zu laden und dann vom User in der Konsole auswählen zu lassen. Jeder Algorithmus soll in einer eigenen Assembly liegen. Machen Sie dann eine Assembly für MD5 und Sha256.

 

Die  Lösung zu diesem Zeitpunkt müsste sich Fragen bzgl. der Erweiterung der folgenden 2 Änderungen stellen:

  • Lösche Dubletten anhand eines bestimmten Patterns, z.B. alle in Verzeichnis ‚xy‘ oder mit Dateinamen, die ‚yz‘ enthalten.
  • Ermögliche die Erkennung von doppelten Bildern anhand der Bilderkennung und nehme immer die beste Auflösung.

 

Kürzlich gab es in der dotnetpro einen Artikel von Stefan Lieser, welcher eine recht ähnliche Kata gelöst hat. Die Lösung von Herrn Weißbecker befindet sich hier.


Einsortiert unter:Development, German Tagged: Lernen, Patterns

Holger Schwichtenberg: Erste Schritte mit der PowerShell Core unter Ubuntu-Linux

Die plattformunabhängige PowerShell Core hat mittlerweile den Stand "Beta 6" erreicht und basiert auf der fertigen Version 2.0 von .NET Core. Dieser Beitrag zeigt die Installation auf Ubuntu und erste Schritte in der Anwendung.

Christian Dennig [MS]: Secure an Aurelia Single Page App with Azure Active Directory B2C / MSAL

If you create a modern web application with an API / REST backend and a Single Page Application (SPA) as your frontend, that you want to run in the internet, you definitely don’t want to handle security / user management on your own. You will want to use a service like Auth0 or Azure Active Directory to handle authentication and/or authorization.

In this example, we will develop a basic Aurelia frontend application, that will be secured via Microsoft Authentication Library (MSAL) for JavaScript backed by a custom Azure Active Directory (B2C). The B2C directory is the identity store where users of our application will be stored. The Aurelia app will login to the B2C directory via MSAL and use an ID token provided after a successful login to call the web API (authorization token in the request header).

23

Implicit Flow

Let’s start by creating the directory…

Create a B2C Directory

The creation of an Azure Active Directory is quite simple. Log in to your Azure account and select “New”. Search for “Azure Active Directory B2C” and select “Create” –> “Create a new Azure AD B2C Tenant”. Enter your desired organization and domain name and select the country/region you want the directory to be located. In my example, the configuration looks like that:

2

After the tenant is created, we have to add an application that can access the directory. Therefore, go to the newly created B2C directory and select “Applications” in the settings blade. Click “Add” and enter a name for the application. Also, be sure to select “Include web app / web API”, because we want to use the “implicit flow” as shown in the intro section above. In the reply URL enter “http://localhost:9000“, because this is the URL our Aurelia app will be running on. After a successful login, Azure AD will redirect the browser to that given URL.

4

B2C Application Settings

Note down the Application ID and the domain name (https://{yourtenantname}.onmicrosoft.com – in my case https://aureliab2c.onmicrosoft.com), as we will need the values when we configure the authentication of our Web API.

Next, to give users the ability to register for the application, you have to create a policy within the directory that allows users to create an account. You also need a policy to sign in and – if you want to – a policy to reset the password for a user.

In this example, we will only use “local accounts” (login with username and password). You can also add further identity providers like Google or Facebook to add social login capabilities to your application. To keep it simple, we only support local accounts at the moment.

Fortunately, we can create a policy that allows users to add an account and configure the login process in one single configuration. It’s called “Sign-up or sign-in policy”.

Click on the corresponding entry in the configuration blade and add a new one.

Next, you have to enter information about the identity provider to use (in our case “Local Account”, the signup attributes (fields a user can enter during signup like “Given Name” and “Surname”) and application claims (information that will be sent with the identity token after login). In our application, the configuration is as follows:

5

Identity Provider

6

Signup Attributes

7

Application Claims

Of course, you can adjust a lot more settings, but for our case, this is enough in the first place.

After you have created the policy, you can test it by clicking on the “Run Now” button and create a user for yourself.

19

Login Page / B2C

20

Signup Page

As a last step, note down the name of the policy you have just created – in our case: B2C_1_signupin. We need it later on…

Our directory is now ready to go, so let’s head over to the API…

Create REST API

The REST API we want to develop is based on ASP.NET Core 2.0 and will be secured by the Azure AD we just created. To achieve this in a very convenient way, you can enter the connection to the Azure AD during project setup.

In Visual Studio, open “File –> New — Project” and select “ASP.NET Core Web Application” and in the following wizard, select “Web API”.

Create Web API

Create Web API

To enter the B2C data you noted down in the last steps, click on the “Change Authentication” button, select “Individual User Accounts” and enter the following details:

  • the domain name of your B2C tenant
  • the application ID that corresponds to the API
  • the policy to use
11

Auth Configuration

The wizard adds settings to you appsettings.json file and configures you startup class to use the B2C information you just entered.

While we want to focus on the authentication process and securing the backend, the API will only provide one controller that can be called on the path “people“. The GET request on that path will return a list of characters (from StarWars 😉 – well, just sample data). As the controller is annotated with the Authorize attribute, requests will only be successful, if the caller adds the Authorization header with a valid ID / Bearer token. The token will be provided by the AAD B2C directory after a successful login. More one that later when we implement the Aurelia app.

So, as I said…if you want to call the people controller without a valid token, you will receive a 401 status code (Unauthorized) – the way we want it. To test that behavior, we can use Postman.

13

401 Unauthorized / Call API without a Bearer Token

The backend is now secured by the Azure B2C directory…so let’s add the frontend.

Create the Aurelia SPA

Before we can start developing the frontend application with Aurelia, we have to install the command line interface (CLI). To do this, open a command prompt an enter the following command:

npm install -g aurelia-cli@latest

This command will install the Aurelia CLI globally on your machine. After the command returns, we can create a new Aurelia application via

au new frontend

The command (btw. frontend is the name of our application) will start a wizard where you can select different options, e.g. if you want to use plain JavaScript or TypeScript.

15

Start the wizard

16

Select Options

We will use TypeScript for our application.

After you have entered all the necessary information, you can finish the wizard and install the dependencies (you will be ask by the wizard) afterwards.

The result will be an Aurelia application with minimal dependencies, built with TypeScript. You can test the application by running…

au run --watch

and opening the browser, pointing to http://localhost:9000.

To be able to call the API, request an ID token at the B2C directory and display the results from the people controller, we need a few more dependencies.

First, we need the current version of the MSAL JS library, which is – at the time of writing – still in preview for JavaScript. You can install it via NPM, but the current version available (0.11) has a few bugs. Therefore, we will take the current dev version directly from the GitHub repository 🙂

You can download the version from https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/out/msal.js and save it in a “lib” folder in the root directory of the Aurelia app.

Next, we need the Aurelia fetch library and jwt_decode (to decode the ID token we receive after login from the B2C directory). Install both dependencies via

npm install aurelia-fetch-client@latest jwt_decode@latest --save

Because we are “compiling” the application via the Aurelia CLI, we also need to add these additional libraries to the aurelia.json file located in the “aurelia_project” folder. Insert entries for the MSAL lib (line 4), aurelia-fetch-client (line 12) and jwt_decode (starting line 47) in the dependencies section of the JSON file.

Now we are ready to implement the application. As we want to focus on the authentication process, I will concentrate on the pieces that are important for our case.

  1. Create a settings file, where we can access the B2C settings during runtime.
  2. Add a class (in auth.ts file) that is responsible for handling the authentication process
    1. Check, if user is already authenticated
    2. Get the current ID / JWT token
    3. Login via B2C directory
    4. Logout
  3. Add a class (HttpConfig) that will configure the Aurelia HttpClient class to intercept each request and insert the Authentication header (Bearer token)

The Auth class is the “main actor” in our frontend example. It uses the MSAL class UserAgentApplication to login & logout…as well as getting the ID token after a successful login attempt.

As we want to secure the complete frontend application and not only a few path/routes, we use the Auth class in the bootstrapping process of the Aurelia app – in the main.ts file.

As you can see, after loading the Aurelia framework, we check if the user is already logged in (by calling isAuthenticated on the auth object). If the promise is rejected (user is not logged in), we redirect to the AAD login page by invoking auth.login. After entering the username and password and submitting the form, we will be redirected with an ID token as URL parameter (id_token) to our app. The MSAL library picks up the id_token and handles expiration checking and storing all the necessary data for us. If everything is ok, the next call to isAuthenticated will successfully resolve and the app loads the main class App (in app.ts). While loading the view and view-model, a call to the web API is invoked (in method activate, line 4 – one of the lifecycle methods of an Aurelia view. activate is called before the view is visible/attached to the DOM).

In the .NET Core application, the request headers are checked for a valid Bearer token. If the token provided is valid, the call is routed to the people controller and the results are sent to the Aurelia app, which in turn displays the list of…StarWars characters 🙂

To see the contents of the ID token in the frontend, we decode the token in the App class (see above) and display the results (given name, surname, email and token expiration date) in a message box.

22

Final Application

If the Logout button in the top menu is clicked, auth.logout is called, the MSAL library clears stored data about the ID token and endpoints and redirects to http://localhost:9000/#.

Wrap Up

As you have seen, it is possible to secure a REST API and a corresponding Single Page Application – in our case an Aurelia app – with Azure Active Direct B2C. Of course, the app is far away from “production-ready”, but it is a good example to get an impression of how you can integrate an OpenID Connect flow in your own application and secure a SPA in a very simple way.

You can find the complete sample on GitHub: https://github.com/cdennig/azure-b2c-aurelia-example

Have fun with it…

Cheers!


Uli Armbruster: Angular Starthilfe – 2-tägiger Workshop in Leipzig

Zur Unterstützung des Developer Open Space 2017 bieten wir allen Teilnehmern, die uns eine Kopie ihres Tickets an schulung@co-IT.eu zukommen lassen, die Teilnahme an unserem 2-tägigen Angular 4 Workshop zum reduzierten Preis von 1250€ an.

Das Training ist vom 11-12. Oktober in Leipzig und damit direkt vorm #devspace selbst, sodass eine separate Anfahrt nicht notwendig ist. In unserem Firmenprofil könnt ihr euch vorab über uns informieren.

Angular_full_color_logo.svg

Für alle diejenigen, die meinen Blog lesen und nicht zur Veranstaltung gehen können, habe ich ebenfalls eine spezielle Aktion: Wer mir als erstes an die oben genannte E-Mail-Adresse eine Nachricht mit dem Titel „ich blogge“ schreibt und darin seinen Blog verlinkt, der erhält ebenfalls einen Rabattcode.

Die Anmeldung ist ab sofort auf Eventbrite freigeschaltet.


Einsortiert unter:Development, Events, German, Web Tagged: Workshop

Uli Armbruster: Einfachere Reisekostenabrechnungen für alle

Optimierung unserer Prozesse ist bei uns schon fast eine Leidenschaft, daher evaluieren wir regelmäßig neue Tools. Eines davon ist Einfach Reisekosten.

Unsere Anforderungen waren klar umrissen:

  • Skalierung auch mit 30 Mitarbeitern möglich
  • Reduzierung der benötigten Zeit pro Consultant auf 30 Minuten pro Monat
  • Reduzierung der Durchlaufzeit im Rechnungswesen
  • Auswertungen zur Kalkulation unserer Preise möglich
  • Einheiltung der immer aktuellen gesetzlichen Regelungen
  • Nice-to-Have: Integration in Debitoor (*)

*) Zu unserer Buchhaltungssoftware Debitoor kann ich gerne einen Artikel verfassen, wenn Interesse besteht. Schreibt mir einfach einen Kommentar.

Nach mehreren Tests, unter anderem dank des schnellen und freundlichen Supports, welcher uns einen Demo Team Account zur Verfügung gestellt hat, haben wir jetzt das Abo gebucht. Besonders gefällt uns, dass Entfernungen anhand von Google Maps automatisch berechnet werden und längere Projekte, in denen immer die gleichen Fahrten anfallen, mit wenigen Schritten erfasst werden können. Das Synchronisieren in Debitoor und die korrekte Zuordnung gemäß DATEV-Kostenrahmen funktioniert problemlos. Selbst so seltene Vorgänge wie das Erfassen von W-LAN Kosten, SIM-Karten, Vignetten, Fähren u.v.m. lassen sich damit abbilden. Durch die genaue Zeiterfassung werden die jährlich aktualisierte Pauschalen automatisch erfasst. Eine Reduktion durch Eingabe der gestellten Verpflegung z.B. von Frühstück im Hotel rundet das Ganze ab. Die Administration der Mitarbeiter scheint auf den ersten Blick auch keine Wünsche offen zu lassen.

Schwächen sehen wir aktuell noch im Zusammenspiel mit Debitoor. So findet kein Abruf des Kundenstamms statt, um den Kunden bzw. das Projekt in der Abrechnung per Auswahl erfassen zu können. Die PDF mit der Abrechnung würden wir darüber hinaus gerne customizen. Preislich wäre ein Rabatt mit steigender Mitarbeiterzahl wünschenswert.

Gerne ziehe ich Vorschläge vom Leser in Betracht. Postet diese in die Kommentare.


Einsortiert unter:CIO Topics, German, Misc Tagged: Produktbewertungen

Kazim Bahar: Tutorial: Twitter Sentiment Analyse per WPF-Client

In diesem sehr interessanten Tutorial von Microsoft wird gezeigt, wie man mit Hilfe der Twitter...

Uli Armbruster: ebuero leider nicht für alle geeignet

Kürzlich haben wir für uns den Dienst von ebuero evaluiert. Danke an dieser Stelle an Hans-Peter Schelian und Thomas Bandt für die Empfehlung.

Das Ziel war es, dass unsere Kunden 24/7 365 Tage im Jahr immer jemanden erreichen können – wohlgemerkt einen Menschen! Das muss nicht zwingend jemand sein, der sofort weiterhelfen kann, aber es sollte damit gewährleistet sein, dass der Anrufer darauf vertrauen konnte, dass sich um sein Anliegen asap gekümmert wird.

Mir hat besonders gefallen, dass unser Kunden immer Telefonistinnen aus der gleichen „Sekretärinnen-Gruppe“ bekommen hätten, sprich 3-4 Mitarbeiter von ebuero wären immer für unsere Anrufer zuständig gewesen. Dass diese bereits alle fließend Englisch sprechen, war ein weiterer  Pluspunkt. Gegen einen Aufpreis wären Muttersprachler möglich. Die Preise waren für mein Empfinden durchaus akzeptabel.

Leider konnten einige unsere must-have Anforderungen nicht erfüllt werden:

  • Freigabe von Nummernblöcken in der VIP-Liste: Leider müssen Nummern einzeln eingetragen werden. Bereiche der Form +49-721-1234-XXX, wie sie für größere Kunden notwendig sind, werden nicht unterstützt. Speziell hierfür fehlt mir das Verständnis, wieso das nicht möglich ist. Während des Schreibens fällt mir auf, dass es interessant zu wissen wäre, ob CSV-Dateien hochgeladen werden können. So ließe sich diese Einschränkung vielleicht umgehen.
  • Benutzerbezogene Benachrichtigungen sind nicht möglich, d.h. die Notifikation über einen Anruf geht an genau eine anzugebende E-Mail-Adresse. Eine Zuordnung zu der Person, die vom Kunden versucht wurde anzurufen, gibt es nicht. Eine Anpassung der E-Mail, um per Regeln in unserem Exchange die automatische Weiterleitung zu gewährleisten, ist ebenfalls nicht möglich.
  • Das direkte, automatisierte Weiterleiten der VIP Anrufer ist nicht möglich, sprich in jedem Fall landet der Kunde erst im virtuellen Sekreteriat.
  • Das Kalender-Sharing Feature, um der Sekretärin Zugriff auf die Termine von uns zu geben, um dem Kunden zu erwartende Rückrufzeiten zu nennen, unterliegt Einschränkungen, die ich hier nicht weiter aufzählen will.

 

Wenn ein Leser uns einen alternativen Dienst empfehlen kann, wäre ich dankbar. Gerne dürfen auch die Nutzer von ebuero hier Verbesserungsvorschläge in die Kommentare posten, da ich den Artikel meinem Ansprechpartner in der Hoffnung zusenden werde, dass diese Punkte noch verbessert werden.


Einsortiert unter:CIO Topics, German, Misc Tagged: Produktbewertungen

Manfred Steyer: Clever white space handling for better performance in Angular 5+ and 4.4

This post is part of my Angular Performance Tuning article series. If you looking for ways to make your Angular application faster, you might find the other articles in this series useful too.

Update: This optimization technique has now been merged into Version 4.4.0-rc.0 too.

Although tuning an application's performance can be difficult, sometimes all we need to do is laying back and waiting for the next version of the used framework. This especially holds true for Angular as the Core Team is working constantly on improving things under the hoods. A good example for this is the conciser code the Angular Compiler emits beginning with Version 4 or the Angular Bundle Optimizer that transforms code to make it more treeshakable.

Another of these optimization techniques landed with 5.0.0-beta.4 about two weeks ago. It allows the compiler to remove unneeded (consecutive) white spaces from text nodes and to remove even whole text nodes that only contain white space characters. This leads to less code emitted by the AOT compiler and therefore to smaller bundle sizes as well as faster start up times.

In this post I'm describing how to use it, which performance gains I measured when applying to an example application as well as how this approach works under the covers. The example application I've used for this can be found in my GitHub repository.

Removing white spaces

While removing white spaces from HTML is most of the time a safe operation (at least when respecting some rules outlined below), it can also destroy your layout. Because of this, you have to opt-in for it. For instance, you could to this on component level by setting the new property preserveWhitespaces to false:

@Component({
  selector: 'app-passenger-search',
  templateUrl: './passenger-search.component.html',
  styleUrls: ['./passenger-search.component.css'],
  preserveWhitespaces: false
})
export class PassengerSearchComponent  {
}

Instead of this, you can also set this property on application level by passing the new preserveWhitespaces command line option when calling the Angular Compiler ngc. At the time of writing, the Angular CLI did not support this option but this seems to be just a matter of time.

In addition to this, you can protect a section within a template by adding the attribute ngPreserveWhitespaces to a tag. If you want to spot a space that isn't allowed to be removed, you can use the pseudo-entity &ngsp;, which is converted to a space in the emitted code. Don't confuse this with the well known entity.

Results for example application

When I applied this optimization technique to my example application, I got the following results:

  • ~ 8% reduction in size for the javascript bundle
  • ~ 4% reduction in size for the gzipped javascript bundle
  • ~ 9% faster application startup time

As this shows, this approach allows for an appreciable performance gain. This gain we get after "just" removing white spaces is higher than you might have expected. The next section shows how this is possible.

Under the hoods

To understand why such a high performance gain is possible by "just" removing white spaces, let's look at a simple template:

<h1>
  Search for Passengers 
</h1>
<p>
  Lorem ipsum dolor sit amet.
</p>

When we compile this template, the AOT compiler transforms it to JavaScript code:

function View_PassengerSearchComponent_0(_l) {
  return __WEBPACK_IMPORTED_MODULE_1__angular_core__["_37" /* ɵvid */](
      0,
      [
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_15" /* ɵeld */](
                     0, null, null, 1, 'h1', [], null, null, null, null, null)),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_35" /* ɵted */](
                     null, [ '\n  Search for Passenger \n' ])),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_35" /* ɵted */](
                     null, [ '\n' ])),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_15" /* ɵeld */](
                     0, null, null, 1, 'p', [], null, null, null, null, null)),
        (_l()(), __WEBPACK_IMPORTED_MODULE_1__angular_core__["_35" /* ɵted */](
                     null, [ '\n  Lorem ipsum dolor sit amet.\n' ]))
      ],
      null, null);
}

As you can see, this code contains a function call for each dom node we need. For instance, there is a function call for the h1 tag as well one for the p tag. In addition to that, there are function calls for text-nodes. When analyzing this, we find characters as well as even a text node that aren't displayed in the browser due to the way HTML works. Examples are consecutive white spaces as well as empty text nodes like the one between the closing h1 tag and the opening p tag.

When using the discussed technique, the compiler removes these whites spaces or these text nodes. The following image shows a diff between the emitted code above and the code that is created after activating white-space-removal:

diff between version with and w/o whitespaces

This shows that a whole function call as well as some consecutive white space characters are removed. When we just count the removed lines for the sake of simplicity we see they make up about 10 % of all lines. This explains the reduction in bundle size we've faced. Of course, a reduction in function calls means that the Browser can download the bundles faster as well as Angular has less to do when starting the application. Both leads to the improvement of start up performance mentioned above.

Holger Schwichtenberg: Roadmap für Entity Framework Core 2.1

Zwei Wochen nach dem Erscheinen von Entity Framework Core 2.0 mit doch eher enttäuschenden Neuerungen hat Microsoft nun eine Roadmap für die Version 2.1 veröffentlicht und dabei versprochen, zumindest einige der zuvor als "kritisch" und "High Priority" eingestuften Funktionen endlich zu realisieren.

Code-Inside Blog: IdentityServer3 with WindowsAuthentication with ASP.NET WebApi & ASP.NET & WPF App

Please note: In my sample and in this blogpost I cover IdentityServer 3, because last year when I was working on the sample and our real implementation IdentityServer4 (a rewrite of IdentityServer 3) was in beta. My guess is, that most stuff should still apply even if you are using IdentityServer4, but I didn’t test it.

Also: I’m not a security expert - this might be all wrong, but currently this more or less works for us. If you find something strange, please let me know!

Overview

The sample consists of the following projects:

IdentityTest.IdServerHost: That’s the central IdentityServer in our solution. It contains all “clients” & “identityprovider” settings. IdentityTest.WinAuth: This is our Windows-Authentication provider. Because of the nature of WindowsAuth it needs to be an extra project. This needs to be hosted via IIS (or IIS Express) with Windows authentication enabled. The ASP.NET app acts as a bridge and will convert the Windows-Auth ticket into a SAML token, which can be integrated into the IdentityServer. It is more or less like a mini-ADFS. IdentityTest.WebApp: The WebApp itself can be used via browser and also hosts a WebApi. The WebApi is secured by the IdentityServer and secured pages will trigger the authentication against the IdServerHost. IdentityTest.WpfClient: With the WPFApp we want to get a AccessToken via a WebBrowser-Control from the IdServerHost and call the WebApi that is hosted and secured by the very same IdServerHost.

The IdentityServer team did a great job and have a large sample repository on GitHub.

x

I will talk about each part in my sample. Now lets beginn with…

The ‘IdServerHost’ Project

The IdentityServerHost is a plain ASP.NET application. To include the IdentityServer3 you need to add IdentityServer3 NuGet-Package.

The code is more or less identical with the Minimal-Sample from the IdentityServer3 team, but I disabled the SSL requirements for my demo.

Be aware: The IdentityServer use a certificate to sign the tokens, but this has nothing to do with the SSL certificate. This was a hard learning curve for me and IISExpress or something messed things up. In the end I disabled the SSL requirements for my development enviroment and could start to understand how each part is communicating with each other. The signing certificate in the sample is the sample .pfx file from the offical samples.

Remember: DO USE SSL IN PRODUCTION. Oh - and use the Cert-Store for the signing certificate as well!

Cert-Handling in IdentityServer in a nutshell: Do use SSL in production with a valid SSL certificate and setup another certificate that the IdentityServer will use to sign the tokens.

Besides the SSL stuff the most important stuff might be the client-registration and the identity-provider-registration.

The IdentityServer - as the auth-central - knows each ‘client’ and each ‘identity-provider’. Make sure all URLs are correct otherwise you will end up with errors. Even a slightly difference like ‘http://test.com/’ and ‘http://test.com’ (without the trailing slash at the end) will result in strange errors.

The ‘WinAuth’ Project

As already written this is our Windows-Authentication provider. Of course, it is only needed if you need WinAuth. If you want to use any other provider, like a Google/Microsoft/Facebook/Twitter-Login, then this is not needed. It is a bridge to the enterprise world and works quite well.

In the project I just reference the IdentityServer.WindowsAuthentication NuGet-Package and I’m nearly done. In the config I need to insert the URL of my IdentityServer host - those two parts needs to know each other and they will exchange public keys so they can trust each other.

For this trust-relationship the WinAuth provider has its own certificate. Actually you can reuse the same cert from the IdentityServerHost but I’m not sure if this is super secure, but it works.

The code and sample can also be found on the offical GitHub repo

The ‘WebApp’ Project

This project is a regular ASP.NET MVC project with WebApi 2 included. Nothing ASP.NET Core related, but the actual doing would be pretty similar.

On this page there are two ways to interact:

  • Via Browser
  • Via the WebApi

Browser Auth via OpenIdConnect Auth:

The NuGet Package Microsoft.Owin.Security.OpenIdConnect does the heavy lifting for us. In combination with the Microsoft.Owin.Security.Cookies NuGet package the authentication will kick in when someone access a [Authorize] marked Controller. The Cookie-Auth will preserve the identity information.

WebApi Auth:

To use the protected WebApi with any HTTP client the request must have a JWT bearer token. The implementation is super simple with this NuGet package IdentityServer3.AccessTokenValidation.

Setup of both auth options:

The setup is quite easy with the NuGet packages:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = ConfigurationManager.AppSettings["Security.Authority"],
            RequiredScopes = new[] { "openid" }
        });

        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationType = "cookies",
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
        {
            AuthenticationType = "oidc",
            SignInAsAuthenticationType = "cookies",
            Authority = ConfigurationManager.AppSettings["Security.Authority"],
            ClientId = "webapp",
            RedirectUri = ConfigurationManager.AppSettings["Security.RedirectUri"],
            ResponseType = "id_token",
            Scope = "openid all_claims"
        });
    }
}

It is important to use the correct “clientIds” and URLs as configured in the IdentityServer, otherwise you will receive errors from the IdentityServer.

The ‘WpfClient’ Project

This project is a small version of the original WpfOidcClientPop sample. The idea behind this sample is that a user can sign in with his regular account.

Auth via browser:

Instead of a Loginname/Password form rendered from the WPF app itself the authentication is delegated to a embedded browser control. Another option is to delegate it to the “real” installed browser, but this is another topic. The Microsoft Account login in Visual Studio is made that way or think of any popular “Facebook-Login” mobile app on your phone: The auth-process is basically a typical Web signin.

This scenario is also convered as a offical OpenID Connect specification. In WPF your best and easiest choice would be the IdentityModel.OidcClient2 package.

Auth “Steps”

The first step in the sample project is to aquire a access token from the IdentityServer. The actual implementation is thanks to the OidcClient quite simple as you can see here.

The OidcClient will try to get the needed accesstoken in the silent mode first (this can be configured) and if this fails a embedded browser will be rendered and the user needs to sign in there. After a successful signin you will get a accesstoken and refreshtoken.

Sample note: If you try this on your local machine the auth-window should not appear, because it will just do a “silent” windows auth login.

Multiple IdentityProvider: If you configure multiple identity provider, a simple designed identity selection will appear in the embedded browser window.

After the intial sign in you can regenerate new accesstokens via the refreshtoken.

With the accesstoken we craft a HTTP request to our beloved WebApi and write use the token in the authorization header and finally we are done.

Things to consider:

It is important to setup the OIDCClient the correct way with the values that you specified in your IdentityServer configuration. Also you should read about OpenID scopes because they are linked to the actual result. Without the correct scope you might not get a accesstoken or refreshtoken.

x

Summary

With these 4 projects we have a quite mighty solution created. We can still use Windows Auth for enterprise needs, we can protect WebApis and WebPages via a central Identity solution and also use “native” apps. The IdentityServer itself has a wide range of configuration possibilities.

If you start doing something in this direction I would point you to the IdentityServer4, because new is always better, right?

GitHub Link

The full sample can be found on GitHub.

Hope this helps.

Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.