Automatic deployment of an ASP.NET Web Application Project with TeamCity and MSBuild

by Oliver 21. January 2011 20:19

We recently updated one our largest project to use ASP.NET 4.0, and for this matter the new Package/Publish feature including sub-web.configs which is meant to supersede the Web Deployment Project. For a manual deployment there’s a good write-up on the msdn library titled ASP.NET Web Application Project Deployment Overview which shows how and where to set this up. In our case this was not satisfactory because our deployment process is a bit more complicated. We push our changes to a central repository and use JetBrains’ continuous integration server (CIS) TeamCity Professional, which is totally free for our project size, for a continuous integration process. Once TeamCity has pulled and tested the current version, it is supposed to deploy this version to our staging server where we further test the complete site. The key point in an automatic deployment was the management of the different web.config files for the different environments our project is running on. Unfortunately, until yesterday every deployment that included changes to the web.config file – even to the staging server - required a manual step of editing the web.config that live on our staging system (outside of source control!). What we used to do: after a successful build on our CIS we simply copied the web application (files) to our staging server! But as Scott Hanselman wrote: If You're Using XCopy, You're Doing It Wrong! This post inspired us to move along and take advantage of the new possibilities that we were given. In the meanwhile, before switching to .NET 4.0 actually, we also took a shot at the Web Deployment Project way of doing things but never actually got that far as to fully automate the deployment – somehow the setup was not as easy as we hoped. Anyway, we wanted web.config Transforms! So what does our setup look like and what did we want to do?   During local development and testing I use a web.config file that talks to a local DB instance and has some more specific settings. To run the web application on our staging server we need to replace certain values or whole sections in the web.config. For this transformation we use the sub-web.config files, one for each build configuration: Now, with all of these web.config files the simple XCOPY deployment we used to use does not work any longer. We need to trigger the web.config transformation on the build server and then deploy the whole application. As easy as this looks using the built-in menus and dialogs in Visual Studio – it took me quite a while to find how to do this in an automated build, more concretely from the command line. After unsuccessfulle skimming stackoverflow.com for a solution I finally tripped over this very informative blog post on publishing a VS2010 ASP.NET web application using MSBuild. Admittedly, the author focuses on how to publish on the local machine as it’s yet a different process but towards the end he posts the solution I was looking for: 1: msbuild Website.csproj "/p:Platform=AnyCPU;Configuration=Release;DesktopBuildPackageLocation=c:\_Publish\stage\Website.zip" /t:Package This was it! After running this on my machine with my own settings I looked into the folder with the zip file and found the following 5 files: At first I just wanted to take the zip file, copy it to the staging server, unpack it – done! But then I peaked into it… and deeper… and deeper… and… still deeper… until I finally saw our application files underneath this directory: This has got to be one of the longest paths I’ve ever seen and used! How would I automate the extraction of web application files from the zip with such a path? I was already seeing myself hacking away on the command line… But wait: what about those files that appeared next to the zip file? A ci-stage.deploy.cmd and a readme.txt caught my attention – of course, I opened the cmd file first :-D Well… maybe the readme file gives me a shortcut to understanding this and the rest of the 190 lines: Looks promising! I convinced myself to give it a shot. So we set up a new configuration in TeamCity with the following settings: These settings reflect the command line from above with a few minor changes (removed the DesktopBuildPackageLocation and set the /v[erbose] switch to m[inimal]): msbuild Website.csproj "/p:Platform=AnyCPU;Configuration=Release" /t:Package /v:m The second step is to use the generated script, ci-stage.deploy.cmd. I recommend to run the script by hand once using the /T switch just to make sure everything looks alright. In our case we found out that the deployment of the package would have deleted a lot of files, most of all images, from our website. This was not what we wanted! After a quick search I found this question on stackoverflow.com: MSDeploy: “Leave extra files on destination” from command line? So I added this switch to the parameters list in the build step configuration as follows: That’s it! This is all we need on the command line to generate a package that is ready for deployment on the staging server. There are a few more necessary settings, including the DesktopBuildPackageLocation, that can be found in the Package settings window inside the project properties of the web application project: the DesktopBuildPackageLocation can be set here instead of on the command line the website and application name on the destination IIS server that this package should be installed to some more options like excluding debug symbols etc. These settings are saved in the project file and will be used during generation and deployment of the package. That’s all I have to say right now. Happy Coding, Oliver

Zu viel Konstruktor-Injizierung!?

by robert 10. June 2010 10:02

Ist zu viel Konstruktor-Injizierung ein Anti-Pattern? In einem Blog-Post konstruiert Jeff Palermo ein Beispiel, in dem eine injizierte Komponente eine wirklich langsame Initialisierung hat. Da die Komponente nur von einer Methode der Klasse benötigt wird, leitet er ab, dass statt der Konstruktor-Injizierung von Komponenten eine Lazy-Factory die bessere Alternative ist. Die Kommentare lehnen diesen Schluss fast fast einhellig  ab, denn: 1) Eine Verzögerung der Initialiszierung ist auch über den Container und mit .NET 4 auch über System.Lazy<> zu erreichen. 2) Gegen eine Schnittstelle zu programmieren bedeutet gerade, dass man Implementierungsdetails nicht in der Hand hat. 3) Langwierige Initialisierung in der Komponente bedeutet, dass die Komponente minderer Qualität ist, hier muss dann nachgebessert werden. Es sollten keine aufwändigen Operationen in der Konstruktor-Implementierung durchgeführt werden. 4) Ein Abstrakte Fabrik erzeugt ein Abhängigkeit gegenüber dieser, was die Menge der Abhängigkeiten steigert und die Vorteile des IoC reduziert. Jedes „new“ ist bad „news“. 5) Um häufige Initialiserung zu umgehen, könnten Services als Singelton konfiguriert werden, wobei globale Konfiguration ein Problem sein kann, weil sie schwer zu Debuggen und auf Anwendungsebene auch schwer nachzuvollziehen sein kann. Danke an Jeff Palermo für seinen sehr lesenwerten Post! Links: http://jeffreypalermo.com/blog/constructor-over-injection-anti-pattern/

GEN0 und GEN2 Heap gegenläufig?

by robert 3. June 2010 19:48

Gen2 und Gen0 sind deutlich gegenläufig, versteh ich nicht, hat jemand ein Erklärungsmuster? Fängt der Garbarge-Collector an in Gen2 aufzuräumen weil viel neuer Speicher allokiert werden muß? Bild: Speicher für Camping.Info Server: (8 GIG RAM, 2x Quadcore, ca.: 1000 aktive Sessions, Requests per Second: Avg.: 50, Peak: 120) Das 10x mehr Speicher in Gen2 liegt bedeutet natürlich, dass wir unser Memory-Leak immer noch nicht aufgespürt haben. Normal müsste Gen 2 kleiner sein als Gen 0? Viele Fragen :-) (Robert)

ALT.NET.BERLIN Zweiter „Coding Dojo“ und erster „OPEN-TABLE“

by robert 10. May 2010 20:39

Die Berliner User Group „ALT.NET-BERLIN“ hat Ihren zweiten „Coding-Dojo „durchgeführt (und das 14. Treffen). Sehr lesenswert ist die Aufbereitung des Coding-Dojos durch Mike. Ich konnte wirklich viel aus dem Treffen und dem Bericht ziehen. Jedem der kann, sei ein Besuch des nächsten Dojos sehr ans Herz gelegt! Morgen am Dienstag den 11.05.2010 findet unser erster Open-Table statt. Thema sind Design-Patterns. Die Agenda ist hier zu finden. Wer über ALT.NET.BERLIN Termine auf dem Laufenden bleiben möchte, dem sei die XING-Gruppe .NET Berlin empfohlen, die Marco heute gegründet hat :-)

Memory-Leak vs. Memory Waste

by robert 15. April 2010 14:55

kurzer Nachtrag zum vorherigen Post. Eigentlich kann management Code für sich keine Memory-Leak haben. Es ist lediglich möglich Speicher schlecht zu nutzen, in dem er zum Beispiel zu lange gehalten wird. Technisch ist das kein Memory-Leak. Nur unmanaged Code kann nicht mehr verwalteten Speicher zurück lassen. Deswegen suchen wir streng genommen vermutlich eher nach "Memory-Waste"!

ASP.NET Memory-Leak

by robert 15. April 2010 14:38

Wir sind auf der Suche nach einem Memory-Leak für Camping.Info. Es ist jetzt Donnerstag, 11:56Uhr. Der Web-Server läuft jetzt erst seit 16 Stunden.   Die aktuelle Session-Zahl liegt bei 652. Den Session Timeout von 20min rausgerechnet, bei einer Verweildauer von 7 Minuten, entspricht das ungefähr 70 tatsächlich aktiven, parallelen Besuchern zu diesem Zeitpunkt. (Im laufen des Tages, werden das deutlich mehr.) Der Speicherverbrauch ist für die aktuelle Session-Zahl zu hoch und steigt anscheinend stetig über mehrere Tage hinweg. Es gibt 2 Möglichkeiten für Memory-Leaks in Managed-Code. Referenzen auf Objekte werde nicht wieder frei gegeben, in dem Sie Beispielsweise im ASP.NET Cache gehalten werden. NET Code verwendet unmanaged Resourcen wie FileSystem oder "Datenbank-Connections und diese Resourcen werden nicht wieder explizit frei gegeben. Auch “System.Drawing" stellt ein Problem dar, da es auf unmanagement Resourcen verweist. Oliver hat einen Dump erstellt der wie folgt aussieht:   Wir sind auf der Suche, über die Lösung des Problems werden wir berichten.

Vorsicht bei String als Lock-Objekt

by Oliver 20. March 2010 14:59

Wenn man denn unbedingt einen String als Lock-Objekt nutzen will und man sich davon überzeugt hat, nicht den Type eines gerade durch die Methode schwirrenden Objektes zu nutzen (das könnte ja jeder machen ;-)), dann sollte man auf jeden Fall an String.Intern(myLockString) denken! Wo man kann, sollte man also weiterhin einen einfachen object-Lock nutzen: 1: private static readonly object _lock = new object(); Aber man darf dann nicht vergessen, dass alle Threads, die auf den Code im Lock zugreifen von einem einzigen Lock bedient werden. Wenn man die Threads aber in Gruppen aufteilen kann, die unabhängig voneinander arbeiten können, ist diese Beschränkung zu scharf. Ich suchte also einen Weg, einen eindeutigen Lock für jede dieser Gruppen (hier: TranslationGroup) zu generieren, der von allen Instanzen geteilt wird. Der Gruppenidentifikator heißt hier _pageNameKey. Beispiel: 1: private const string _migrateLock = "9B90360AAF4342798A739DA2D85AF5AF"; // guid 2:  3: private string _pageNameKey = ...; // can be the same in multiple parallel threads 4:  5: public void Migrate(string oldGroupName, Dictionary<string, string> controlIdsOldToNew) 6: { 7: lock (string.Intern(_migrateLock + _pageNameKey)) 8: { 9: // do something not here that should not be done in parallel 10: } 11: } Ließe man in Zeile 7 das String.Intern(…) weg, würde bei jedem Aufruf der Methode ein neues String-Objekt generiert – was für dem Lock seine Existenzberechtigung raubt. Gruß, Oliver Technorati-Tags: Multithreading,Lock,String,Intern

Arten von Branches, Branch per Refactoring

by robert 8. December 2009 15:42

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.

Benamungs-Strategien

by robert 15. November 2009 19:57

SettingsApp SettingsUser SettingsSubSystemX vs. AppSettings UserSettings SubSystemXSettings beides hat seine für und wider. Variante 1 ist IntelliSense freundlicher und Variante 2 ist näher an der Grammatik. Ich bin ich ganz klar - soweit die Wortgebilde nicht total obskur werden - meist für Variante 1.

32-bit DLL in 64-bit WebApp: An attempt was made to load a program with an incorrect format

by Oliver 30. October 2009 16:36

Die folgende Fehlermeldung erhielt ich heute von unserem IIS7, als ich Camping.Info starten wollte: Server Error in '/' Application. Could not load file or assembly 'Microsoft.Cci' or one of its dependencies. An attempt was made to load a program with an incorrect format. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.BadImageFormatException: Could not load file or assembly 'Microsoft.Cci' or one of its dependencies. An attempt was made to load a program with an incorrect format. Also das Orakel gefragt und u.a. das hier gefunden: http://forums.asp.net/t/1358032.aspx Standardmäßig unterstützt ein 64-bittiger IIS 7 keine 32-bit-Module (u.a. DLLs). Man kann es ihm aber einfach beibringen :-) Im IIS-Manager den gewünschten ApplicationPool auswählen und in den Advanced Settings die folgende Einstellung vornehmen: Der Vollständigkeit halber hier noch ein Link zur Anleitung für den IIS6 auf Windows 2003 Server: http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/405f5bb5-87a3-43d2-8138-54b75db73aa1.mspx?mfr=true Happy Coding! Oliver

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our three children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.