Peter Bucher Ralf Westphal

Blog

Nutze den Augenblick
und teile der Welt mit, was Du zu sagen hast.

LightCore signiert

Dienstag, 9. Februar 2010, 22:39 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Am 1. Januar 2010 hat Peter Bucher die erste Version seines neu entwickelten Microkernels LightCore veröffentlicht. Seitdem sind bereits wieder einige Features hinzugekommen, die allerdings derzeit nur in der Entwicklerversion zur Verfügung stehen, die als Quellcode aus dem zugehörigen Subversion-Repository abgerufen werden kann.

Nach wie vor halte ich LightCore für einen äußerst bemerkenswerten Microkernel und Dependency Injection-Container, auch die intensive Nutzung hat meiner Begeisterung keinen Abbruch getan – im Gegenteil.

Und dennoch – es gibt eine Kleinigkeit, die mich bislang gestört hat: Peter stellt LightCore lediglich als nicht-signierte Assemblies bereit. Das bedeutet, dass jeder, der wie ich eine signierte Variante benötigt, sich zunächst den Quellcode herunterladen, ihn anpassen und neu übersetzen muss.

Peter und ich haben daher beschlossen, dass ich zukünftig den jeweils aktuellen Stand von LightCore als signierte Version zur Verfügung stellen werde. Signiert sind dabei die folgenden drei Assemblies:

  • LightCore.dll
  • LightCore.Configuration.dll
  • LightCore.Integration.Web.dll

Nicht signiert ist die LightCore.CommonServiceLocator.dll, da diese von einer zumindest derzeit ebenfalls nicht signierten Assembly von Microsoft abhängt. Da es in .NET aus Sicherheitsgründen nicht möglich ist, aus einer signierten Assembly auf eine nicht-signierte Assembly zu verweisen, kann die LightCore.CommonServiceLocator.dll vorerst nicht signiert werden.

Der Download der signierten Variante von LightCore findet sich als ZIP-Archiv unter http://www.goloroden.de/Downloads/LightCore.aspx, wobei darin ebenfalls die nicht-signierte vierte Assembly enthalten ist.

In den nächsten Tagen wird dieser Link dann auch auf der offiziellen Webseite von LightCore verfügbar sein, so dass auch zukünftig alle verfügbaren und offiziellen Downloads dort aufgelistet sind.

Performanceimplikationen von System.Transactions

Montag, 9. November 2009, 14:23 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

In meinem Blogeintrag Transaktionale Methoden habe ich vorgestellt, wie mit Hilfe von PostSharp ein TransactionalAttribute für .NET implementiert werden kann, das sich analog zu der @Transactional-Annotation von JEE verhält. Intern habe ich dabei den in .NET 2.0 eingeführten Namensraum System.Transactions verwendet.

Nun wurde als Kommentar zu diesem Blogeintrag die Frage gestellt, ob die Klasse TransactionScope generell verwendet werden könne, oder ob man besser auf die klassischen ADO.NET-Transaktionen zurückgreifen sollte, weil diese angeblich performanter seien.

Als Antwort auf diese Frage habe ich folgenden Kommentar verfasst:

die kurze Antwort lautet: Sofern Du den SQL Server einsetzt, kannst Du guten Gewissens auf System.Transactions setzen, bei anderen Datenbanken ist zumindest Vorsicht geboten.

Wie komme ich zu dieser Aussage?

Vor der Einführung des Namensraums System.Transactions in .NET 2.0 gab es zwei verschiedene Möglichkeiten, Transaktionen zu verwenden:

  • Einerseits konnten die klassischen ADO.NET-Transaktionen verwendet werden, die programmatisch angesprochen und gesteuert wurden.
  • Andererseits konnten die Enterprise Services verwendet werden, um deklarativ Methoden zu kennzeichnen, die transaktional ausgeführt werden sollten.

Beide Verfahren haben ihre jeweiligen Vor- und Nachteile: Da die klassischen ADO.NET-Transaktionen direkt auf der Datenbank ausgeführt werden, sind sie sehr performant, sind aber für verteilte Transaktionen ungeeignet.

Transaktionen, die hingegen über die Enterprise Services ausgeführt werden, verwenden implizit den Distributed Transaction Coordinator (DTC) von Windows und sind daher für verteilte Transaktionen geeignet. Da der DTC jedoch immer eingesetzt wird – auch dann, wenn eine lokale Transaktion genügen würde – ist die Performance nicht ganz so hoch wie bei den klassischen ADO.NET-Transaktionen.

Der Namensraum System.Transactions dient nun als Nachfolger für beide Varianten, weshalb prinzipiell so wohl lokale wie auch verteilte Transaktionen unterstützt werden. Die Logik, nach welcher der DTC hinzugezogen wird (oder eben nicht), ist dabei folgendermaßen aufgebaut:

  • Wenn eine Verbindung zum SQL Server aufgebaut wird, wird die Transaktion zunächst lokal ausgeführt. Sobald innerhalb der gleichen Transaktion eine weitere Verbindung zu einer Datenbank aufgebaut wird, wird die Transaktion verteilt ausgeführt. Damit dies funktioniert, enthält der SQL Server ein Feature namens Transaction Propagation, womit eine lokale in eine verteilte Transaktion umgewandelt werden kann.
  • Wenn eine Verbindung jedoch zu einer anderen Datenbank als einem SQL Server aufgebaut wird, geht .NET davon aus, dass Transaction Propagation nicht unterstützt wird, weshalb von System.Transactions direkt eine verteilte Transaktion gestartet wird – unabhängig davon, ob noch eine weitere Verbindung zu einer anderen Datenbank hergestellt wird oder nicht.

Die Konsequenz: Sofern innerhalb einer Transaktion nur eine einzelne Verbindung zu einem SQL Server hergestellt wird, verhält sich System.Transactions analog zu der klassischen ADO.NET-Transaktion – und verfügt damit über dieselbe Performance.

Der Overhead des DTC schlägt erst bei verteilten Transaktionen oder beim Einsatz einer anderen Datenbank zu.

Insofern kann man die Verwendung von System.Transactions beim Einsatz von SQL Server guten Gewissens generell empfehlen – bei allen anderen Datenbanken ist hingegen Vorsicht geboten, da hier automatisch der DTC zum Einsatz kommt, auch wenn keine verteilten Transaktionen genutzt werden.

Transaktionale Methoden

Sonntag, 6. September 2009, 21:34 Uhr
Permalink | Kommentare (3) | Kommentare als RSSRSS

Spätestens seit dem in .NET 2.0 eingeführten Namensraum System.Transactions ist es keine Kunst mehr, so wohl lokale wie auch verteilte Transaktionen in eigenem Code zu nutzen.

Allerdings ist es dafür in jeder Methode erneut notwendig, einen TransactionScope zu öffnen, gegebenenfalls dessen Complete-Methode aufzurufen und ihn schließlich wieder korrekt zu verwerfen – zumindest dies kann durch den Einsatz einer using-Anweisung erleichtert werden.

Andere Plattformen als .NET bieten an dieser Stelle mehr: So genügt beispielsweise bei der Java Enterprise Edition (JEE) seit dem EJB 3.0-Standard die Angabe der Annotation @TransactionAttribute bei der Deklaration einer Methode, um diese in einer Transaktion auszuführen.

Mit Hilfe von PostSharp ist es jedoch auch innerhalb von .NET ein Leichtes, ein solches Attribut zu entwickeln. Dazu muss lediglich ein OnMethodBoundary-Aspekt geschrieben werden, der beim Aufruf einer Methode einen neuen TransactionScope anlegt, diesen an die Methode bindet, und der beim Beenden der Methode den TransactionScope wieder schließt.

Die Entscheidung, ob ein Commit oder ein Rollback durchgeführt werden soll, kann dann beispielsweise an Hand der Tatsache entschieden werden, ob die gekapselte Methode durch eine Ausnahme oder auf normalem Wege verlassen wurde:

using PostSharp.Laos;
using System;
using System.Transactions;
namespace goloroden.de.Aspects
{
    /// <summary>
    /// Provides transaction control to methods.
    /// </summary>
    [Serializable]
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = true)]
    public sealed class TransactionalAttribute : OnMethodBoundaryAspect
    {
        /// <summary>
        /// Contains the transaction scope options.
        /// </summary>
        private TransactionScopeOption _transactionScopeOption;
        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionalAttribute" /> type.
        /// </summary>
        public TransactionalAttribute() : this(TransactionScopeOption.Required)
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionalAttribute" /> type.
        /// </summary>
        /// <param name="transactionScopeOption">The transaction scope options.</param>
        public TransactionalAttribute(TransactionScopeOption transactionScopeOption)
        {
            this._transactionScopeOption = transactionScopeOption;
        }
        /// <summary>
        /// Executed when a method is entered.
        /// </summary>
        /// <param name="eventArgs">The event args.</param>
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            base.OnEntry(eventArgs);
            // Open a new transaction scope with the specified options.
            eventArgs.InstanceTag = new TransactionScope(this._transactionScopeOption);
        }
        /// <summary>
        /// Executed when a method is left.
        /// </summary>
        /// <param name="eventArgs">The event args.</param>
        public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
            base.OnExit(eventArgs);
            // Get the transaction.
            TransactionScope transactionScope = (TransactionScope)eventArgs.InstanceTag;
            // If no exception has been thrown, commit the transaction.
            if(eventArgs.Exception == null)
            {
                transactionScope.Complete();
            }
            // Dispose the transaction scope.
            transactionScope.Dispose();
        }
    }
}

Der entscheidende Punkt innerhalb dieses Attributs ist die Verwendung der Eigenschaft InstanceTag der MethodExecutionEventArgs, die ein beliebiges Objekt aufnehmen und an die aufgerufene Methode anhängen kann – auf diese Art steht der neu erzeugte TransactionScope auch beim Beenden der Methode wieder zur Verfügung.

ClientHttpStack in Silverlight 3

Montag, 13. Juli 2009, 19:00 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Vor einigen Tagen wurde Silverlight 3 endlich veröffentlicht. Wer sich dafür interessiert, welche Neuerungen die dritte Version enthält, wird in dem entsprechenden Blogeintrag Silverlight 3 Released! What is new / changed? von Tim heuer fündig.

Ein Feature, das – so scheint es mir – nur am Rande wahrgenommen wird, ist der neue Netzwerkstack von Silverlight 3. Tim Heuer schreibt dazu:

That’s right a new networking stack option is being introduced for Silverlight 3 and is new since beta. Silverlight has always leveraged the browser HTTP stack and will continue if you don’t care. In Silverlight 3 we’ve introduced the Client HTTP stack as an option as well. You must opt-in to use the client HTTP handling. This gives you the ability to go more than just GET/POST (i.e., more REST-ful verbs), using response status codes/headers, etc.

Prinzipiell klingt das zunächst äußerst vielversprechend. Immerhin war es bislang nur über Umwege und ausgesprochen umständlich möglich, andere HTTP-Verben als GET und POST zu verwenden.

War bislang nämlich Unterstützung für PUT und DELETE erforderlich, gab es die beiden folgenden Alternativen:

  • Zum einen konnte auf das auch in JavaScript verfügbare XmlHttpRequest-Objekt des Browsers zurückgegriffen werden. Diese Variante ist in ihrer Ausführung allerdings verhältnismäßig langsam, da der Zugriff auf dieses Objekt mit Hilfe der JavaScript-Bridge von Silverlight stattfinden muss. Außerdem sind die Sicherheitseinstellungen von JavaScript derart restriktiv, dass eine Kommunikation mit anderen Domains gänzlich verhindert wird – hier hilft also auch keine Client Access Policy auf dem jeweiligen Server weiter.
  • Zum anderen konnte das HTTP-Verb getunnelt werden, indem einer POST-Anfrage der Header X-HTTP-Method-Override mitsamt dem gewünschten HTTP-Verb mitgegeben wurde. Leider wird dieser Header von einigen Webservern nicht ausgewertet, so dass diese Variante in der Regel ebenfalls nicht in Frage kommt.

Insofern scheint der neue Stack sehr vielversprechend zu sein, löst er schließlich die bisherigen Probleme von Silverlight, REST-basierte Webservices anzusprechen. In diesem Sinne schreibt auch Tim Heuer des weiteren dazu:

Hopefully this new client stack will give you some flexibility in some situations beyond what the browser networking stack was providing.

Leider wurden die hohen Erwartungen in den ersten Tests des neuen Stacks gedämpft, denn es gibt zwei gravierende Probleme:

  • Der ClientHttpStack unterstützt keinen Proxy, zumindest werden die entsprechenden Einstellungen des Browser ignoriert, und es gibt keinen offensichtlichen Weg, einem mit Hilfe dieses Stacks erzeugten Request einen Proxy anzugeben. Die Konsequenz ist dementsprechend, dass eine Silverlight-Anwendung je nach verwendetem Stack mit oder ohne Proxy arbeitet, was in der Praxis bedeutet, dass einige Requests schlichtweg verschluckt werden können.
  • Außerdem unterstützt der ClientHttpStack auch nicht die Weitergabe von Credentials, so dass zumindest die in HTTP integrierten Verfahren zur Authentifizierung nicht genutzt werden können.

Alles in allem ist die Einführung eines eigenen Stacks durchaus ein Schritt in die richtige Richtung, allerdings wirkt die Art, wie dies in Silverlight 3 geschehen ist, weder ausgereift noch durchdacht – de facto ist der ClientHttpStack in dieser Form in der Praxis nicht verwendbar.

Als Workaround bietet sich an, die Kommunikation mit dem Server über einen Wrapper laufen zu lassen, der nur die Verben GET und POST nutzt, der dann seinerseits die eigentlichen REST-Anfragen aufbaut und an den entsprechenden Webservice weiterleitet.

Mit Silverlight auf den Client zugreifen

Montag, 29. Juni 2009, 14:45 Uhr
Permalink | Kommentare (1) | Kommentare als RSSRSS

Wie kann man aus einer in Silverlight geschriebenen Webanwendung trotz Sandbox auf den zu Grunde liegenden Client zugreifen, um beispielsweise den aktuell angemeldeten Windows-Benutzer zu ermitteln, oder auf spezielle Hardware wie Smartcard-Lesegeräte zuzugreifen?

Die kurze Antwort: Es geht nicht. Otto Fischer schreibt dazu in seinem aktuellen Blog-eintrag Mit Silverlight trotz Sandbox auf Windows-Daten zugreifen. Wahr oder falsch?:

Auf der angegebenen Seite steht dann tatsächlich: “Silverlight – Unter Windows den angemeldeten Benutzer ermitteln”. Das liest sich so, als meine der Autor, er könne den “angemeldeten WINDOWS-Benutzer” ermitteln. Das würde ich wirklich gerne sehen.

Was dann aber gezeigt wird, ist der Zugriff auf HttpContext.Current.User. Och. Das ist aber nicht der Windows-Benutzer. Das ist der Benutzer der Web-Applikation.

Dabei bezieht er sich auf den aktuellen Blogeintrag von Gregor Biswanger, der unter dem Titel Silverlight – unter Windows den angemeldeten Benutzer ermitteln genau dies verspricht. Wie Otto schon korrekt angemerkt hat, ermittelt Gregor aber nicht den aktuell angemeldeten Windows-Benutzer, sondern den der Webanwendung – und das sind unter Umständen zwei verschiedene Benutzer.

Die Frage, die im Raum steht, ist natürlich: Ist es überhaupt möglich, aus Silverlight heraus auf den Client zuzugreifen? Wie oben schon erwähnt, lautet die kurze Antwort, dass dies nicht möglich ist.

Die Begründung liegt schlichtweg in der Sandbox, in der Silverlight ausgeführt wird, und die sämtliche Zugriffe von dem zu Grunde liegenden Client abstrahiert.

Allerdings gibt es zwei potenzielle Workarounds:

Erstens kann die JavaScript-Bridge von Silverlight genutzt werden, um aus der Sandbox auszubrechen und JavaScript auszuführen. Zwar läuft dieses JavaScript nicht mehr innerhalb der Silverlight-Sandbox, sehr wohl aber noch in der JavaScript-Sandbox des Browsers.

Die Nutzung der JavaScript-Bridge bietet sich an, wenn Informationen ermittelt werden sollen, die zwar im Browser zur Verfügung stehen, nicht jedoch in Silverlight. Hierfür seien als Beispiel die aktuellen DPI-Einstellungen genannt, die zur Entwickler einer high DPI-fähigen Anwendung erforderlich sind.

An Stelle der Silverlight-Bridge können auch die von Gregor angeführten Startparameter verwendet werden, doch geht hierbei sämtliche Dynamik verloren: Werte können nur beim Start übergeben werden, danach verhalten sie sich statisch. Auf sich ändernde Umgebungsbedingungen zu reagieren ist mit dieser Technik also nicht möglich.

Zweitens kann auf dem Client zusätzlich zu der Silverlight-Anwendung eine echte Desktopanwendung installiert werden, die beispielsweise als Dienst oder im Systemtray laufen kann. Diese Anwendung hat – da sie nicht an den Browser oder das Silverlight-Addin gebunden ist – vollen Zugriff auf die lokalen Ressourcen, soweit die jeweiligen Benutzerrechte dies gestatten.

Bringt diese Anwendung zusätzlich noch über einen integrierten Webserver, oder wird sie im Rahmen eines leichtgewichtigen Webservers wie beispielsweise XSP ausgeführt, können ihre Dienste problemlos per HTTP in Anspruch genommen werden, was aus einer Silverlight-Anwendung wiederum kein Problem darstellt.

Die Beantwortung der Frage, ob es möglich ist, aus Silverlight heraus auf den Client zuzugreifen, hängt also vom jeweiligen Kontext ab, und davon, welche zusätzlichen Komponenten und Kommunikationskanäle genutzt werden dürfen.

Für und wider C# 4.0

Mittwoch, 10. Juni 2009, 00:56 Uhr
Permalink | Kommentare (17) | Kommentare als RSSRSS

Jede in den vergangenen sieben Jahren erschienene Version von C# konzentrierte sich auf einen speziellen Bereich:

  • C# 1.0 war auf Objektorientierung ausgelegt und bildete das äußerst konsistente objektorientierte Modell von .NET auf die eigentliche Sprache ab.
  • C# 2.0 enthielt als wesentliche Neuerung generische Typen. Alle weiteren Features waren letztlich nur für die konsistente Umsetzung dieses Themas erforderlich.
  • C# 3.0 verfügte mit LINQ über die Möglichkeit, nahezu beliebige Datenquellen in einer ausgesprochen effizienten Art auf deklarative Weise abzufragen.

Das besondere an all diesen Versionen von C# war, dass eine bemerkenswerte Konsistenz gegeben war – nicht nur innerhalb der einzelnen Versionen, sondern auch versionsübergreifend.

Auf der PDC 2008 hat Microsoft im Oktober des vergangenen Jahres die kommenden Neuerungen von C# 4.0 vorgestellt. Auch diese Version wird wieder von einem Thema geprägt sein: Der Integration von C# mit dynamischen Sprachen und COM.

Anders als in den vergangenen Versionen fallen die Änderungen dieses Mal allerdings verhältnismäßig kompakt aus, Angekündigt sind derzeit sogar nur vier Features, wobei sich daran bis zur finalen Version von C# 4.0 voraussichtlich auch nichts mehr ändern wird:

  • dynamic-Schlüsselwort: Das Schlüsselwort dynamic erlaubt die Instanziierung von dynamischen Typen, deren Typ erst während der Ausführung festgelegt wird – und der sich sogar ändern beliebig kann. Intern basiert dynamic auf object, erweitert dieses aber um Late Binding.
  • Optionale Parameter: Optionale Parameter ermöglichen es, einzelne Parameter einer Methode bei deren Aufruf auslassen zu können, und statt dessen auf Standardwerte zurückzugreifen. Letztlich erspart sich der Entwickler die Erzeugung verschiedener überladener Varianten einer Methode.
  • Benannte Parameter: Benannte Parameter dienen dazu, einzelne Parameter einer Methode bei deren Aufruf gezielt, das heißt, unabhängig von ihrer Position in der Parameterliste spezifizieren zu können. Dieses Feature ergibt insbesondere in der Kombination mit optionalen Parametern Sinn.
  • Ko- und Kontravarianz: Durch eine verbesserte Ko- und Kontravarianz werden die Möglichkeiten zur Spezialisierung beziehungsweise Generalisierung verbessert. So kann beispielsweise nun List<string> als Ersatz für List<object> verwendet werden, da string von object ableitet – obwohl dies für List<string> und List<object> nicht gilt.

Man mag es bedauerlich finden, dass C# 4.0 nur diese wenigen Änderungen enthält. Potenzial für weitere Aspekte wäre durchaus vorhanden gewesen. Andererseits spricht es für die Reife einer Sprache, wenn eine gewisse Sprachstabilität erreicht wurde und nicht jede neue Version eine Unmenge an weiteren Features enthält.

Wie bereits bei den meisten Neuerungen von C# 3.0 bemüht sich Microsoft auch bei C# 4.0, darauf hinzuweisen, dass dessen Neuerungen nicht für den tagtäglichen Gebrauch gedacht sind, sondern explizit nur der vereinfachten Interoperabilität zu dynamischen Sprachen und vor allem COM dienen.

Bei dem Schlüsselwort dynamic mag dies ohne weitere Erklärung einleuchten, doch wie steht es um die anderen Features?

Ko- und Kontravarianz sind mit Sicherheit nicht nur das am wenigsten beachtete, sondern auch am wenigsten verstandene Merkmal – zugleich jedoch auch das leistungsfähigste. Schließlich ermöglichen diese beiden Aspekte eine deutlich bessere Verwendung von Schnittstellen und implementierenden, aber spezialisierenden Klassen.

Da für die Verwendung dieser erweiterten Ko- und Kontravarianz jedoch auch zwei neue Schlüsselwörter erforderlich sind, ist fraglich, ob sich die Verwendung dieses Feature all zu weit verbreiten wird. Dies wäre schade, allerdings auch nicht tragisch.

Kritisch sind jedoch die beiden übrigen Features: Optionale und benannte Parameter. Das Problem mit diesen ist, dass man zu schnell glaubt, diese verstanden zu haben, ohne es jedoch wirklich getan zu haben. Das Problem liegt in der Implementierung der Standardwerte von optionalen Parametern.

Prinzipiell ist es möglich, optionale Parameter als syntaktisch kompakten Ersatz für die Erzeugung verschiedener überladener Methoden zu verwenden. Für den Aufrufer macht dies keinen Unterschied: Er kann so oder so eine Methode mit weniger Parametern aufrufen, als es für den Aufruf der primären Methode erforderlich wäre.

Allerdings stellt sich die Frage, wie die Standardwerte der fehlenden optionalen Parameter ermittelt werden. Bei einer klassischen Überladung ist die Antwort offensichtlich: Hier stecken die Standardwerte in den überladenen Methoden. Wird ein Standardwert in einer dieser Methoden geändert, wirkt sich das automatisch auf alle Verwender aus – selbst wenn diese sich in einer anderen Assembly befinden, die nicht neu kompiliert wird.

Anders sieht dies jedoch bei der Verwendung optionaler Parameter aus, wie Bernd Marquardt, Neno Loje und ich herausgefunden haben: Hierbei werden die jeweiligen Standardwerte beim Kompilieren in den Code des Verwenders (!) kopiert. Das heißt, dass sich eine Änderung eines Standardwertes eines optionalen Parameters nur dann auf den Verwender durchschlägt, wenn auch dieser neu kompiliert wird.

Sofern sich der Verwender in der gleichen Assembly wie die aufzurufende Methode befindet, geschieht dies auch automatisch. Ist dies jedoch nicht der Fall, wird die betroffene Methode zukünftig mit potenziell falschen oder sogar ungültigen Parametern aufgerufen.

Kritisch hieran ist, dass man einem Methodenaufruf nicht ansieht, ob intern eine überladene Methode oder eine Methode mit optionalen Parametern aufgerufen wird. Es handelt sich also um eine Verletzung des Uniformitätsgesetzes der Informatik, das besagt, dass semantisch verschiedene Dinge auch über eine verschiedene Syntax verfügen sollten.

Was folgt nun aus all diesem? Optionale Parameter sind – wie von Microsoft auch vollkommen korrekt erwähnt – nicht für den tagtäglichen Gebrauch gedacht. Statt dessen sollten sie nur dann Verwendung finden, um eine vereinfachte Interoperabilität zu Legacycode herzustellen – wie beispielsweise im Falle von COM. Man denke nur an die zahlreichen Type.Missing-Angaben bei zahlreichen Aufrufen in das API von Office.

Allerdings werden viele Entwickler optionale Parameter als komfortable Alternative zu überladenen Methoden sehen. Und genau dafür sind optionale Parameter eben nicht gedacht.

Sofern man also weder mit dynamischen Sprachen noch mit COM hantiert, bleibt als einzige relevante Neuerung von C# 4.0 schließlich die Verbesserung von Ko- und Kontravarianz.

Exceptions im Konstruktor

Mittwoch, 22. April 2009, 11:40 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Vor einigen Tagen wurde ich gefragt, ob man in .NET Exceptions innerhalb eines Konstruktors werfen dürfe, und wenn ja, was dabei zu beachten sei. Die Antwort auf diese Frage lautet wie so oft: Es kommt darauf an.

Um diese Frage beantworten zu können, muss zunächst zwischen normalen und statischen Konstruktoren unterschieden werden. Während erstere dazu dienen, ein Objekt zu initialisieren, sind letztere für Typen zuständig, weshalb sie häufig auch als Typeninitialisierer bezeichnet werden.

Ob nun innerhalb eines Konstruktors Exceptions geworfen werden dürfen, hängt davon ab, um welchen Typ es sich handelt:

Statische Konstruktoren

Statische Konstruktoren werden von der CLR garantiert nur ein einziges Mal ausgeführt, nämlich vor der ersten Verwendung des dazugehörigen Typen (zugegeben, es ist über Umwege tatsächlich möglich, einen statischen Konstruktor mehrfach auszuführen, aber in 99,9% der Fälle findet die Ausführung nur ein Mal statt).

Zudem muss ein statischer Konstruktor vollständig und erfolgreich durchlaufen werden, um einen Typ verwenden zu können. Die Konsequenz liegt auf der Hand: Wird innerhalb eines statischen Konstruktors eine Exception geworfen, kann der Konstruktor nicht vollständig und erfolgreich durchlaufen werden, was wiederum verhindert, dass der Typ genutzt werden kann.

Kurzum: Wird innerhalb eines statischen Konstruktors eine Exception geworfen, kann dieser Typ nicht mehr verwendet werden – es sei denn, die AppDomain wird neu gestartet.

Instanzkonstruktoren

Instanzkonstruktoren verhalten sich im Prinzip ähnlich: Auch diese müssen vollständig und erfolgreich durchlaufen werden, damit das zugehörige Objekt verwendet werden kann. Wird innerhalb eines solchen Konstruktors eine Exception geworfen, gibt der new-Operator keine Referenz auf das gerade erzeugte Objekt zurück, und dieses kann im weiteren Verlauf nicht verwendet werden.

Im Unterschied zu Typeninitialisierern können Instanzkonstruktoren allerdings beliebig oft aufgerufen werden, indem jeweils ein neues Objekt erzeugt wird: Schlägt die Initialisierung also fehl, genügt es, ein neues Objekt des gleichen Typs zu instanziieren, um den Konstruktor erneut auszuführen.

Insofern ist es durchaus zulässig und sogar guter Stil, Exceptions innerhalb von Instanzkonstruktoren zu werfen.

Besonderheiten

Dennoch gilt es, beim Werfen von Exceptions innerhalb von Instanzkonstruktoren zwei Dinge zu beachten:

  • Falls die zugehörige Klasse über einen Finalizer verfügt, muss dieser gegebenenfalls mit partiell initialisierten Objekten zurecht kommen und in jedem Fall unverwaltete Ressourcen wieder sauber freigeben.
  • Da im Falle einer Exception keine Referenz auf das neu erzeugte Objekt an den Aufrufer zurückgegeben wird, kann auf dem Objekt kein Dispose aufgerufen werden. Mit anderen Worten: Die Verwendung von using schlägt in einem solchen Fall fehl.

Auf Application.Start in einem HttpModule reagieren

Freitag, 20. März 2009, 17:58 Uhr
Permalink | Kommentare (3) | Kommentare als RSSRSS

In ASP.NET ist es mit Hilfe der Datei Global.asax möglich, auf anwendungsweite Ereignisse zu reagieren. Dazu zählen unter anderem:

  • Start der Anwendung
  • Start einer Session
  • Ende einer Session
  • Ende der Anwendung

Allerdings handelt es sich bei diesen Ereignissen um keine Ereignisse im eigentlichen Sinne, wie man sie ansonsten aus C# kennt. Statt dessen ist der Aufruf der jeweiligen ereignisbehandelnden Methode in der Global.asax in der Engine von ASP.NET hart verdrahtet.

Während es bei anderen Ereignissen, wie beispielsweise dem Start eines Requests, problemlos möglich ist, die Behandlung in ein HttpModule zu verschieben, ist es mit diesen vier Ereignissen also nicht möglich – es gibt schlichtweg kein Ereignis, an das man das HttpModule anhängen könnte.

Doch zumindest für Application.Start gibt es einen Workaround: Es genügt, einen leeres HttpModule zu erstellen, das keinerlei Funktionalität enthält, und dieses mit einem statischen Konstruktur auszustatten:

using System.Web;

namespace silkveil.net.Application
{
    /// <summary>
    /// Represents the application module that starts up the application.
    /// </summary>
    public class ApplicationModule : IHttpModule
    {
        /// <summary>
        /// Initializes the <see cref="ApplicationModule" /> type.
        /// </summary>
        /// <remarks>
        /// A static constructor is used since HTTP modules are not able to subscribe to the start
        /// event of an ASP.NET application. This is only possible in the Global.asax file. So, a
        /// basically useless module is used to have a static constructor available (which gets
        /// executed only once).
        /// </remarks>
        static ApplicationModule()
        {
            // TODO: Insert your initialization code here ...
        }

        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">
        /// An <see cref="T:System.Web.HttpApplication" /> that provides access to the methods,
        /// properties, and events common to all application objects within an ASP.NET application.
        /// </param>
        public void Init(HttpApplication context)
        {
            // At the moment, nothing needs to be done here.
        }

        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements 
        /// <see cref="T:System.Web.IHttpModule" />.
        /// </summary>
        public void Dispose()
        {
            // At the moment, nothing needs to be done here.
        }
    }
}

Da dieses HttpModule bei jedem Request aufgerufen wird, wird es auch beim ersten Request – also quasi direkt nach dem Start der Anwendung – aufgerufen. Zuvor wird jedoch der statische Konstruktor ausgeführt, so dass der Code zur Initialisierung der Anwendung hier platziert werden kann.

Da die CLR garantiert, dass ein statischer Konstruktor nur ein Mal pro Typ ausgeführt wird, lässt sich auf diese Art das gewünschte Verhalten mit wenig Aufwand nachbilden.

WCF ohne .svc

Donnerstag, 19. März 2009, 19:21 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Um in .NET einen WCF-Dienst auszuführen, muss prinzipiell lediglich eine Instanz der Klasse ServiceHost erzeugt werden, die anschließend das Hosting für den jeweiligen Dienst übernimmt.

Nahezu jedes Buch zu WCF enthält dementsprechend ein Beispiel, wie man einen einfachen Host für WCF-Dienste auf Basis der Konsole entwickelt. Auf Grund gewisser Anforderungen wie Verfügbarkeit, Skalierbarkeit und ähnlichem genügt ein solcher Host in der Praxis jedoch in den seltensten Fällen.

Da .NET erst ab der Version 4.0 über einen Application Server verfügen wird, stellt derzeit der IIS die beste Alternative dar, da dieser neben Webseiten auch WCF-Dienste hosten kann. Allerdings benötigt der IIS hierfür in jedem Fall eine .svc-Datei, die auf den eigentlichen Dienst verweist:

<% @ServiceHost language="C#" Debug="true" Service="MyService" CodeBehind="~/App_Code/MyService.cs" %>

Sofern man Wert auf ein einfaches Deployment legt und den WCF-Dienst in einer einzigen DLL bereitstellen will, die sämtlichen Code enthält, stößt man nun auf ein Problem: Es ist scheinbar unter WCF nicht möglich, die .svc-Datei ebenfalls in die DLL auszulagern – es müssen also immer zwei Dateien angefasst werden.

Doch es gibt eine Lösung für dieses Problem: ASP.NET stellt im Namensraum System.Web.Hosting die Klasse VirtualPathProvider zur Verfügung, mit deren Hilfe ein virtuelles Dateisystem implementiert werden kann.

Um also einen WCF-Dienst ohne .svc-Datei im IIS zu hosten, muss ein eigener VirtualPathProvider implementiert werden, der auf Anfragen nach der Datei Service.svc mit einer per Code erzeugten Variante reagiert.

Damit ein VirtualPathProvider funktioniert, müssen die beiden Methoden FileExists und GetFile überschrieben werden, die zudem prüfen müssen, ob der aktuelle Provider überhaupt zuständig ist – falls nicht, wird die Ausführung an den nächsten in ASP.NET registrierten Provider übergeben:

using System.Web.Hosting;
using System.Web;

namespace silkveil.net.Wcf
{
    /// <summary>
    /// Provides activation for a WCF service without the need for a .svc file.
    /// </summary>
    public class ServiceActivationProvider : VirtualPathProvider
    {
        /// <summary>
        /// Checks whether the specified path is a virtual one.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <returns><c>true</c> if the path is virtual; otherwise <c>false</c>.</returns>
        private static bool IsVirtualPath(string virtualPath)
        {
            // Check whether the path is virtual.
            virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
            return virtualPath == "~/Service.svc";
        }

        /// <summary>
        /// Gets a value that indicates whether a file exists in the virtual file system.
        /// </summary>
        /// <returns>
        /// <c>true</c> if the file exists in the virtual file system; otherwise, <c>false</c>.
        /// </returns>
        /// <param name="virtualPath">The path to the virtual file.</param>
        public override bool FileExists(string virtualPath)
        {
            // If the path is not virtual, delegate to the previous provider in the provider chain.
            if(!IsVirtualPath(virtualPath))
            {
                return this.Previous.FileExists(virtualPath);
            }

            // Otherwise, always return true.
            return true;
        }

        /// <summary>
        /// Gets a virtual file from the virtual file system.
        /// </summary>
        /// <returns>
        /// A descendent of the <see cref="T:System.Web.Hosting.VirtualFile" /> class that
        /// represents a file in the virtual file system.                
        /// </returns>
        /// <param name="virtualPath">The path to the virtual file.</param>
        public override VirtualFile GetFile(string virtualPath)
        {
            // If the file does not exist in this virtual path provider, delegate execution to
            // the previous provider in the provider chain.
            if (!IsVirtualPath(virtualPath))
            {
                return this.Previous.GetFile(virtualPath);
            }

            // Create a Service.svc file on the fly and return it to the caller.
            return new ServiceActivationFile(virtualPath);
        }
    }
}

Außer diesem Provider muss zusätzlich auch noch eine Klasse von VirtualFile abgeleitet werden, welche die eigentliche .svc-Datei erzeugt:

using System.IO;
using System.Web.Hosting;

namespace silkveil.net.Wcf
{
    /// <summary>
    /// Represents a virtual .svc file.
    /// </summary>
    public class ServiceActivationFile : VirtualFile
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceActivationFile" /> type.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        public ServiceActivationFile(string virtualPath) : base(virtualPath)
        {
        }

        /// <summary>
        /// Returns a read-only stream to the virtual resource.
        /// </summary>
        /// <returns>A read-only stream to the virtual file.</returns>
        public override Stream Open()
        {
            // Create a stream.
            Stream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);

            // Write to the stream.
            writer.Write("<%@ ServiceHost Language=\"C#\" Debug=\"false\" Service=\"silkveil.net.Wcf.Service\" %>");
            writer.Flush();

            // Reset the stream's pointer to its beginning and return the stream to the caller.
            stream.Seek(0, SeekOrigin.Begin);
            return stream;
        }
    }
}

Kompiliert man diese beiden Klassen mit in die DLL, die den WCF-Dienst erhält, genügt es für ein Deployment zukünftig, nur noch diese DLL zu verteilen. Auf diese Art ist es ein Leichtes, beispielsweise eine bestehende ASP.NET-Anwendung um einen WCF-Dienst zu erweitern – ohne mit einer .svc-Datei hantieren zu müssen.

Allerdings muss der VirtualPathProvider noch registriert werden, damit ASP.NET von seiner Existenz weiß: Dazu genügt es, diesen beim Start der Anwendung in der Datei Global.asax zu registrieren:

// Initialize the service activation provider.
HostingEnvironment.RegisterVirtualPathProvider(new ServiceActivationProvider());

Der entscheidende Punkt an dieser Vorgehensweise ist, dass Dateien, die von einem VirtualPathProvider in ASP.NET geladen werden, nicht – wie beispielsweise bei einem HttpHandler – direkt in den Ausgabestrom geschrieben werden, sondern dass sie zunächst noch von der ASP.NET-Engine verarbeitet werden.

Damit wäre es also auch möglich, an Stelle einer .svc-Datei eine beliebige andere Datei per Code bereitzustellen – .Webseiten, Usercontrols, … einzig Dateien, welche die gesamte Anwendung betreffen, bleiben außen vor, wie beispielsweise die Global.asax und die web.config.

Für alles andere gilt: Der Fantasie sind dabei keine Grenzen gesetzt …

Microkernel im Eigenbau

Freitag, 13. März 2009, 21:17 Uhr
Permalink | Kommentare (9) | Kommentare als RSSRSS

Ende November des vergangenen Jahres habe ich in Architektur lernen empfohlen, sich mit dem Einsatz eines Microkernels zu beschäftigen:

Auch dieses Thema fördert das Denken in Schnittstellen, allerdings auf einer anderen Ebene als die Entwurfsmuster - beziehen diese sich nämlich auf das Klassendesign, bezieht sich der Einsatz eines Microkernels auf das Komponentendesign einer Anwendung.

Außerdem habe ich behauptet, dass es allen bestehenden Microkerneln zum Trotz nicht schwierig sei, einen einfachen Microkernel selbst zu implementieren:

Zwar gibt es einige Projekte wie beispielsweise Unity, die man sich näher anschauen kann, allerdings ist ein einfacher Microkernel in wenigen Zeilen selbst implementiert, wobei der Lerneffekt dann allerdings weitaus größer ist.

Da ich seither immer wieder nach einem Beispiel gefragt werde, beschreibe ich nun hier, wie man einen eigenen Microkernel erstellt. Ich werde mich dabei auf das wesentliche Feature – nämlich die Instanziierung eines konkreten Typen an Hand eines gegebenen Contracts – beschränken, und Aspekte wie Fehlerbehandlung, Performance, Sicherheit und Konfigurierbarkeit außen vor lassen.

Letztlich sind folgende Schritte nötig, um einen eigenen lauffähigen Microkernel zu entwickeln:

  • Erzeugen einer Konfiguration, die das Mapping von Contracts auf konkrete Typen übernimmt. Es bietet sich an, diese Konfiguration in einer XML-Datei abzulegen, da diese erstens dank LINQ to XML mit wenig Aufwand ausgelesen werden kann, und dies zum anderen ermöglicht, die Konfiguration des Microkernels zu ändern, ohne das Projekt neu kompilieren zu müssen.
  • Instanziieren des für einen gegebenen Contract definierten konkreten Typs. Dies geschieht im Wesentlichen mit Hilfe der Activator-Klasse von .NET, wobei so wohl in das Projekt integrierte Assemblies wie auch zusätzliche Assemblies unterstützt werden sollen.

Zunächst muss also eine Konfiguration erzeugt werden, die das Mapping von einem Contract auf einen konkreten Typen übernimmt. Dazu dient folgende Struktur, die das Grundgerüst einer entsprechenden XML-Datei darstellt:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="" type="" />
  </mappings>
</serviceLocator>

Als einfaches Beispiel soll eine Instanz des Typs MemoryStream im Namensraum System.IO verwendet und auf die Schnittstelle IDisposable gemappt werden.

Hierzu müssen lediglich die Namen der beiden Typen in der XML-Datei angegeben werden, wobei diese vollqualifiziert  – also einschließlich des jeweiligen Namensraums – angegeben werden müssen:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="System.IDisposable" type="System.IO.MemoryStream" />
  </mappings>
</serviceLocator>

Im nächsten Schritt wird eine statische Klasse ServiceLocator erzeugt, die über die folgenden Methoden verfügt:

  • Einen statischen Konstruktur: Dieser ist dafür zuständig, die Konfiguration aus der XML-Datei auszulesen, und die dort angegebenen Namen der Contracts und Typen in jeweils eine Instanz der Type-Klasse zu überführen, und diese Zuordnungen danach in einem Dictionary abzuspeichern.
  • Eine statische Methode namens GetService: Diese Methode hat als einzige Aufgabe, den konkreten Typ für den angeforderten Contract aus dem Dictionary zu ermitteln, diesen zu instanziieren und entsprechend gecastet an den Aufrufer zurückzugeben.

Insgesamt ergibt sich daraus die folgende Grundstruktur für die Klasse ServiceLocator:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace CherryFlavored.NET.Core.Kernel
{
    /// <summary>
    /// Represents a service locator.
    /// </summary>
    public static class ServiceLocator
    {
        /// <summary>
        /// Contains the mappings from contracts to types.
        /// </summary>
        private static Dictionary<Type, Type> _mappings;

        /// <summary>
        /// Initializes the <see cref="ServiceLocator" /> type.
        /// </summary>
        static ServiceLocator()
        {
        }

        /// <summary>
        /// Gets an instance for the specified contract.
        /// </summary>
        /// <typeparam name="TContract">The type of the contract.</typeparam>
        /// <returns>An instance that matches the specified contract.</returns>
        public static TContract GetService<TContract>() where TContract : class
        {
        }
    }
}

Um die Konfigurationsdatei auszulesen und das Dictionary entsprechend zu erzeugen, bietet sich – wie oben bereits erwähnt – die Verwendung von LINQ to XML an:

// Load the configuration.
_mappings =
    (from mapping in
         XElement.Load("ServiceLocator.xml").Element("mappings").Elements("mapping")
     select
         new KeyValuePair<Type, Type>(
             Type.GetType(mapping.Attribute("contract").Value),
             Type.GetType(mapping.Attribute("type").Value))).ToDictionary(
        kvp => kvp.Key, kvp => kvp.Value);

Die Instanziierung des konkreten Typs in der Methode GetService ist sogar noch wesentlich einfacher, und kann in einer einzigen Zeile erfolgen:

// Instantiate the type and return it to the caller.
return (TContract)Activator.CreateInstance(_mappings[typeof(TContract)]);

Um nun tatsächlich eine Instanz der Klasse MemoryStream an Hand ihres Contracts zu erzeugen, genügt eine Referenz auf den Microkernel und folgende Zeile:

// Try to get an instance for an IDisposable type.
IDisposable memoryStream = ServiceLocator.GetService<IDisposable>();

Dieser Microkernel kann nun bereits verwendet werden, um Komponenten innerhalb eines Projektes zu entkoppeln. Häufig ist der zu instanziierende Typ aber gar nicht Bestandteil des eigentlichen Projektes, sondern liegt in einer eigenständigen Assembly, die potenziell erst nach dem Kompilieren zu einer bereits erfolgten Installation beigefügt wird.

Alles, was zur Kompilierungszeit in einem solchen Fall zur Verfügung steht, ist der Contract – der eigentliche Typ wird unabhängig davon bereitgestellt, um beispielsweise Addins zu ermöglichen.

Um ein solches Szenario zu unterstützen, muss der Microkernel also eine Möglichkeit bieten, einen Typ auch aus einer Assembly zu laden, die neben dem eigentlichen Projekt losgelöst im Dateisystem liegt, ohne dass sie bereits zur Kompilierzeit verfügbar war.

Das schöne ist: Dieses Szenario wird bereits unterstützt. Alles, was hierfür notwendig ist, ist, in der Konfigurationsdatei neben der Klasse auch noch die enthaltende Assembly anzugeben, wie folgendes Beispiel an Hand der Post-Klasse von BlogEngine.NET zeigt:

<?xml version="1.0" encoding="utf-8" ?>
<serviceLocator>
  <mappings>
    <mapping contract="System.IDisposable" type="BlogEngine.Core.Post, BlogEngine.Core" />
  </mappings>
</serviceLocator>

Mehr ist für einen eigenen Microkernel prinzipiell nicht notwendig. Natürlich empfiehlt es sich, diesen für einen produktiven Einsatz noch an etlichen Stellen zu optimieren und um weitere Funktionen zu ergänzen. Die grundlegende Funktionsweise dürfte aber klar geworden sein.

[Flags] – wozu?

Sonntag, 8. März 2009, 22:22 Uhr
Permalink | Kommentare (0) | Kommentare als RSSRSS

Die gängige Literatur wie auch entsprechende Webseiten zu C# und .NET empfehlen, eine Enumeration mit dem Attribut [Flags] zu kennzeichnen, sofern es sich bei dieser Enumeration um ein sogenanntes Bitfeld handelt – also um eine Enumeration, deren Werte in verknüpfter Form einen gültigen Zustand darstellen.

Eine häufig geäußerte Frage ist jedoch – welchen Zweck erfüllt dieses Attribut? Denn weder erspart es die Zuweisung geeigneter Zahlenwerte an die einzelnen Werte der Enumeration, noch beeinflusst es die Kombinierbarkeit der einzelnen Werte mit Hilfe bitweiser Operatoren in spürbarer Form.

Einzig die Repräsentation als Zeichenfolge, die mit Hilfe der ToString-Methode gebildet wird, verhält sich unterschiedlich. Doch welche Bedeutung hat das [Flags]-Attribut wirklich?

Laut MSDN besagt das [Flags]-Attribut:

Indicates that an enumeration can be treated as a bit field; that is, a set of flags.

Außerdem heißt es dort im Abschnitt Remarks:

Bit fields are generally used for lists of elements that might occur in combination, whereas enumeration constants are generally used for lists of mutually exclusive elements. Therefore, bit fields are designed to be combined with a bitwise OR operation to generate unnamed values, whereas enumerated constants are not. Languages vary in their use of bit fields compared to enumeration constants.

Dies bedeutet, dass es letztlich der jeweiligen Sprache obliegt, ob sie bitweise Operationen auf Enumerationen ohne gesetztes [Flags]-Attribut zulässt oder abweist. C# und Visual Basic lassen dies zu. In diesen Sprachen bewirkt das [Flags]-Attribut also wirklich nur eine Änderung im Verhalten der ToString-Methode. In anderen Sprachen könnte dies aber potenziell anders sein.

Diese Interpretation deckt sich auch mit einer Aussage des MVPs Jon Skeet, die er in einer Diskussion zu diesem Thema gemacht hat:

Not in C#, but another language *may* decide to only allow the & and | operators to work with enums declared as Flags.

Schlussendlich bedeutet das also, dass es für eine saubere Implementierung im Sinne der sprachlichen Interoperabilität notwendig und sinnvoll ist, das [Flags]-Attribut wie vorgeschlagen zu verwenden – obwohl man beispielsweise in C# und Visual Basic darauf verzichten könnte.