Pfad der aktuellen Assembly

In welchem Pfad befindet sich die aktuell verwendete Assembly? Bisher dachte ich immer AppDomain.CurrentDomain.BaseDirectory wäre ein verlässlicher Weg dies herauszufinden. Stimmt aber nicht, den dieser Pfad kann natürlich auch gesetzt werden, was zu unerwarteten Ergebnissen mit unterschiedlichen Test-Runnern führte ("R#" und der "Nunit GUI").

Der richtige Weg: Assembly.GetExecutingAssembly().CodeBase, das den ursprünglichen Speicherort der Assembly zurück gibt.


Externe JavaScript-Dateien in asynchronem Postback laden

Wenn man viel UI-Funktionalität haben will, braucht man hin und wieder ein paar Dateien Javascript, die diese enthalten – da kommt auf die Dauer einiges zusammen. Wenn man nun versucht, gleichzeitig die Seitengröße unter Kontrolle zu behalten, kommt man schnell auf die Idee, bestimmte JavaScript-Dateien erst dann zu laden, wenn die enthaltene Funktionalität tatsächlich auch benötigt wird.

Ein Schlüssel dazu ist die Methode ScriptManager.RegisterClientScriptInclude, aber leider nur einer. Der zweite, entscheidende Schlüssel ist der hier:

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

Diese Zeile muss am Ende jeder auf diesem Wege eingebundenen js-Datei stehen.

Ein ausführliches Beispiel habe ich hier eingestellt, kurz bevor ich dann die Lösung selbst gefunden habe. So ist es manchmal!

Happy coding!


.NET Build Performance optimieren!

In den letzten Wochen hatte ich die die Möglichkeit Strategien für Beschleunigung von Builds mit Visual-Studio 2008 und MsBuild zu untersuchen. Hier meine Ergebnisse.

1:) Multiprozessor Build

MSBuild-Multithreaded

Visual Studio unterstützt beim Build per Default nur eine Thread. Doch es geht mehr! Das Zauberwort heißt: "msbuild /m".

Detailiert wird das Problem und die Lösung von Scott Hanselman beschrieben:

2:) Einzelnes Output Directory und keine "lokalen Kopien".

Per Default werden Build Ergebnisse in /projekt/bin/<Target>/ kopiert. Alle abhängigen Assemblies werden per Default ebenso kopiert, also auch Build-Ergebnisse anderer Projekte.

Nehmen wir eine Beispiel-Solution mit 50 Projekten. Jedes Projekt hat ungefähr 10.000 Zeilen Code und nehmen wir an die 50 Projekte sind gerechtfertigt. Wenn jedes Projekt mehrfach von anderen Projekten referenziert wird und 10.000 Zeilen ohne .resx Dateien eine DLL Größe von ca.: 250kb ergeben und eine PDB Größe von ca.:500kb, dann ergeben sich, wenn jedes Projekt ungefähr 10x referenziert wird , 10x750kb*50 - also 375 MB zu kopierende Daten. Obwohl eigentlich nur 37MB kopiert werden müssten. Die Lösung für das Problem? Das Build in ein einzelnes Verzeichnis durchführen, ohne die Abhängigen immer wieder zu kopieren. Hierfür muss neben der Pfadangabe für das OutputDirectory folgende Einstellung vorgenommen werden:

Copy-Local

3:) Abhängigkeiten minimieren

Wenn I/O Zeiten (siehe 2) kein Problem sind und alle verfügbaren Kerne ausgelastet sind (siehe 1), dann ist der größte verbleibende Zeitfaktor das Auflösen von Abhängigkeiten.

Eine Analyse lässt sich mit dem MSBUILD-Profiler durchführen. 

MSBuild-Profiler

Lösung des Problems? Weniger Abhängigkeiten und Projekte zusammenfassen :-)

4:) .licx Dateien nicht Teil des Projekts

LC.exe ist langsam. Es lässt sich Build Zeit sparen, wenn Lizenz-Informationen nur bei Bedarf in die Assembly "gebaut" werden. .licxs Dateien sollten nicht eingecheckt werden und nicht Teil des Dev-Builds sein.

Was noch?

Solutions entkoppeln und Modular arbeiten? Klar :-) Ein schnelle Entwicklungsmaschine hilft, eine gute SSD Platte und ein schneller Prozessor helfen. Wenn man damit nicht mehr weiterkommt, dann gilt es die Solution zu zerlegen. Als Richtwert sollte ein Clean-Build von 500.000 Zeilen Code (Nettozeilen ohne Kommentare) unter einer Minute liegen.


Arten von Branches, Branch per Refactoring

Wir hatten gerade eine Diskussion zum Nutzen von “Feature-Branches”, “Long-Running Branches” und “Topic Branches”. Das Gespräch bezog sich auf die Nutzung mit GIT. Zunächst eine kurze Klärung der Begriffe:

  • “Feature-Branches”, für ein Feature wird eine neuer Entwicklungszweig genutzt
  • “Branch-per-Configuration”, es wird kein physischer branch erzeugt, sondern die Entwicklung erfolgt im Hauptzweig. Ein entwickeltes Feature läst sich per Konfiguration ein und ausschalten.
  • “Long-Running Branches”, Ein Branch der länger als ein paar Tage genutzt wird
  • “Topic Branch”, Ein Branch der kurzlebig ist. Kurzlebig sind ein paar Minuten oder Stunden.

Aus meiner Perspektive sind Feature-Branches und Long-Running-Branches - wenn möglich - zu vermeiden. Ideal ist sind “Branches per Configuration”, die bei der Entwicklung ein modulares Arbeiten und damit eine gute Architektur erzwingen. Aufwände für das mergen werden vermieden. “Topic Branches“ sind durchweg positiv, wenn sie lokal bleiben und nicht zu entfernten Repositories gepushed werden.

Branch per Refactoring

Es kam auch kurz das Gespräch auf das Thema “Branch per Refactoring” bzw. eine Refaktorisierung wurde als “Feature Branch” bezeichnet. Aus meiner Sicht deutet das auf einen CodeProcess-Smell hin. Die Refaktorisierung ist zu groß.  Refaktorisierungsschritte sollten klein sein und ein Arbeitsschritt direkt wieder in den Hauptentwicklungszweig einfließen können.


Monitoring-Nachrichten: Auswertung und Gruppierung (1)

Meine Monitoring-Inbox sieht heute Morgen so aus:

PA-Monitor

Die Signal vs. Noise-Ratio ist schlecht und mein Ziel ist es diese zu verbessern. Keine der Nachrichten erfordert eine unmittelbare Reaktion. Die meisten der Nachrichten sollen jedoch gelegentlich, stichprobenartig auswerten. Die Reports sollen weiterhin in der Inbox bleiben.

Die Alerts betreffen primär die letzten Event-Log Einträge. Hier enthalten sind Sicherheitsnachrichten, ausgelaufenen Sessions, etc. Wir sollten hier mal besser Filtern um unnötige Informationen nicht als Alert zu zeigen. Auf Programmierseite besteht jedoch keine dringender Handlungsbedarf.

Als Monitoring Werkzeug verwenden wir zur Zeit "PA-Server-Monitor" und sind damit auch sehr zufrieden. Wir Monitoren sowohl Webseiten über HTTP, als auch Server Interna über WMI. Hier interessiert uns am meisten der Eventlog, aber auch Leistungsdaten wir Arbeitsspeicher, CPU-Auslastung und verfügbarer Festplattenplatz werden überwacht.

Gruppierungskriterien

Einfließen in die Gruppierung sollen unsere "Eskalationstufen". Unsere Eskalationstufen würde wie folgt darstellen:

  1. "Unbekannt" Nachricht als Email, wird in regelmäßigen Intervallen ausgewertet
  2. "Dienst nicht beeinträchtigt" Nachricht als Email
  3. "Dienst gering beeinträchtigt" Nachricht als Email
  4. "Dienst stark beeinträchtigt" Nachricht SMS
  5. "Offline" - Nachricht SMS

Einfließen soll die Wichtigkeit des Dienstes. Für uns hängt die Dienstwichtigkeit am Kunden. Alle Kunden bekommen zwar eine gleich gute großartige Hosting und Monitoring-Qualtität - aber für einen Kunden der €22.50 im Jahr bezahlt, muss sich in einem eventuellen Fehlerfall, niemand nach einer SMS, morgens um 3 Uhr aus dem Bett lösen. 7Uhr sollte dann noch vollends reichen :-) Unser Wichtigkeiten sehen also so aus:

  1. Kunde A
  2. Kunde B
  3. Intern

Vorgehensweise

Nach dem die Eckdaten nun ungefähr klar sind, geht es daran eine Strategie zu entwickeln. Folgende Überlegung ist dabei zentral:

  • Alles was wir tun soll, soll als Prozess dem ganze Team nützen und an dritte zu übergeben sein.

Daraus könnte sich ergeben, dass ein zentraler Account die Beste Möglichkeit ist. Hier könnte alle Nachrichten und Filter-Regeln. für alle erreichbar umgesetzt werden.

(Da die das Definieren von Filtern und Regeln auf Textinhalte im PA-Monitor Relativ schwer ist und bei einigen Nachrichten in der Eskalationsstufe Stufe 1 eine Unschärfe durchaus gewünscht ist, probieren wir das Filtern im Nachrichtenclient durchzuführen.)

Lösungsansatz

Mehrere Lösungsansätze sind denkbar. Der Einfachheit halber konzentrieren wir uns auf den ersten guten (nicht der Erstbeste!) Lösungsansatz. Wenn dieser nicht befriedigend ist - suchen wir eine alternative.

Vorschlag: Wir nutzen einen zentralen Google-Email Account für Auswertung der Monitoring und Lognachrichten. Hier definieren wir Labels und Regeln, die unsere Eskaltions und Gruppierungskriterien abbilden.

So können wir gemeinschaftlich die Regeln weiterentwicklen und im Fall von Urlaub können wir für die Auswertung Rollen übertragen. GMails hat soweit mir bekannt ist, sehr leistungsfähige Filter und kann Multitagging (Labeling).

Wenn Mark sein Ok gibt, dann sind die nächsten zwei Schritte: 1:) Gmail Account für Monitoring einrichten und alle Nachrichten umleiten :-)

- Fortsetzung folgt -


“NEUE” ASP.NET AJAX Version (seit September)

Hatte das gar nicht mitbekommen, seit September gibt es eine neue ASP.NET AJAX Version. Änderungen lassen sich hier nachlesen: http://www.asp.net/AJAX/AjaxControlToolkit/Samples/Default.aspx. Auch interessant, das ASP.NET AJAX Toolkit ist jetzt Teil der ASP.NET AJAX Library.

Es wurden 20 Bugs gefixed und es gibt ein neues VS Projekt Template für Visual Studio. @Stefan, hätte Dir das geholfen?

Neu: Seadragon ist wirklich spannend. Wir könnten das sowohl für Produkt-Bilder als auch für Stellplatzkarten einsetzen.

Seadragon-Ajax

Neu: AsynFileUpload, vielleicht lohnt es bei Gelegenheit mal mit unserer Lösung zu vergleichen. Wobei in Opera der Upload-Button schon nicht funktionierte – gleich ein schlechtes Zeichen. Insgesamt würde ich mal vermuten, dass unsere Lösung in Speak-Lib deutlich besser ist :-)