Mehr von Jürgen Gutsch

Mehr von Jürgen Gutsch

Empfehlungen von Jürgen Gutsch

Blog-Empfehlungen von Jürgen Gutsch

Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Jürgen Gutsch

ASP.NET und mehr...
View Components in ASP.NET 5

Eine der schönsten Neuerungen in ASP.NET 5 sind die View Commponents. Diese sind eine Art Mini MVC innerhalb der MVC Applikation die sich allerdings einbinden lassen wie PartialViews. Also Partielle Views mit eigenem Controller. In etwa Vergleichbar mit User Controls in ASP.NET Webforms. Anwendungsfälle wären wiederkehrende Elemente einer Website oder eins Blogs, die nichts direkt mit dem eigentlichen Inhalt zu tun haben. Also alles was nicht unbedingt über die aktuelle Controller Action kommen muss. Nehmen wir als Beispiel das Blog, das außer die Artikelliste und den Artikel noch mehr enthält, wie z. B: eine Navigation, Tag-Liste, Linkliste, Archivübersicht, etc. Das alles kann man über die aktuelle Controller Action ermitteln und an die View geben. Allerdings muss das in jeder View immer wieder gemacht werden.

Schöner wäre es, wenn sich die aktuellen Actions nur mit dem befassen was ihre primäre Aufgabe ist: Das ermitteln und zurückgeben eines Artikels, oder das ermitteln und zurückgeben einer Artikelliste. Alle anderen genannten Teile des Blogs könnten doch irgendwo anders passieren, wo es die Actions nicht mit Logik zumüllt für die die Actions nicht zuständig sein sollten.

Genau hier kommen die View Components ins Spiel:

Zuerst wird eine Klasse erstellt, die von ViewComponent ableitet und ViewComponent als Sufix haben sollte, z. B. TopTagsViewComponent:

public class TopTagsViewComponent : ViewComponent
{
    private readonly ITagService _tagService;

    public TopTagsViewComponent(ITagService tagService)
    {
        _tagService = tagService;
    }

    public IViewComponentResult Invoke()
    {
         var tags = _tagService.LoadTopTags();
         var models = tags.Select(=>
            new TagViewModel
            {
                Id = tag.Id,
                Name = tag.Name
            });
        return View(models);
    }
}

Die Methode Invoke entspricht der Action in einem regulären Controller, an deren Ende eine View erzeugt und zurückgegeben wird. Der TagService der hier genutzt wird kann per IoC in die ViewComponent hineingegeben werden. Da IoC wirklich überall zur Verfügung steht, kann auf jede Quelle zugegriffen werden, auf die eine regulärer Controller zugreift.

Die View ist eine ganz normale Razor View:

@model IEnumerable<WebApp1.ViewComponents.TagViewModel>

@if (Model.Any())
{
    <ul>
        @foreach (var tag in Tags)
        {
            <li>
                [@tag.Id] @tag.Name
            </li>
        }
    </ul>
}

Einzig der Ort an dem diese View abgelegt werden muss ist ein wenig speziell. Die Views werden unter Shared in einem Ordner Components abgelegt. Dort wiederum in einem Unterordner, der dem Namen der ViewComponent ohne dessen Sufix entspricht. Unsere ViewComponent View liegt also unter /Shared/Components/TopTags/Default.cshtml

Der Standardname ist Default.cshtml. Es kann aber auch jeder beliebige andere Name verwendet werden, wenn dieser in der Methode Invoke angegeben wird:

return View("TheNicerName", models);

Auf die Art könnte man auch innerhalb der ViewComponent daas Template wechseln, wenn es erforderlich sein sollte.

Die oben beschriebene Komponente wird dann ganz einfach, fast ähnlich wie eine PartialView eingebunden:

@Component.Invoke("TopTags");

Argumente übergeben

Interessant werden ViewComponents auch, wenn man Argumente übergibt. Angenommen wir möchten die Zahl der Tags anpassen, je nachdem, wo wir die Liste einsetzen wollen.

Wir erweitern die Methode Invoke einfach um ein Argument:

public IViewComponentResult Invoke(int count)     

    var tags = _tagService.LoadTopTags().Take(count);     
    var models = tags.Select(tag =>
        new TagViewModel 
        { 
            Id = tag.Id, 
            Name = tag.Name 
        }); 
     return View(models); 
}

Und rufen die Methode Invoke auch einfach mit diesem zusätzlichen Argument auf:

@Component.Invoke("TopTags", 10);

Asynchrone ViewComponents

Um Asynchrone Views besser zu unterstützen, kann statt der synchronen Methode Invoke auch eine asynchrone Methode geschrieben werden:

public async Task<IViewComponentResult> InvokeAsync(int count)     

    var tags = await _tagService.LoadTopTags();     
    var models = tags.Select(=>
        new TagViewModel 
        { 
            Id = tag.Id, 
            Name = tag.Name 
        }).Take(count); 
     return View(models); 
}

Bei der Nutzung muss dann await genutzt werden:

@await Component.InvokeAsync("TopTags", 10);

ASP.NET 5 RC1 wurde veröffentlicht

 

November 2015 war die schwammige Angabe für das RC1 und seit letzter Woche Mittwoch ist es soweit. RC1 ist draußen inklusive Go-Live Lizenz, was so viel heißt, dass diese Version produktiv genutzt werden kann und dieses auch von Microsoft supportet wird.

image

Mit ASP.NET 5 kommen auch das neue .NET Core 5 und das Entity Framework 7 als Release Candidate raus.

.NET Core und ASP.NET 5 bringt gegenüber der Beta 8 nicht viel neues. Es wurde eher im Hintergrund sehr viel optimiert und gefixt. Auch das Tooling im Visual Studio 2015 wurde verbesset. So gibt es jetzt unter anderem eine Package Manager UI für Bower, die ähnlich aufgebaut ist wie die UI von NuGet und der NuGet Package Manger UI werden Packages mit Scripts und ähnlichem, die mit Bower geholt werden sollten als inkompatibel markiert und mit dem Hinweis versehen, doch die Packages mit Bower zu laden.

Mehr Details zur aktuellen Version gibt es von Jeffrey T. Fritz unter http://blogs.msdn.com/b/webdev/archieve/2015/11/18/announcung-asp-net-5-release-candidate-1.aspx

Wie man auf die aktuelle Version aktualisiert, links zu Installern und weitere Anleitungen sind in der Dokumentation unter folgender Adresse zu finden: https://docs.asp.net/en/latest/getting-started/installing-on-windows.html

AppVeyor: Einfacher und intuitiver Build-Server für Open Source Projekte

Für LightCore 2.0 möchte ich öffentlich den Build-Status des Projektes im Repository anzeigen. Ich hätte dafür auch meine Jenkins-Installation auf Azure nutzen können, denn auch Jenkins zeigt über ein Plugin den aktuellen Build-Status in form einer kleinen Grafik an.

Allerdings war das jetzt mal der richtige Anlass um AppVeyor zu testen:

https://ci.appveyor.com/project/JuergenGutsch/lightcore

AppVeyor steht als SaaS (Software as a Service) über eine Web-UI zur Verfügung und bietet relativ viel Möglichkeiten über eine recht einfache, intuitive und aufgeräumte Oberfläche. Sehr leicht kann hier das GitHub Projekt eingebunden werden:

image

Eine kleine Herausforderung war hier eigentlich nur der Build eines .NET Core Projektes und die Ausführung der XUnit Tests innerhalb eines .NET Core Projektes. Aber auch das war schnell eingerichtet, da der .NET Version Manager (DNVM) bereits installiert ist. Man musste nun lediglich herausfinden welche DNX Version installiert ist, bzw. die richtige nachinstallieren. So nutzte ich die ersten paar Builds um über den Build-Output die vorhandenen Runtimes zu ermitteln. Am Ende lasse ich aber dann doch einfach gewünschte beta-8 installieren:

image

dnvm update-self
dnvm install 1.0.0-beta8 -a x86 -r coreclr -OS win 
dnvm alias lccoreclr 1.0.0-beta8 -a x86 -r coreclr -OS win 
dnvm install 1.0.0-beta8 -a x86 -r clr -OS win
dnvm alias lcclr 1.0.0-beta8 -a x86 -r clr -OS win

Bei Build und Test muss ich jeweils die Kommandozeile wählen und folgende Zeilen einfügen:

Build:

image

cd LightCore
dnvm use lccoreclr
dnu restore
dnu build

Test:

image

cd ..\LightCore.Tests
dnvm use lccoreclr
dnu restore
dnx test

Anzeige

Am Ende kann ich mir auf AppVeyor ein Stück MarkUp Code kopieren, dass ich in die Readme.md Datei im Repository einfügen kann, um den aktuellen Buid-Status des Projektes anzuzeigen:

image

[![Build status](https://ci.appveyor.com/api/projects/status/et1fpjlmnsrkw3mv?svg=true)](https://ci.appveyor.com/project/JuergenGutsch/lightcore)

Wie man sieht ist AppVeyor relativ einfach zu handhaben. Ich denke ich werde den SimpleObjectStore ebenfalls hierüber bauen lassen. Vorerst muss ich aber auch dieses Projekt erst nach .NET Core portieren. ;)

LightCore 2.0

Beim MVP Summit gab es am letzten Tag ein Hackathon mit dem Ziel ASP.NET 5 zu unterstützen. Das Hauptanliegen war sicher die Dokumentation, aber es ging auch darum Samples und Bibliotheken auf die neuesten Version von ASP.NET 5 und .NET Core zu heben.

Albert Weinert war es, der mich auf die Idee brachte LightCore auf .NET Core zu heben. Also habe ich ich den Tag drangesetzt die Bibliothek .NET Core kompatibel zu machen. Der größte Aufwand lag nicht darin, die eigentliche Bibliothek für .NET Core zu bauen, sondern alle Unit-Tests von NUnit nach XUnit zu portieren. Im gleichen Zuge habe ich die NUnit Asserts durch FluentAssertion ersetzt. Ein zukünftiger Wechsel des Test-Frameworks sollte dann kein Problem so großes Problem mehr sein.

Aktueller Stand

In der neuen Version habe ich in Bezug auf Silverlight und Compact Framework einen harten Schnitt gemacht. Diese Plattformen werden nicht mehr direkt unterstützt. Sollte die .NET Core Bibliothek kompatibel zu Silverlight und Compact Framework sein (was ich im Moment weder ausschließen noch bestätigen kann) so ist das eher Zufall.

Unterstützt werden ASP.NET 5, UWP, .NET Framework und Mono.

Abstriche muss LightCore in der LightCore.Configuration unter .NET Core machen, da der XAML-Reader nicht zur Verfügung steht. Diesen werde ich nur für das .NET Framework aktivieren. In allen anderen Fällen wird es eine JSON Konfiguration geben. Die JSON Konfiguration soll die bevorzugte Art der Konfiguration für ASP.NET 5 und UWP sein, abgesehen von der Konfiguration per Code natürlich.. 

Die alte Integration Web wird nicht angefasst, um Kompatibel zu MVC3 und -4 zu bleiben, verweist aber auf die .NET Core Version von LightCore. Zusätzlich wird es aber eine ASP.NET 5 Integration geben.

Der ThreadSingeltonLifecycle Manager ist aktuell für .NET Core deaktiviert, da System.Threading.Thread nicht mehr zur Verfügung steht. Hier muss eine andere Lösung gefunden werden, um die aktuellen Threads zu identifizieren. Dieser Livecycle wird vorläufig nur unter .NET Framework zur Verfügung stehen.

Im Falle von Reflection beim ermitteln von zu injizierenden Eigenschaften steht unter NET Core der BindingFlags.SetProperty nicht zur Verfügung, es werden hier also Performance-Einbußen möglich sein, wenn nun Eigenschaften ohne Setter ermittelt und geprüft werden müssen.

Eine weitere Baustelle ist der CommonServiceLocator, da die ServiceLocatorImplBase unter Microsoft.Practices.ServiceLocation noch nicht für .NET Core zur Verfügung steht. Eventuell macht eine eigene Implementation für .NET Core mehr Sinn. Der CommonServiceLocator bleibt für das .NET Framework bestehen.

Bei den Unit-Tests zur LightCore.Configuration gab es Probleme mit drei Tests die fehlschlagen, wenn sie nicht einzeln aufgerufen werden. Das liegt daran, dass XUnit die Tests parallel ausführt und der RegistrationLoader in LightCore nicht Thread-Save ist und auch nicht zwingend sein muss. Die verhauen sich also gegenseitig die Referenzen. Ein Lock um alle Tests die RegistrationLoader.Instance.Register() aufrufen behebt das Problem.

Ein öffentlicher Buildserver – mit AppVeyor - ist ebenfalls eingerichtet

Weitere Ideen

  • Die Idee Property-Injection ausschließlich über ein MarkerAttribut zuzulassen steht wieder im Raum. Das würde die Performance an dieser Stelle erhöhen.
  • Der Wegfall von benannten Instanzen wurde mehrfach kritisiert. Ich kann mir vorstellen, dass dieses Feature wieder Einzug findet. Natürlich ist das kein optimales Pattern, aber wie in allen Fällen, können schöne Patterns in der realen Welt nicht immer durchgezogen werden.

Contributions needed

Wie bereits geschrieben, müssen die Web Integrationen noch umgestellt werden, sowie alle Samples, Dokumentationen und die Performance-Tests.

Den aktuellen Stand könnt ihr auf GitHub direkt in folgendem Feature-Branch mitverfolgen: https://github.com/juergengutsch/lightcore/tree/jg/portable

Ich würde euch bitten Ideen, Anregungen und Probleme ebenfalls auf GitHub einzutragen.

Wer sich aktiv an der Entwicklung beteiligen möchte, ist herzlich eingeladen. Forkt auch den Stand, holt auch den Feature-Branch und schickt mir die Pull-Requests.

Windows 10 in der Praxis

Sodele… Nun sind es über 2 Monate, dass ich Windows 10 auf einem neuen Dell XPS 13 nutze.

Gegen die Hardware kann ich absolut nichts sagen. Das Gerät ist Top und vor allem klein und leicht, was es zu einem perfekten Arbeitsgerät macht, wenn man viel im Zug unterwegs ist wie ich.

Auch das Windows 10 ist im großen und ganzen in Ordnung, einzig ein Luxusproblem ist relativ störend – nachdem ich mich an das unnötige Startmenü gewöhnt habe, dass ich eigentlich seit WinXP nicht mehr benutzt habe. Bei dem Luxusproblem handelt es sich darum, dass Windows 10 mit der hohen Auflösung des XPS 13 nicht 100% klarkommt (Anwendersicht)

Das Gerät hat eine Auflösung von 3200 x 1800 Pixeln, was im Alltag bei dem kleinen Bildschirm keinen Sinn macht. Daher skaliert Win10 auch – fast - alles um 225%. Das ist im Prinzip auch eine sehr gute Idee, wäre es nicht “fast” alles, was skaliert wird.

Ausgerechnet das SQL Server “Management Studio” und der “Windows Live Writer” skalieren nur Teilweise. Diverse Icons und Dialoge bleiben klein. Auch Checkboxen und Radiobuttons werden nicht skaliert. Text dagegen schon. Im Live Writer wird die Breite des Arbeitsbereiches nicht skaliert, aber der Text, so das man eigentlich keinen Platz mehr zum schreiben hat.

Auch andersrum gibt es Probleme, also wenn die Skalierung auf den Hauptschirm stimmt, aber beim verschieben auf einen weiteren Bildschirm mit geringerer Auflösung die Applikation nicht mehr herunterskaliert wird. Das passiert z. B. bei “Skype for Business” oder bei PowerPoint. Bei letzterem sind es vor allem die Ribbons die dann fast ein viertel des Bildschirms ausmachen.

Ich könnte darüber nachdenken, externe Bildschirme mit höherer Auflösung anzuschaffen, allerdings wird immer alles in doppelter Ausführung benötigt, damit ich im Geschäft und im Home-Office gleich gut arbeiten kann. Einfacher war es allerdings Win10 generell mit einer geringeren Auflösung zu fahren. So habe ich die Skalierungsprobleme sowohl auf den Gerätebildschirm als auch mit externen Bildschirmen behoben.

Wahrscheinlich wäre es bessern, wenn Windows 10 die Skalierung übernimmt, statt es der laufenden Software zu überlassen. Eine Lösung könnte sein, der Software eine geringere Auflösung vorzumachen und dann das Bild zu skalieren. Sicher nicht ganz einfach, aber dann würde es die Probleme nicht geben.

Alles andere funktioniert problemlos. Visual Studio, Outlook, Word, etc. machen absolut keine Probleme.

Einfacher Dateienzugriff mit ASP.NET 5 Hosting Environment

Wer auf Dateien zugreifen möchte, sollte sich einfach nur das IHostingEnvironment in seinen Service holen. 

private readonly IHostingEnvironment _env;

public AuthMessageSender(IHostingEnvironment env)
{
    _env = env;
}

_env.WebRootFileProvider ermöglicht den schnellen Zugriff auf Dateien im Ordner wwwroot,  bzw. in dem Ordner der in der project.json beim “webroot” hinterlegt ist. Dieser FileProvider wiederum kann mit GetFileInfo() einer angegebenen IFileInfo liefern, die relativ im Ordner wwwroot gesucht wird. Die Methode GetDirectoryContents() ermöglicht einem den schnellen Zugriff per LINQ auf Inhalte eines Verzeichnisses. Mit der Methode Whatch() lassen sich Dateien beobachten, dabei lassen sich die zu beobachtenden Dateienüber einen Filter spezifizieren.

Das genannte IFileInfo ist eine reduzierte Version des bekannten System.IO.FileInfo, aber auch das bietet eine kleine Unterstützung: Die Methode CreateReadStream() öffnet direkt einen Strem der Datei im Lesemodus.

var document = new Document(name);

var fi = _env.WebRootFileProvider.GetFileInfo($"App_Data/{name}.md");
if (!fi.Exists)
{
    throw  new FileNotFoundException("Resource not found '{0}'!", $"App_Data/{name}.md");
}

var fs = fi.CreateReadStream();

await fs.CopyToAsync(document.Stream);

document.LastModified = fi.LastModified.DateTime;

return document;

Um relative Pfade in absolute Pfade zu mappen, kommt eine altbekannte Methode zum Einsatz, die hier als Erweiterungsmethode auf den IHostingEnvironment angelegt ist. Dabei handelt es sich um die MapPath Methode, die einigen noch aus dem Server Objekt im HttpContext.bekannt sein dürfte.

var absolutPath = _env.MapPath(“App_Data/content.md”);

Localization mit ASP.NET 5

Seit der Beta 8 wird Localization richtig unterstützt. Mit diesem Beitrag möchte ich Zeigen, wie man die Localization einsetzen kann.

Konfigurieren

Die Konfiguration findet wie üblich in der startup.cs statt. In der Methode Configure fügen wir eine neue Middleware ein:

app.UseRequestLocalization();

Diese Middleware setzt die Thread Culture für den aktuellen Request. Wie die Culture gesetzt wird, beschreiben die RequestLocalizationOptions die optional als Argument übergeben werden können. In den meisten Fällen werden die Options wohl nicht nötig sein. Wer aber die Steuerung der Cultures selbst übernehmen möchte kann das machen, indem die RequestLocalizationOptions angepasst werden:

Die Eigenschaft DefaultRequestCulture legt die Standard-RequestCulture. Die RequestCulture ist eine Kombination aus Culture und UICulture. Werden die Cultures nicht gesetzt, wird System.Globalization.CultureInfo.DefaultThreadCurrentCulture, bzw. System.Globalization.CultureInfo.DefaultThreadCurrentUICulture gesetzt

Die Eigenschaften SupportedCultures und SupportedUICultures definieren eine Liste mit unterstützten Cultures. SInd die Listen leer, wird die DefaultRequestCulture als einziger Eintrag gesetzt. Sind die Listen ‘null’, werden alle Cultures unterstützt.

Die interessanteste Eigenschaft ist die List der RequestCultureProviders. Die Provider ermitteln die aktuelle Culture. Das wird die Stelle sein, wo wohl am ehesten eingegriffen werden müsste. Standardmäßig sind drei Provider aktiv:

  • QueryStringRequestCultureProvider
    Ermittelt die Culture aus einem QueryString-Parameter
    Die standard Query-Variablen sind “culture” und “ui-culture”
    z. B: &culture=en-US&ui-culture=de-DE
  • CookieRequestCultureProvider
    Ermittelt die Culture aus einem Cookie
    Der Cookie-Name lautet: ASPNET_CULTURE und die Werte des Cookies sind mit der Pipe (|) getrennt, wie folgt angegeben “c=” für die Culture und “uic=” für die UICulture:
    z. B: c=en-US|uic=de-DE
  • AcceptLanguageHeaderRequestCultureProvider
    Ermittelt die Sprache aus den Browsereinstellungen die per AcceptLanguage-Header vom Browser mitgesendet werden.

An dieser Stelle kann theoretisch ein eigener RequestCultureProvider eingehängt werden. Z. B: einer der die Cultures aus einer Konfiguration oder aus der Datenbank holt. Dabei gewinnt der erste Provider der einen Wert liefert.

In der folgenden Reihenfolge werden nun die Cultures ermittelt:

  1. Vom Query String
  2. Vom Cookie
  3. Vom AcceptLanguage-Header
  4. Von der DefaultRequestCulture aus den Options
  5. Von der aktuellen Thread Culture

Services einbinden

Jetzt muss die Localization noch bekannt gemacht werden. Dafür müssen wir in der Methode ConfigureServices die nötigen Services registrieren und diesen mitteilen wo die Resource-Files gefunden werden können:

// Add Localization to the services container.
services.AddLocalization(x => x.ResourcesPath = "Resources");

// Add MVC services to the services container.
services.AddMvc()
    .AddViewLocalization(x => x.ResourcesPath = "Resources");

Die Angabe des ResourcePath in den Methoden AddLocalization() und AddViewLocalization() ist optional, allerdings tut das in der aktuellen Beta 8 noch nicht fehlerfrei.

Implementation 

Das war es dann auch schon. Wir können nun pro View und pro Controller eine Resource-Datei erstellen. Dabei gilt die Konvention, dass die Resource-Datei wie die View oder wie der Controller zu heißen hat.

In der View wird nun der IViewLocalizer injiziert, der die Resource anhand des ViewNamens ermittelt:

using Microsoft.AspNet.Mvc.Localization
@inject IViewLocalizer Localizer

<p>@Localizer["MyLocalizedKey"]</p>

Im Controller nutzen wir den generischen IHtmlLocalizer den wir ebenfalls per IoC erhalten

public class HomeController : Controller
{
    private readonly IHtmlLocalizer<HomeController> _localizer;

    public HomeController(IHtmlLocalizer<HomeController> localizer)
    {
        _localizer = localizer;
    }

    public IActionResult About()
    {
        ViewData["Message"] = _localizer["AboutMessage"];

        return View();
    }
}

Für die DataAnnotations geben wir wie gewohnt die Resource-Eigenschaften ErrorMessageResourceName und ErrorMessageResourceType in den jeweiligen Attributen an.

[Required(
        ErrorMessageResourceName = "RequiredName",
        ErrorMessageResourceType = "MyAwesomeResources")]
public string Name { get; set; }

Fazit

Im Prinzip funktioniert die Localization recht gut, allerdings nicht, wenn das Web mit dem Visual Studio über den IIS Express gestartet wird. Das ist ein bekannter Bug, der wohl das Visual Studio Tooling für ASP.NET 5 betrifft. Benutze ich dagegen Kestrel als Host, funktioniert die Localization wunderbar. 

Was man noch beachten sollte:
Die Resources für die Views müssen – anders als angekündigt – nicht heißen wie die View, sondern wie der komplette Pfad zur View. Wenn also eine View unter Views/Home/About.cshtml liegt, so muss die Resource-Datei die Namen Views.Home.About.cshtml.de-DE.resx (den entsprechenden anderen Culture-Name für die jeweilige andere Sprahe natürlich) Diese Namenskonvention scheint nicht wirklich intuitiv zu sein.

ASP.NET 5 Konfiguration

Wie inzwischen bekannt sein dürfte, ist im ASP.NET 5 die alte Web.Config verschwunden. Diese wird durch ein viel flexiblere Art der Konfiguration ersetzt. Ein Teil der Konfig ist in die project.json gewandert. Die eigentliche Konfiguration der Applikation über AppSettings oder auch für die ConnectionStrings, wandert im neuen Visual Studio Template in die appsettings.json.

Dabei fällt wieder mal auf, dass es sich hauptsächlich um JSON basierte Konfiguration handelt, die bei ASP.NET 5 zum Einsatz kommt. Standardmäßig werden aber mehr Arten der Konfiguration angeboten. Neben der erwähnten JSON-Konfiguration, gibt es die Möglichkeit INI-Dateien einzulesen, Kommandozeilen-Parameter und Umgebungsvariablen einzulesen, oder ganz einfach ein Dictionary (genauer: ein IEnumerable<KeyValuePair<String, String>>) im C# Code zu übergeben.

Da es nun mehrere Varianten gibt, muss auch die Konfiguration konfiguriert werden. ;)

Genau das passiert im Constructor der Startup.cs, in der schon die appsettings.json und der BasisPfad eingelesen werden:

var builder = new ConfigurationBuilder()
                 .SetBasePath(appEnv.ApplicationBasePath)
                 .AddJsonFile("appsettings.json");
// … 
Configuration = builder.Build(); 

Die Methoden AddIniFile(), AddCommandline() und AddInMemmoryCollection können hier analog zu AddJsonFile angehängt werden.

Ist alles konfiguriert, geht es nun darum an die konfigurierten Daten zu kommen.

Schauen wir uns mal eine kleine appsettings.json mit folgendem Inhalt an:

{
  "ApplicationConfiguration": {
    "SiteName": "Configuration Demo",
    "SiteColor""#0000FF",
    “ArticlesPerPage”: 10
  }
}

Wir sehen hier einen Knoten (ojeh, ich nutze immer noch die alten XML-Begriffe) mit der Bezeichnung “ApplicationConfiguration“, sowie die darunter legenden Elemente “SiteName”, “SiteColor” und “ArticlesPerPage”. Die Bezeichner sind hier völlig beliebig

Um an die Daten zu kommen, könnten wir nun auf das oben erstellte Objekt Configuration zugreifen und wie in einem KeyValue Liste auslesen:

var siteName = Configuration["ApplicationConfiguration:SiteName"];

Dabei bilden alle übergeordneten Bezeichner, bis zum gewünschten Element, den Pfad zum gewünschten Inhalt. Ähnlich wie sich mit XPath verhält. Der Komplette Pfad, mit Doppelpunkt als Trenner, wird als Key angegeben. Das Resultat ist ein String.

Damit das Applikationsweit funktioniert, müsste das Objekt Configuration als Service registriert werden:

services.AddInstance<IConfigurationRoot>(Configuration);   

Allerdings ist das keine sehr elegante Methode und die Registrierung der Configuration als Service wäre ganz sicher schon als Standard von Microsoft umgesetzt. Besser ist es die Konfiguration typisiert ein ein eigenes Konfig-Objekt einzulesen:

Der Bereich mit dem Namen “ApplicationConfiguration” wird als Configuration Section gesehen, wie wir es noch aus der Web.config kennen. Diese Sections können explizit ausgelesen werden und einem eigenen Objekt zugewiesen werden:

services.Configure<ApplicationConfiguration>(
    Configuration.GetSection("ApplicationConfiguration"));

Der Typ zum zugehörigen JSON:

public class ApplicationConfiguration
{
    public string SiteName { get; set; }
    public string SiteColor { get; set; }
    public int ArticlesPerPage { get; set; }
}

Dabei ist zu beachten, dass der Name der Klasse und auch die Namen der EIgenschaften, den Bezeichnern im JSON entsprechen

Diese ApplicationConfiguration ist dann sehr einfach – und bereits vorkonfiguriert – über das IoC überall zugreifbar. z. B: auch in den Views:

@using Microsoft.Framework.OptionsModel
@using WebApplication6.Configuration
@inject IOptions<ApplicationConfiguration> Config

<title>@Config.Options.SiteName</title>

Oder in jeder per IoC instanziierten Klasse im ASP.NET 5 Projekt:

public class HomeController : Controller
{
    private readonly IOptions<ApplicationConfiguration> _configuration;

    public HomeController(IOptions<ApplicationConfiguration> configuration)
    {
        _configuration = configuration;
    }

    public IActionResult Index()
    {
        var siteName = _configuration.Options.SiteName;

        return View();
    }
}

Oben haben wir gesehen, wie sowohl eine JSON Datei eingelesen wird, als auch Umgebungsvariablen. Es lassen sich auch weitere JSON Dateien mit einlesen. Diese können sowohl unterschiedliche Sections enthalten, also auch identische. Die zuletzt eingebundenen Konfigurationen überschreiben dabei die vorigen, wenn es identische Angaben gibt. Das kann z. B: genutzt werden, um eine Standardkonfiguration bei Bedarf zu überschrieben.

Unterschiedliche Konfiguration per Deployment-Umgebung

Nehmen wir einmal an, wir möchten unterschiedliche Konfigurationen für verschiedene Deployment-Umgebungen automatisiert hinterlegen, um z. B: ConnectionStrings, oder was auch immer, unterschiedliche zu Konfigurieren. Bisher musste man dafür entweder die Settings im Deployment-Prozess umschreiben, oder die Web.Config Tranformation nutzen. Mit ASP.NET 5 lässt sich das viel einfacher Lösen. Dabei können wir die vorhandene Umgebungsvariable ASPNET_ENV nutzen, die standardmäßig immer eingelesen wird. Diese steht unter IEnvironment.EnvironmentName mit den Werten “Development”, “Staging” und “Production” zur Verfügung. (Sie kann theoretisch auch andere Werte enthalten die vom ASP.NET 5 Intern halt nicht berücksichtigt werden.)

Möchten wir nun also eine Datei namens “appsettings.Development.json” einlesen sieht der Konstruktor der Startup-Klasse nun so aus:

public Startup(IHostingEnvironment env, IApplicationEnviroNun nment appEnv)
{
    // Setup configuration sources.
    var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
        .AddJsonFile("appsettings.json")
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    Configuration = builder.Build();
}

Per String-Interpolation geben wir den EnvironmentName in de Namen der angefragten JSON-Datei rein und können nun je nach Umgebung verschiedene Konfig-Dateien laden, die vom Deployment-Prozess in das Projektverzeichnis gelegt werden. Der zweite optionale Parameter “optional” gibt an, ob die Datei vorhandenen sein muss oder nicht. So könnte die Datei in der Produktiv-Umgebung weggelassen werden, um die Grundkonfiguration zu nutzen. Auf diese Art können wir also die Konfiguration für die jeweilige Deployment-Umgebung überschreiben oder ergänzen.

Ohne Umgebungsvariablen

Sollte es aus irgendeinem Grund nicht möglich sein, eine Umgebungsvariable in den Zielumgebungen zu setzen. So schafft dieser Blog Beitrag Abhilfe. Er erklärt hier wie man eine JSON-Konfig als Grundkonfiguration nutzen kann, um die Ziel-Umgebungen einzustellen, damit mit diesen Daten die eigentliche Konfiguration geladen werden kann.

XML Konfiguration

Oben habe ich mit keinem Wort die XML Konfiguration erwähnt. Die gibt es natürlich immer noch, allerdings muss sie separat per NuGet Package eingebunden werden:

"Microsoft.Framework.Configuration.Xml": "1.0.0-beta7"

Nun sollte analog zur JSON auch die Methode AddXmlFile() zur Verfügung stehen. Diese Funktioniert genauso wie die AddJsonFile() Methode.

Weitere Informationen

Strongly typed AppSettings Configuration in ASP.NET 5

Neu in ASP.NET 5 Beta 8

Letzte Woche habe ich in den .NET Usergroups Bern und Friedrichshafen noch die Beta 7 gezeigt und dann erscheint die Beta 8 mit einigen interessanten Änderungen und Neuerungen.

Kestrel überall

Es wurde angekündigt, dass Kestrel ASP.NET 5 Webs auch im IIS hosten soll. Nun ersetzt Kestrel auch im neuen Visual Studio Projekt den WebListener als Hosting umgebung. Das Kommando “web” verweist jetzt auf Kestrel. Der WebListener bleibt uns erhalten, ist aber nicht mehr standardmäßig eingebunden.

image

Im IIS sorgt der HttpPlattformHandler (ein natives Plugin für den IIS) jetzt dafür, dass die Anfragen direkt an Kestrel weitergegeben werden. Der HttpPlattformHandler muss auf den IIS installiert werden, wenn ASP.NET 5 Webs gehostet werden sollen. Für Entwickler ist das Plugin bereits im Beta8 Web Tools Update enthalten.

Für bestehende Webs und klassische ASP.NET Projekte muss nichts angepasst werden. Diese laufen wir gewohnt. Der HttpPlattformHandler wird über eine web.config configuriert die im Verzeichnis “wwwroot” liegt.

Konfiguration überall

Eine kleine Änderung, die aus Sicht von ASP.NET Entwicklern Sinn macht: Die “config.json” wurde umbenannt und heißt jetzt “appsettings.json”. Kein großes Ding, macht es aber klassischen ASP.NET Entwicklern möglicherweise etwas einfacher sich zu orientieren.

image

(Mehr zur Konfiguration in einem der nächsten Beiträge)

Localization überall

Neu wird auch Localization jetzt richtig unterstützt. Ein Thema dem ich mich in einem separaten Blog-Beitrag intensiver widmen werden. Eine neue Middleware sorgt dafür dass die richtige Culture für den aktuellen Request gesetzt wird. Weiterhin lassen sich Services registrieren, welche die Localization in der Applikation Verfügbar macht.

NuGet überall

Die Benutzung von NuGet wurde ebenfalls etwas überarbeitet. So wird nun ein Feedback angezeigt, wenn die Packages restored werden und die Icons wurde redesigned. Auch die Anzeige von Referenz-Fehlern im Soution Explorer und in der project.json wurde verbessert. Lauter Kleinkram, im Vergleich zu folgender Änderung: Wie wir wissen, kann eine ASP.NET 5 Applikation (wie alle DNX–Apps) in ein NuGet-Package deployed werden. Jetzt ist es endlich möglich für eben dieses Package explizit Abhängigkeiten festzulegen und zusätzliche Dateien zu definieren die im Package enthalten sein sollen.

image

Weitere Änderungen:

Etwas detaillierter und noch ein paar weitere Neuigkeiten zu Beta8 ist in der offiziellen Ankündigung zu lesen: Announcing Availability of ASP.NET 5 Beta8

Authorization Policies in ASP.NET 5 Middlewares

Um auch selektiv statische Dateien im Ordner “wwwroot” vor unautorisiertem Zugriff zu schützen, kann man den Weg über eine sog. Authorization Policy gehen. Die Policy wird dann in einer eigenen Middleware genutzt, welche die eigentliche Zugriffsprüfung vornimmt.

Wie das konkret aussieht schreibt K. Scott Allen sehr ausführlich in seinem aktuellen Blogpost: Authorization Policies and Middleware in ASP.NET 5

Generell sind Authorization Policies eine elegante Art der Autorisierungsprüfung in eigenen Middlewares. Dabei lassen sich beliebig viele Policies hinzufügen, die wiederum an beliebigen Stellen geprüft werden können.

Slides zu meinen Vorträgen auf der Basta! Konferenz 2015

ASP.NET auf dem Raspberry PI und Docker

Auf der diesjährigen Basta! Konferenz durfte ich ebenfalls zeigen, wie man ASP.NET 5 auf einem Raspberry PI 2 mit Rasbian (Debian Wheezy) installiert und zum laufen bringt. Als zusätzliches Thema habe ich diesmal Docker mit in den Vortrag genommen und gezeigt, wie man ein eigenes ASP.NET 5 Projekt in einem Docker-Container startet:

http://www.slideshare.net/juergengutsch/aspnet-5-auf-raspberry-pi-docker

HTML 5 Magic

Im zweiten Vortrag ging es um die die Features aus HTML 5, die man nicht auf Anhieb in Form von HTML-Tags sehen kann. Local Storage, File API, Media Capture, Web Worker waren auch unter den Beispielen. Die sechs Folien die ich hier verlinke haben keinen wirklich relevanten Inhalt, außer den Link auf die Beispiele auf GitHub und meine Kontaktinformationen. ;)

http://www.slideshare.net/juergengutsch/html-5-magic

Der direkte Link auf die Beispiele: https://github.com/JuergenGutsch/html5magic/

Plattformunabhängige Desktop-Apps

Das erstellen von plattformunabhängigen Desktop-Apps, mit HTML5 und JavaScript zu erstellen ist aktuell in aller Munde und ein großes Thema. Plattformunabhängige Apps gibt es immer mehr. Schöne Beispiele sind unter anderem die IDEs Brackets, Visual Studio Code, Ionic Labs, sowie der Messenger Slack

Dennoch schreibe ich das nicht aus dem Grund, weil es im Moment ein Hype zu sein scheint, sondern weil es rein Zufällig eine aktuelle Anforderung ist.

Genauer geht es darum, eine Web-App auch offline Verfügbar zu machen und auf möglichst vielen verschiedenen Plattformen verfügbar zu machen.

In den letzten Tagen habe ich mich deshalb ein wenig umgeschaut und app.js und nw.js genauer angeschaut. Auch auf electron habe ich einen kurzen Blick geworfen. Dieses sehr gute und umfangreiche Framework ist allerdings für meine zwecke etwas oversized.

app.js – mein Favorit – wollte auf Anhieb nicht laufen. nw.js lief dagegen sofort, weswegen ich mich auch dazu entschlossen habe, dieses Framework noch intensiver anzuschauen.

NW.js

Ich war ganz ehrlich positiv überrascht wie einfach nw.js funktioniert. Auch das deployment der fertigen App ist ein Kinderspiel.

Die Installation erfolgt per NPN:

> npn install nwjs –g

Einmal global installiert ist das Kommando “nw” überall verfügbar und ich kann jeden Ordner zu einer App machen.

Für uns ist es nicht relevant am Ende eine ausführbare Datei zu haben (was ebenfalls möglich is), sondern für unsere Zwecke reicht es, einfach ein Verzeichnis zu verteilen und die App zu starten.

Um ein Verzeichnis zu einer App zu machen, muss eine package.json erstellt werden die in etwa so aussehen kann:

{
  "name": "nwjstest",
  "version": "0.0.1",
  "main": "index.html",
  "window": {
    "toolbar": false,
    "width": 800,
    "height": 600,
    "frame": false,
    "fullscreen": false
  }
}

Die Angaben zu “name” und “version” sind obligatorisch, “main” gibt die HTML-Datei an die Initial aufgerufen werden soll. Interessanter wird es mit den Angaben für das Fenster. Unter “window” kann angegeben werden wie sich das Fenster verhalten soll. Die “toolbar” ist während der Entwicklung interessant um ggf. die Chrome Developer Tools aufzurufen. “frame” legt fest ob der Windows-Rahmen angezeigt werden soll oder nicht. Mit der zusätzlichen Aktivierung von “fullscreen” kann dann eine Art Kiosk-Mode gestartet werden. Es gibt noch ein paar weitere Parameter die in der Doku zu nw.js beschrieben sind. Unter anderem gibt es auch einen echten “kiosk”-Mode mit “kiosk”:true

Genau das, was wir in diesem Projekt benötigen.

Die index.html ist die Web-App, die mit allem was dazu gehört (Bilder, Scripts und Styles) in dem Verzeichnis abgelegt werden sollte.

Gestartet wird die App dann mit folgendem einfachen Kommando

> nw appfolder

oder mit

> nw .

wenn ich mich mit der Konsole bereits in dem Verzeichnis befindet.

Mehr gibt es nicht zu tun um eine sehr einfache und anspruchslose plattformübergreifende App zu bauen. Allerdings bleibt zu erwähnen, dass in den Scripts der Web-App direkt Node.JS ausgeführt werden kann. Sollte man also eine App erstellen wollen die mit dem System interagiert, ist das eine enorme Hilfe und macht das Framework unheimlich flexibel.

Usergroup Events im Oktober

Dieser Herbst ist voll mit interessanten Community Events. Leider kann ich nur an sehr wenigen Veranstaltungen teilnehmen. KW 42 ist für mich besonders spannend, da ich gleich an drei Usergroup Treffen aktiv teilnehmen werde.

Basel

Den Start macht am Montag die .NET Usergroup Basel, bei der ich als einer der Usergroup-Leiter teilnehmen werde. (Möglicherweise wird der Termin noch um eine Woche verschoben. Ich erwarte noch Feedback vom Sprecher.)

Bern

Am Dienstag fahre ich zur .NET Usergroup nach Bern, um neues zu ASP.NET 5 und MVC 6 zu erzählen. Mir geht es hierbei allerdings weniger um den Vortrag, als darum endlich mal die Usergroup Bern zu besuchen. Der letzte Versuch die Usergroup zu besuchen, endete leider im Feierabend-Stau in Zürich. Diesmal komme ich aber aus Basel mit der Bahn. Ich freue mich schon auf die bekannten und vielen neuen Gesichter.

Friedrichshafen

Am Mittwoch werde ich dann mit dem gleichen Vortrag, bei der neu gegründeten .NET Usergroup in Friedrichshafen am Bodensee erwartet. Auch das wird ein spannendes Event, da ich hier auf einen Hochmotivierten, .NET begeisterten, Ex-Kunden in der Rolle des Usergroup Leiters treffe. Ich freue mich das Tobias Allweier es gewagt hat eine Usergroup in Friedrichshafen zu starten. Potentielle Teilnehmer gibt es am Technologie-Standort Friedrichhafen viele. Auch aus dem angrenzende Allgäu oder aus Ravensburg, können Teilnehmer erwartet werden.

Was wurde aus der NUG Konstanz-Kreuzlingen?

Die Usergroup Konstanz-Kreuzlingen pausiert im Moment. Der Grund hier ist ein starker Rückgang der Teilnehmerzahlen, die immer schon recht klein waren. Am Ende resultierte es in Events, an denen nur die Usergroup-Leiter beteiligt waren, oder gar in Events, bei dem der extra angereiste Sprecher gemütlich mit einem Usergroup-Leiter ein Bierchen am See schlürften. Um so mehr freut es mich, dass die NUG Friedrichshafen hier eine Lücke füllen wird.

Weitere Events

Im November haben wir übrigens Roland Krummenacher (Azure MVP) bei der .NET Usergroup Basel, der uns Neuigkeiten zu Azure erzählen wird

Neben den Usergroup Events bin ich dieses Jahr, Ende September, auf der Basta! und zeige wie man ASP.NET auf dem Raspberry PI 2 und Docker laufen lassen kann und in einem zweiten Vortrag zeige ich ein paar interessante Features die uns HTML5 zu bieten hat und die nicht für jeden Alltäglich sind.

Im November, werde ich zudem auf der continuous lifecycle 2015 einen Erfahrungsbericht präsentieren, in dem es darum geht, wie wir bei der YooApps mit einem verteilten Team eine große SharePoint App Entwickelt und weltweit ausgerollt haben. Dafür gab es ein paar Tools, Plattformen und Techniken die uns das Leben enorm einfach gemacht haben.

Der Herbst ist nun aus meiner Sicht – vor allem auch aus der Sicht der Familie – relativ voll. Dennoch biete ich gerne an, dass ich für die ASP.NET 5 Session auch zu weiteren .NET Usergroups fahre. Im Herbst allerdings nur im Umkreis von max. 2 Stunden fahrt. Ab Dezember, dann gerne auch weiter weg.

ASP.NET 5 auf Docker starten

Im letzten Beitrag zu Docker habe ich gezeigt, was man tun muss um die Docker Toolbox so einzurichten, dass man damit arbeiten kann. In diesem Beitrag zeige ich, wie man nun eine ASP.NET Applikation auf Docker zum Laufen bringt.

Microsoft hat auf Docker Hub ein Image bereitgestellt, das komplett eingerichtet ist und sogar mit der aktuellen ASP.NET 5 Beta7 läuft. Das kann als Grundlage genutzt werden um ein ASP.NET 5 Web auf einem Docker Container laufen zu lassen:

$ docker run microsoft/aspnet

Dieser Befehl in der Bash lädt das Image bei Bedarf (in der Regel beim ersten Aufruf) herunter und startet es.

Ein Web ist scheinbar nicht vorinstalliert, oder es lässt sich keines starten. Daher möchte ich auf Grundlage des Images von Microsoft ein eigenes erstellen mit einer installierten ASP.NET 5 App.

Dafür erstelle ich zunächst mit dem Visual Studio 2015 ein neues ASP.NET 5 Projekt mit dem Standart-Template. Da es sich um ein Linux Image handelt muss ich in der project.json zwei Dinge einrichten. Zum einen benötige ich Referenzen zum Webserver „Kestrel“ und zum anderen das Kommando um diesen aufzurufen:

"Microsoft.AspNet.Hosting": "1.0.0-beta7",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-beta7"

 

"kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5001"

image

Sind diese Einträge gemacht, so kann ich das ASP.NET Web unter Linux mit folgendem Befehl starten:

$ dnx kestreld

Nun muss das Docker Image noch erstellt werden.

Dafür erstellen wir am besten im Projekt-Verzeichnis unseres Webs (dort wo die project.json liegt) eine neue Datei mit dem Namen „Dockerfile“ (ohne Endung) mit folgendem Inhalt:

FROM microsoft/aspnet:latest

COPY . /app
WORKDIR /app

RUN ["dnu", "restore"]

EXPOSE 5001

ENTRYPOINT ["dnx", "kestrel"]

Diese Datei ist ein Set von Befehlen die nacheinander ausgeführt werden. Die erste Zeile legt das Basis-Image fest von dem wir „erben“

Anschließend wird das aktuelle Verzeichnis (das Projektverzeichnis) in den Ordner „app“ kopiert und dieses Verzeichnis wird zum Arbeitsverzeichnis erklärt. Nun müssen die NuGet Pakete hergestellt werden. In der zweitletzten Zeile wird der Port gesetzt und zu guter Letzt wird die Applikation gestartet.

Ist die Datei erstellt und gespeichert, müssen wir das Image erstellen. Das passiert mit folgendem Befehl:

$ docker build -t guj/aspnet5 .

Dazu wechseln wir mit der Bash in der Projektverzeichnis. „-t guj/aspnet5“ legt den Image-Name fest, der beliebig gesetzt werden kann. Der Punkt verweist auf das aktuelle Verzeichnis. Dieser Vorgang keine eine Weile gehen, weil dabei die NuGet Packages geladen werden.

Ist das Image fertig können wir es starten:

$ docker run -i -t -p 5001:5001 guj/aspnet5

Die Parameter i und t legen einen interaktiven Modus fest, damit wir mit dem Container interagieren könen. „-p 5001:5001“ mappt die Ports von der Anwendung zur Maschine, damit der in der project.json festgelegte Port auch von außer erreichbar ist. Anschließend wird der name des zu startenden Images angegeben:

image

Nun können wir die Applikation mit der IP der Maschine und dem Port 5001 aufrufen:

image

Es sollte noch erwähnt werden, dass dieses Image keine .NET Core Applikation starten kann. Das von Microsoft bereitgestellte Image mit Mono, erwartet dnx451 als Runtime. Der erste Versuch den ich nur mit dnxcore gestartet habe scheiterte mit der Meldung, dass eben dnx541 erwartet wird.

TagHelpers Beipiele auf GitHub

Dave Paquette hat auf GitHub ein Repository eingelegt, welches Beispiele für diverse selbstgeschriebene TagHelper enthalten soll. Einige TagHelper, wie z. B. für eine ProgressBar, Bootstrap-Alert und Bootstrap-Model, sind bereits eingetragen. Unter anderem hat auch Rick Strahl Bespiele für TagHelper bereitgestellt.

https://github.com/dpaquette/TagHelperSamples

Dave lädt jeden ein, Ideen und Beispiele zu liefern. Mehr Informationen dazu findet ihr in seinem Blog: Custom MVC 6 Tag Helper Samples

Mehr Beiträge Nächste Seite »