ASP.NET, EF4 und Threads: Klappt das eigentlich?

Seit einiger Zeit arbeitete ich mich in die Welt der Programmierung von Web-Applikationen ein, mit Hilfe von ASP.NET MVC2, unter Verwendung von Entity Framework 4 (EF4) für den Datenbank-Zugriff.

Es ging eine ganze Weile, bis mir auffiel, dass in allem Demo-Code und in allen Demo-Projekten wie etwa dem Nerd Dinner nirgends die Rede war von Datenkonsistenz-Problemen, die es geben kann bei Verwendung von ASP.NET und EF4. ASP.NET arbeitet per se mit Multi-Threading, und deshalb können die Datenbankzugriffe ganz schön durcheinandergehen, wenn man auf einem Web-Server eine anständige Menge an Zugriffen hat. Zudem bringt die Verwendung von EF4 mit sich, dass in einem ObjectContext-Objekt eine Menge Daten zwischengespeichert werden können. Die Gefahr, dass sich mehrere Nutzer meiner Web-Applikation gegenseitig ihre Änderungen überschreiben, schien mir real zu sein.

Heute bin ich deshalb dieser Frage auf den Grund gegangen und dabei auf eine kleine Überraschung gestossen.

Multi-Threading in ASP.NET findet statt auf der Ebene der Requests: 1 Request wird jeweils in einem eigenen Thread ausgeführt. In ASP.NET MVC2 werden Controllers pro Request jeweils neu erzeugt; wenn man also wie in den Samples vorgegeben einen EF4-Objekt-Kontext als normale Property der Controller-Klasse implementiert, leben diese Objekte nur recht kurz, womit sich auch das Risiko von Konflikten deutlich reduziert.

Nebenbei bemerkt: Solange man nicht selbst Threads erzeugt in seiner MVC-Applikation, sondern nur dem „automatischen“ Threading von ASP.NET unterworfen ist, hat man also kein Problem damit, dass ObjectContext wie viele andere Framework-Klassen nicht thread-safe ist. Man darf nur nicht auf die Idee kommen, ein solches Objekt static zu deklarieren in seinem Controller!

Soweit, so gut, aber das Risiko von Konflikten sinkt damit natürlich nicht auf Null.

Die erwähnte Überraschung (zumindest für mich) fand sich schliesslich in diesem MSDN-Artikel: Per Default prüft EF4 bei Datenveränderungen nicht auf irgendwelche Optimismus-Verletzungen, sondern überschreibt einfach munter mit den aktuellen Daten in den Entities.

Mir persönlich scheint das ein etwas fragwürdiger Default zu sein, aber zum Glück kann man ihn relativ einfach überstimmen: Auf der Ebene einzelner Properties in einem EF4-Datenmodell ändert man einfach die Eigenschaft Concurrency Mode weg vom Default None auf Fixed. Die korrekte Behandlung einer OptimisticConcurrencyException, die es dann bei SaveChanges() geben kann, wird z.B. in diesem MSDN-Artikel erläutert. Wieso allerdings dieser Modus „fixed“ heisst, hat sich mir bisher nicht erschlossen…

Wenn’s etwas komplizierter wird und man z.B. einen eigenen Datenzugriffs-Layer in seiner Applikation implentieren will, findet man hier gute Erläuterungen zu Varianten, wie man die „Lebenszeit“ von ObjectContext-Objekten organisieren kann.

Veröffentlicht in Allgemein. Schlagwörter: , , . 1 Comment »

Visual Studio Refactoring und Code in .aspx

Als ich vor ein paar Jahren die Refactoring-Befehle in Visual Studio kennenlernte, insbesondere das Rename-Kommando, hatte ich irgendwie das Gefühl, nach einem langem Aufenthalt in der Tool-Steinzeit endlich in der Moderne angekommen zu sein: Nie wieder der Horror von rein textuellem Search & Replace, wenn 2 ganz verschiedene Dinge gleich heissen und man nur eines davon umbenennen will.

Mit der Zeit denkt man gar nicht mehr so darüber nach: Ist doch selbstverständlich, den Texteditor mit dem Compiler zusammenzuschalten, der schliesslich ganz genau weiss, welche Referenz auf welches Objekt zeigt, ganz egal, wie viele Dinge rein textuell gesehen gleich heissen.

Als vor kurzem ein ASP.NET-MVC2-Projekt nach einem Refactoring mit Fehlern in .aspx-Files nicht mehr kompilieren wollte, dachte ich zuerst an eine Fehlbedienung meinerseits oder an einen nur sporadisch auftretenden Bug in Visual Studio 2010, aber eine Recherche heute brachte schliesslich Klarheit: Es herrscht wieder Steinzeit. Das Refactoring in VS2010 ist nicht schlau genug, bei Rename auch Code-Stücke in .aspx-Files zu erfassen.

Man findet dieses Problem im Internet, z.B. in dieser Frage auf Stack Overflow, aber eine eigentliche Empörung über dieses klaffende Loch im System scheint es interessanterweise nicht zu geben. (Intellisense funktioniert auch in .aspx-Files, irgendwie, aber mit einem Fehlen jenes Features wäre Microsoft wahrscheinlich nicht durchgekommen bei der Entwickler-Gemeinde.)

Zum Glück gibt es Leute, welche sich solcher Probleme annehmen, und Tools wie ReSharper programmieren, welche die Welt sozusagen wieder geraderücken.

Allerdings droht bereits neues Ungemach: Leute scheinen sich immer wieder neue Spielarten von Syntax auszudenken, wie man am besten und elegantesten C#-Code mit HTML mischt, nicht zuletzt Microsoft selbst mit der neuen Razor View Engine für ASP.NET MVC3. Und diese neuen Spielarten überfordern dann Tools wie den erwähnten ReSharper zunächst wieder, wie man etwa im Blog der Hersteller-Firma nachlesen kann.

Das ist wohl ein nicht zu vermeidendes Problem im Zuge der rasanten Weiterentwicklung, welche die ganze .NET-Welt mitmacht…

Veröffentlicht in Keine Kategorie. Schlagwörter: , . Leave a Comment »

Wie funktioniert ASP.NET-Inline-Code?

Seit den Anfangszeiten von ASP.NET vor vielen Jahren gibt es die Möglichkeit von Inline-Code, also die Möglichkeit, Code-Schnippsel in HTML einzustreuen. Hier ist ein kleines aktuelles Beispiel mit ASP.NET MVC 2, Teil einer Seite mit einer HTML-Tabelle, bei der die Zeilen mit Hilfe einer Schleife gebildet werden, die in C# programmiert ist:

<table>
    <% foreach (var item in Model) { %>
        <tr>
            <td>
                <%: Html.ActionLink("Edit", "Edit", new { id=item.Id }) %> |
                <%: Html.ActionLink("Delete", "Delete", new { id=item.Id })%>
            </td>
            <td>
                <%: item.Id %>
            </td>
            <td>
                <%: item.Title %>
            </td>
            <td>
                <%: String.Format("{0:g}", item.DateReleased) %>
            </td>
        </tr>
    <% } %>
</table>

Ich habe mich in der Vergangenheit schon öfters gefragt, wie genau ein solches buntes Gemisch von HTML und Code zur Ausführung kommt. Es wird ja wohl nicht so sein, dass irgendein Prozess den Text durchgeht und dabei jeweils fliegend zwischen einer Art „HTML-Modus“ und einer Art „Code-Modus“ wechselt, also quasi die Mischung nachvollzieht. Aber wie es geht es dann?

Am einfachsten sieht man, was läuft, wenn man auf irgendwelchen Code auf so einer Seite einen Breakpoint setzt und sich dann nach einem Break im Debugger genauer ansieht, wo man denn gelandet ist. Die Seiten einer Website mit Code werden durch dynamische Compilation in eine temporäre .NET-Assembly verwandelt, die typischerweise unter kryptischem Namen in einer kryptischen Directory abgelegt ist, bei mir z.B.

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\d0bb04eb\40bc3df1\App_Web_sm1qknju.dll

Pro Seite gibt es 1 Klasse, und in jeder Klasse ist eine Methode namens __RenderContent2() zuständig dafür, den Inhalt der Seite d.h. das reine HTML zu generieren. Dank dem IL-Disassembler ILDASM kann man sich auf einfache Weise die Details einer solchen Methode ansehen.

Die Lösung, wie genau die Mischung von Code und HTML realisiert wird, ist denkbar einfach und eigentlich wenig überraschend: Es wird alles zu Code. Was schon Code ist, bleibt natürlich Code, und alle verbleibenden Sequenzen von Zeichen vor, zwischen und nach Code-Stücken, die HTML darstellen, werden mit Hilfe von Aufrufen von System.Web.UI.HtmlTextWriter.Write ausgegeben.

Das sieht disassembliert dann z.B. so aus:

IL_0031: ldarg.1
IL_0032: ldstr „\r\n </td>\r\n“
IL_0037: callvirt instance void [mscorlib]System.IO.TextWriter::Write(string)

Nun zu wissen, „wie es geht“, bringt mir nicht unbedingt viel, aber immerhin habe ich jetzt kein schlechtes Gefühl mehr, wenn mir das ständige Abwechseln von kleinen HTML-Stücken und Code-Stücken zu dumm wird und ich dann Dinge schreibe wie:

<% Response.Write("<td>"+item.Id+"</td><td>"+item.Title+"</td><td>"+String.Format("{0:g}", item.DateReleased)+"</td>") %>

Schliesslich entspricht das mehr oder weniger dem, was technisch gesehen zur Ausführung kommt, wenn ich es als „Mischung“ hinschreibe. (Acht geben muss ich dabei allerdings darauf, dass ich den bei <%: automatisch inbegriffenen Aufruf von Server.HtmlEncode bei Bedarf selbst mache; siehe hierzu z.B. diesen Artikel.)

Veröffentlicht in Keine Kategorie. Schlagwörter: , , . Leave a Comment »