code it

Martins Tech Blog

Als Sprecher bei der .NET Usergroup Dresden

Im Grunde war es ein Heimspiel. Beim letzten Treffen der .NET Usergroup Dresden vor gut einer Woche war ich dort mal wieder zur Abwechslung als Sprecher.

Thematisch ging es dabei eher um ein Randgebiet für .NET Entwickler. Gemeinsam mit Matthias Wendt widmeten wir den Abend dem SQL Server 2012. Während Matthias das Thema Datenqualität und die in dieser Version neu hinzugekommenen Data Quality Services näher beleuchtete und anhand von Beispielen erklärte, ging ich auf Neuerungen in TSQL ein und zeigte anhand von Vorher-Nachher-Statements, ob und wo sich hier Vereinfachungen ergeben.

Es war ein sehr spannender Abend und anhand der doch recht großen Teilnehmerzahl von über 15 die auch den Abend mit aktivem Mitmachen und Rückfragen bereichert haben war eindeutig erkennbar, dass es auch noch .NET-Entwickler gibt, die sich um Datenspeicherung kümmern und nicht nur im Frontend unterwegs sind.

Englische Version Englische Version

IIS Express komfortabel konfigurieren

... oder "Ich hab da mal was vorbereitet".

Wer schon einmal versucht hat, seinen IIS Express mit SSL mit einem abweichenden Hostnamen (also nicht localhost oder der tatsächliche Rechnername) laufen zu lassen wird es kennen - mit nur sieben winzigen Schritten kommt man zum Erfolg:

  1. hosts-Datei anpassen
  2. Bindings in der applicationhost.config des IIS Express anpassen
  3. mit netsh die Urls für gesicherte und ungesicherte Verbindungen in http.sys reservieren
  4. ggf. mit netsh die entsprechenden Ports an der Windows Firewall öffnen
  5. mit makecert selbstsigniertes Zertifikat erstellen
  6. in der Management-Konsole den Fingerabdruck des neuen Zertifikats suchen
  7. das erstellte Zertifikat mit netsh dem gewünschten SSL-Port zuweisen

Und da das RequireSSL-Attribut fest auf Port 443 verweist und man ggf. einen anderen Port verwendet, kann es sein, dass man noch eine URL-Rewrite-Rule in der web.config einrichten muss.

Die ganzen Schritte hat Scott Hanselmann sehr detailliert in seinem Post Working with SSL at Development Time is easier with IISExpress zusammengefasst.

Interessant ist der letzte Abschnitt des genannten Posts - denn dort zeigt er, dass es noch einen undokumentierten Weg gibt, der uns die meisten der genannten Schritte abnimmt. Mit Hilfe des Kommandozeilentools IISExpressAdminCmd können solche benutzerfreundlichen Urls in http.sys und hosts-Datei hinzugefügt und auch wieder entfernt werden - und das sowohl für ungesicherte als auch ssl-gesicherte Urls. Einzig um die Anpassung der applicationhost.config und ggf. notwendiger Url-Rewrite-Regeln kommt man auch damit nicht herum. 

Ein kleines Manko hat dieses Tool - es werden nur reine Rechnernamen akzeptiert (z.B. contoso); also die Konfiguration einer kompletten Url mit verschiedenen Levels (z.B. dev.contoso.com) ist damit nicht möglich. In diesem Fall kann man nicht die Abkürzung verwenden, sondern ist auf den langen Weg angewiesen.

Englische Version Englische Version

Array-Parameter im WCF Test-Client

Zugegeben, die Eingabe von Array-Parametern im WCF-Test-Client ist nicht sehr intuitiv. Und eben weil man es auf den ersten Blick nicht erkennt, hier an dieser Stelle ein kleiner Tipp für alle die schon daran gescheitert sind.

Gegeben ist ein WCF-Service, der einen Array-Parameter hat - in meinem Beispiel gibt es den EmployeeService, welcher in der Methode Create eine Auflistung an Objekten vom Typ Employee entgegennimmt.

[ServiceContract]
public interface IEmployeeService
{
    [OperationContract]
    void Create(IEnumerable<Employee> employees);
}

Soweit nicht weiter kompliziert.

Startet man im Visual Studio nun eine neue Instanz des Services, so öffnet sich der WCF Test-Client und man kann den Service aufrufen, wenn man es denn schafft, hier Objekte vom Typ Employee einzutragen. Mehr...

Als Sprecher bei der .NET Usergroup Leipzig

Am 25.06. war ich zu Gast bei der .NET Usergroup Leipzig. Thema das Abends war modulares Anwendungsdesign und wie Prism dabei unterstützen kann. Nach ein paar Slides, in denen die theoretischen Grundlagen etwas beleuchtet wurden, ging es auch schon ans Live-Coding.

Die Gruppe war mit unter 10 Teilnehmern recht überschaubar, was aber den großen Vorteil hatte, dass wir bei einigen aufgetretenen Fragen direkt einsteigen konnten. Und auch ich habe dank Marcel dort noch etwas gelernt zu einem Feature von Resharper, das mir bisher unbekannt war.

Ich fand es einen sehr gelungenen Abend und auch das netzwerken im kleinen Kreis nach der Usergroup im Kesselhaus war ein kleiner Event.

Für alle diejenigen, die das Live-Beispiel noch einmal nachvollziehen wollen, sind hier die Slides - 20120625_UGLeipzig_Prism.zip (1,78 mb) - und das Code-Sample - Prism_Slides.pdf (753,54 kb).

PS: Bitte denkt daran, dass das Beispiel Directory-Discovery verwendet.

Flagged Enums und XAML-Syntax

In meinem letzten Post habe ich mich mit den Besonderheiten der XAML-Syntax bei Nested Types auseinandergesetzt. Ähnlich gewöhnungsbedürftig ist die Syntax bei Flagged Enums.

Standard-Enum-Werte können mit Hilfe der Textrepräsentation recht einfach zugewiesen werden. Ein Beispiel dafür ist die Eigenschaft Visibility.

<TextBlock Text="Sampletext" Visibility="Collapsed" />

Hier sorgt der TypeConverter für Enums dafür, dass aus dem String "Collapsed"  der Wert System.Windows.Visibility.Collapsed generiert wird.

Etwas anders sieht das Ganze bei Flagged Enums aus. Als Beispiel soll ein UserControl dienen, an dem per DependencyProperty eingestellt werden kann, welche Eingabemethoden gültig sind. Da hier mehrere Werte möglich sind, wird das Ganze über ein Flagged Enum gelöst.

Mehr...

Nested Types und XAML-Syntax

Ich gebe zu, ich bin kein großer Freund von Nested Types, da sie an einigen Stellen umständlicher zu behandeln sind als Typen, die "ganz normal" in Namespaces liegen. Aber es soll API-Hersteller geben, die Nested Types verwenden. Um dann nicht Dinge mehrfach entwickeln zu müssen, bietet es sich an, diese Typen gleich weiterzuverwenden.

Was ist ein Nested Type?

Das ist recht einfach erklärt: Ein Nested Type ist ein Datentyp, der in einem anderen Datentyp deklariert ist - also eine Klassendefinition innerhalb einer anderen Klasse und eben nicht direkt in einem Namespace. Das folgende Beispiel soll das etwas verdeutlichen:

public class Constants
{
    public class Colors
    {
        public const string Red = "#FF0000";
        public const string Green = "#00FF00";
        public const string Blue = "#0000FF";
    }

    public class Alignments
    {
        public const string Left = "Left";
        public const string Right = "Right";
    }
}

Zugriff im Code

Der Zugriff im Code ist ähnlich wie wenn die Klasse in einem Namespace liegt - man verwendet die Punkt-Syntax.

var color = Constants.Colors.Blue;

Zu beachten ist hier lediglich, dass man sich ab dem obersten Typ durch die Struktur hangeln muss und eben nicht wie im Fall von Namespaces eine using-Direktive verwenden kann. Ok, einen Trick gibt es doch - man kann einen Alias für den Typ vergeben:

using ColorConstants = Constants.Colors;

public class MainViewModel
{
    public MainViewModel()
    {
        LoadData();
    }

    public void LoadData()
    {
        var color = ColorConstants.Blue;
    }
}

Zugriff im XAML

In meinem Beispiel handelt es sich um Konstanten, auf die ich im XAML auch gern im statischen Kontext zugreifen möchte . Dafür bietet sich x:Static an. Verwende ich hier die gleiche Syntax wie im Code, so erhalte ich eine Fehlermeldung die besagt, dass der Typ nicht gefunden werden kann.

Alles kein Problem - dabei handelt es sich nämlich um einen Syntaxfehler. Der Zugriff auf Nested Types erfolgt nicht mit "." sondern mit "+". Korrekt ist es also wie folgt:

<TextBlock Text="{x:Static local:Constants+Colors.Blue}" />

Damit funktioniert der Zugriff nun zur Laufzeit, und auch in Expression Blend wird alles korrekt angezeigt. Aber ab diesem Zeitpunkt streikt nun der Designer von Visual Studio.

Englische Version Englische Version

InvalidOperationException in WebBrowserTask.Show

In Windows Phone 7 hat man mit Hilfe der Launcher und Chooser die Möglichkeit, auf im System integrierte Funktionen zuzugreifen - sei es nun die Kamera, der Emailclient oder wie in meinem Fall der Browser. Meine Anwendung hat mehrere Buttons über die zu unterschiedlichen Webseiten weiternavigiert werden kann - hauptsächlich als weiterführende Information im Impressum.

Dafür hab ich abstrakt gesehen folgende zentrale Methode:

public static void OpenInWebBrowser(string url)
{
    var openBrowserTask = new WebBrowserTask { Uri = new Uri(url) };
    openBrowserTask.Show();
}

Diese Methode wird direkt in dem Command aufgerufen, das an dem Button hängt - alles kein Hexenwerk. Was mich nun wunderte: Gelegentlich bekam ich Error-Reports mit diesem Inhalt:

Navigation is not allowed when the task is not in the foreground. Error: -2147220990

Da stellte sich mir folgende Frage: Wie kann jemand den Button klicken, wenn die Anwendung nicht im Vordergrund ist?

Die Lösung ist ganz einfach: Der Benutzer hat kurz nacheinander auf den Button geklickt - sei es nun weil er so aufgeregt war, eine schöne Webseite zu sehen oder weil er gerade unterwegs auf einer holprigen Straße war - die Möglichkeiten sind da ja sehr vielfältig. Das ist möglich, weil der Launcher eine Weile braucht, um den Browser in den Vordergrund zu holen. Der Nutzer kann also dafür sorgen, dass der Click-Event zweimal ausgelöst wird - der zweite Event wird aber erst dann verarbeitet, wenn der erste WebBrowserTask den Browser geöffnet hat und damit die eigentliche Anwendung nicht mehr im Vordergrund ist. Und dann kommt es zu dieser Exception.

Wie geht man nun damit um? Es gibt mehrere Ansätze. Zum einen kann man natürlich mit Try-Catch-Blöcken arbeiten und genau diesen Exception-Typ abfangen. Da diese Exception bei dieser Methode bisher in der MSDN nicht dokumentiert ist, ist es etwas schwierig hier von allein auf die Idee zu kommen, diesen speziellen Typ abzufangen, aber nachher weiß man es immer besser. Zusätzlich könnte man noch den Click-Handler abhängen bzw. den Button oder das Command deaktivieren. 

Ich bin nicht der einzige, dem dieses Problem untergekommen ist - und Niko hat einen sehr ausführlichen Blogpost zum Thema geschrieben, der auch alle hier genannten Lösungsmöglichkeiten aufführt.

Request Validation an eigene Bedürfnisse anpassen

Die Validierung der an die Webanwendung übertragenen Daten spielt eine zentrale Rolle, ist sie doch der Garant dafür, dass kein schadhafter Code eingeschleust werden kann und verhindert Cross Site Scripting (XSS). Und wer hat nicht schon einmal den YOSD gesehen, der erscheint, wenn ein Wert eingegeben wird, der auch nur im entferntesten wie HTML aussieht.

Was hier passiert ist klar. Die Request Validation von ASP.NET hat in den Eingabewerten einen Eintrag gefunden, den sie als potenziell unsicher erkannt hat und schützt nun die Anwendung indem eine HttpRequestValidationException geworfen wird. Was für Entwickler an der Request Validierung störend ist, ist, dass sie an einer Stelle vorgenommen wird, an der nicht so richtig gut eingegriffen werden kann - sie geschieht vor der BeginRequest-Phase des Http Requests. In meinem Post zum Attribut AllowHtml habe ich schon gezeigt, wie man mit Hilfe des AllowHtmlAttribute an Eigenschaften des Models die Request Validierung für einzelne Feldwerte deaktivieren kann. Aber ASP.NET bietet noch eine weitere Möglichkeit, in die Validierung einzugreifen: eine eigene Implementierung der Request Validation. Mehr...

FileNotFoundException im Konstruktor von SPSite

Als ich eben den Post zu AddFieldAsXml geschrieben habe, habe ich die Beispiele in einer Konsolenanwendung nachgestellt. Das Beispiel ist nicht sehr kompliziert und doch überraschte mich SharePoint direkt in der ersten Zeile mit einer FileNotFoundException.

Laut MSDN wird eine FileNotFoundException im SPSite-Konstruktor dann geworfen, wenn die SiteCollection unter der Url nicht gefunden werden konnte. Die Url ließ sich jedoch problemlos im Browser aufrufen - sowohl unter localhost als auch unter dem Rechnernamen wurde die SiteCollection gefunden. Nächster Versuch war also, die Credentials explizit mitzugeben, auch wenn das eigentlich nicht das Problem sein dürfte, weil sowohl Browser als auch Visual Studio mit dem gleichen Nutzeraccount verwendet wurden.

Was die MSDN-Seite verschweigt: Diese Exception wird auch dann geworfen, wenn das Platform-Target nicht stimmt. Konsolenanwendungen haben in der Visual Studio Vorlage grundsätzlich erst einmal das Platform Target x86; SharePoint aber ist eine 64-bit-Anwendung, daher muss das Platform Target der Konsolenanwendung auch x64 sein, damit es funktioniert. Also die Einstellung eben geändert, neu kompiliert und schon klappt es auch von der Konsole.

AddFieldAsXml - Bug oder Feature, das ist hier die Frage

Möchte man programmatisch Felder zu einer SharePoint-Liste hinzufügen, so stolpert man früher oder später über die Methoden Add bzw. AddFieldAsXml von SPFieldCollection. In vielen Fällen führen beide Methoden zum Ziel, wobei AddFieldAsXml den Vorteil hat, dass man recht einfach das Schema-Xml aus bereits bestehenden Listen übernehmen kann, es auch recht komplexe Szenarien zulässt und dann in meinen Augen besser lesbar ist als Quellcode, der über mehrere Zeilen oder Klassen verteilt versucht, ein Objekt vom Typ SPField zu erzeugen und mit den passenden Eigenschaften zu bestücken.

Damit möchte ich auch schon zum Problem kommen: Ein Objekt vom Typ SPField verfügt über zwei Eigenschaften, die in diesem Kontext eine Rolle spielen: Title stellt in der Liste den Spaltennamen dar, den der Nutzer zu Gesicht bekommt. Dieser kann natürlich auch Leerzeichen oder Sonderzeichen beinhalten. Im Gegensatz dazu ist InternalName die interne Referenz auf die Spalte und als solche ist sie in der jeweiligen Liste eindeutig und readonly. Zudem werden hier sämtliche Sonderzeichen maskiert dargestellt. Um es mal plastisch darzustellen im folgenden das Beispiel aus der MSDN.

using (var site = new SPSite("http://localhost/"))
{
    using (var web = site.RootWeb)
    {
            var list = web.Lists["Shared Documents"];

            var displayName = "My Custom Field";
            var staticName = "MyStaticName";

            var strInternalName = list.Fields.Add(displayName, SPFieldType.Text, false);
            var field = list.Fields.GetFieldByInternalName(strInternalName);

            field.StaticName = staticName;
            field.Update();
    }
}

In diesem Fall ist Title nun wie erwartet My Custom Field und der Internal Name My_x0020_Custom_x0020_Field.

Wie bereits angedeutet, geht das ganze auch mit AddFieldAsXml:

using (var site = new SPSite("http://localhost/"))
{
    using (var web = site.RootWeb)
    {
        var list = web.Lists["Shared Documents"];

        var fieldXml = "<Field Type=\"Text\" MaxLength=\"50\" DisplayName=\"My Custom Field\" ID=\"15C0D97B-3735-4AD9-8FE2-8A01E5B43486\" StaticName=\"MyStaticName\" Name=\"MyInternalName\" />";
        var strInternalName = list.Fields.AddFieldAsXml(fieldXml);

        var field = list.Fields.GetFieldByInternalName(strInternalName);

        field.Update();

    }
}

Was bei der Ausführung nun auffällt: Obwohl ich im XML einen Internal Name vorgegeben habe, wird dieser nicht verwendet, sondern weiterhin auf Basis des Display Name ein Internal Name generiert. Das ist insofern schlecht, da wie beschrieben der Internal Name ja der Identifier der Spalte ist und deshalb auch in CAML-Queries verwendet wird. Mehr...