Sonntag, August 21, 2011

Nuget package für verschiede Frameworks

Eigentlich sollten in der Zwischenzeit alle .NET Entwickler von Nuget gehört haben. Nuget ist wirklich ein sehr schönes Mittel Bibliotheken, Komponenten und Artefakte zu verteilen und für anderen zur Verfügung zu stellen. Für Nuget gibt es eine gute Integration ins Visual Studio 2010, leider nicht in die älteren Versionen, zusätzlich eine Kommandozeilen und Powershell Version. Da ich viel auf Reisen bin, habe ich mir die wichtigsten Pakete, die ich immer wieder brauche in ein lokales Repository gezogen.

nuget install <packagename>

Aus dem heruntergeladenen und installierten Paketen habe ich anschließend alle nupkg-files in einen Ordner kopiert, fertig war mein Repo. Wer mag kann noch eine Nuget-Server (http) daraus machen, dazu muss man nur das Paket “nuget server” 
Im Package Manager im VS kann man mittels Add superleicht seine eigenen Quellen hinzufügen.

Visual StudioNuget Manager Konfiguration

Ein Paket kann man superleicht aus jedem Projekt erzeugen.

Nuget spec <packageName>
Nuget pack <projectFile>

Erzeugt ein Spec file. Wenn es zusätzlich im Verzeichnis der SLN ausgeführt wird, dann werden die Parameter als Platzhalter übernommen. mit dem 2Kommando wird das nupkg erzeugt und der Output des Projektes hinzugefügt.

Der Teil war einfach. Nun sollte das Paket allerdings mehrere FX-Versionen unterstützen. Dazu muss man im SPEC-file den Output entsprechend in verschiedenen Ordnern einsortieren. Im file-Element kann man mittels des target-Attributes das Verzeichnis im Package definieren. Im Beispiel unten werden die Assemblies für Framework 2.0 in den Ordner “lib\net20” einsortiert.

lib\net20 .NET Framework 2.0
lib\net40 .NET Framework 4.0
lib\sl40 Silverlight 4.0

Nach dem editieren sah mein Spec File so (oder so ähnlich aus):

1 <?xml version="1.0"?> 2 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> 3 <metadata> 4 <id>MyPackageName</id> 5 <version>$version$</version> 6 <authors>Jan Zieschang</authors> 7 <owners>Jan Zieschang</owners> 8 <!--<licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl> 9 <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl> 10 <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>--> 11 <requireLicenseAcceptance>false</requireLicenseAcceptance> 12 <description>Here I should describe my package a bit</description> 13 <tags>Tags For Grouping And Finding</tags> 14 <dependencies> 15 <dependency id="PostSharp" version="2.1"/> 16 <dependency id="log4net" version="1.2" /> 17 </dependencies> 18 </metadata> 19 <files> 20 <file src="bin\Release\myPackageLib.dll" target="lib\net40"/> 21 <file src="bin\Release\myPackageLib.xml" target="lib\net40"/> 22 <file src="bin\Release\myPackageLib.pdb" target="lib\net40"/> 23 <file src="bin\Release2\myPackageLib.dll" target="lib\net20"/> 24 <file src="bin\Release2\myPackageLib.xml" target="lib\net20"/> 25 <file src="bin\Release2\myPackageLib.pdb" target="lib\net20"/> 26 </files> 27 </package>

Nun schlug aber der “Nuget pack” Aufruf fehlt. Nach etwas probieren musste ich feststellen, dass der Aufruf nun das Spec file direkt benötigt.
1 "%nugetPath%" pack "MyPackageName.nuspec" -Version %1 -OutputDirectory "%output%" -symbols -Properties Configuration=Release

Wie man ein Package erstellt kann man noch viel detaillierter unter http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package nachlesen. Hier noch einige Tipps für die Paketierung - http://lostechies.com/joshuaflanagan/2011/06/23/tips-for-building-nuget-packages/. Publishen von Packages ist mehr als simpel. Paket in den Ordner kopieren und oder mittels nuget push auf den Paket Server schieben. Ich kam nun aber zu meinem eigentlich Problem.

Da ich für mein Projekt mehrere Ausgaben für unterschiedliche Frameworks erzeugen wollte, musste ich irgendwie meine Projekt-Referenzen austauschen. Letztendlich habe ich mein Project files editiert und die Hint-Paths für die Assemblies verändert.

1 <Reference Include="log4net"> 2 <HintPath>..\packages\log4net.1.2.10\$(Log4NetHintPath)\log4net.dll</HintPath> 3 </Reference> 4 <Reference Include="PostSharp, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7, processorArchitecture=MSIL"> 5 <HintPath>..\packages\PostSharp.2.1.2.8\$(PostSharpHintPath)\PostSharp.dll</HintPath> 6 </Reference>

Die beiden Hint-Paths für die Referenzen PostSharp und Nunit werden mittels einer Variable dynamisiert. In einer PropertyGroup mit einem Condition-Element können die Referenzen ausgetauscht werden.
1 <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release3.5|AnyCPU'"> 2 <PostSharpHintPath>lib\net20</PostSharpHintPath> 3 <Log4NetHintPath>lib\2.0</Log4NetHintPath> 4 <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> 5 </PropertyGroup>

Achtung: Sollte man die Referenzen aktualisieren, zum Beispiel weil es neue Versionen als Nuget-Package gibt, muss ggf. der Hint-Pfad erneut korrigiert werden.

Außer dem Hint-Path muss die Property “TargetFrameworkVersion” gesetzt werden. Dadurch kann man mit MSBuild 4.0 alle Fx Versionen bauen. (Ich glaube, es gebe auch schon in MSBuild 3.5)

Das war die minimal Version. Aber ich habe schon seit Wochen TeamCity auf meinem Rechner gehabt und CI Builds von meinem Paket zu erzeugen. Außerdem wollte ich auch eine minimal Validierung meines Setups haben.

TeamCity Build Steps for my build

Aus bauen und Paket erstellen ist sind 4 Schritte geworden. Baue Fx4 Version, Validiere diese Version, Baue Fx3.5 Version Validiere diese und anschließend erstelle mein Nuget-Paket. Natürlich übernimmt der CI Server auch die Versionierung der Assemblies und des Paketes und checkt die Version immer sauber aus.

Technorati Tags:

Braucht man für DVCS auch lokale Build-Server?

DVCS steht für Distributed Version Control System. DVCS  ist nicht neu, aber es ist seit einigen Jahren noch stärker in Mode gekommen. Die beiden gängigsten Vertreter sind Mercurial (HG) und GIT. Zurzeit nutze ich für meine “Spiel”-Projekte Mercurial, da es unter Windows etwas angenehmer ist und BitBucket unbegrenzte (private) Repos zur Verfügung stellt.

Das Gute an DVCS ist, dass man sämtliche Informationen lokal hat und auch alle Interaktionen mit dem Repository lokal durchführt. Nur ab und an muss man sich mit dem “source” Repo verbinden und die Informationen updaten. Andere haben die Möglichkeit vom eigenen Repo eine komplett arbeitsfähige Kopie zu ziehen. Ich nutze am meisten lokale Branches und Commits (Reverts). Ich hatte leider einige mal Dateien vergessen einzuchecken (genau wie bei zentralen Repos). Ohne Continuous Integration (CI)  Build merkt man das vermutlich erst, wenn man mal einen anderen Rechner nutzt. Bei zentralen Repos nutze ich eigentlich immer CI, bisher bei DVCS eher weniger. Nach vergessenen Dateien und der Frage, wie ich das abstellen kann, habe ich mich entschieden lokal zu integrieren.

Ich habe mein lokales DVCS Repo in einem Build-Server konfiguriert, so dass dieser einen Clone erzeugt und anschließend gegen die Version kompiliert. Später habe ich auch noch die Ausführung von Tests hinzugefügt. Mir ist dabei erst wieder aufgefallen, wie viel man beachtet, wenn man ein weiteres Feedback der Kompilierung erhält. Denn im Studio geht doch fast immer alles. Man kann das lokale CI noch verbessern, in dem man einen Remote-Build-Agent benutzt, so dass auch ggf. Abhängigkeiten zu VS.NET Installation oder anderer Software erkannt werden können. Seit einiger Zeit habe ich mir auch angewöhnt Unit-Tests zu schreiben, es wird immer leichter einen Test zu schreiben, leider vergesse ich aber öfters die Tests vor dem Checkin laufen zu lassen. Mein Build-Server nimmt mir diese Arbeit ebenfalls ab, so dass ich schnell merke, dass etwas nicht passt. Normalerweise sind die heutigen Rechner leistungsfähig genug und eine Software zügig zu kompilieren ohne, dass die Arbeit maßgeblich leidet. Ich werde in Zukunft, sofern ich mit DVCS arbeite, meinen lokalen Build Server nutzen. Aber noch ein weiterer Vorteil entsteht, Reproduzierbarkeit. Ich kann, sofern ich mal eine Version meines Codes zur Verfügung stelle, diese leicht noch einmal bereitstellen. Es ist nicht notwendig Dateien von Hand zusammen zu sammeln und an die richtigen Stellen für meine Auslieferung zu kopieren.

Ich bin ein riesiger Fan von CCNet und finde den Server für .NET einfach sehr schön. Allerdings gibt es einige Dinge, die mich (vermutlich eher andere) stören. So ist die Konfiguration nicht sehr intuitiv und erfordert schon etwas wissen. Zum Anpassen der Konfiguration muss man eigentlich immer das XML manipulieren und auf dem Server aktualisieren. Das war der Grund mich mal wieder umzuschauen und eine sehr schnell aufkommende alternative war TeamCity. Es ist für kleinere Build-Umgebungen, die man vermutlich lokal auch nur hat, kostenlos und ermöglicht in der Website relativ leicht die Konfiguration und Statusabfrage. Ich finde die Webseite des lokalen Build-Servers schick und übersichtlich, es wird viel Ajax genutzt um Informationen bereitzustellen.

ScreenShot 019 http___localhost_81_project.html;jsessionid=F1931030A2F886EF4688798E3307BF1A_projectId=project2 - IE

In TeamCity kann man dem Build-Agent Bedingungen zuweisen die dieser Erfüllt und einer Build-Konfiguration Bedingungen, die erfüllt sein müssen um eine Integration auszuführen. Man kann sich auch einfach mittels verschiedener Kommunikationskanäle über den Status informieren lassen.

Technorati Tags: ,

Montag, April 11, 2011

Ein paar Unity 2.0 Beispiele

Ich komme kaum noch zum Bloggen, es findet sich immer etwas Wichtigeres. Zu dem fehlt momentan die Kick-Technologie zum bloggen.

Heute stelle ich zumindest mal meine Samples zu Unity 2.0 zum Download. Ich setze in fast all meinen Projekten Unity ein (außer ich muss etwas anderes nutzen), da es sehr simpel und effektiv ist. Unity ist bei Microsoft in eine wichtige Rolle geschlüpft. Heute habe ich gelernt, dass Unity im Exchange Server 2010 verwendet wird. Ich muss mir das zukünftig mal genauer anschauen. Das

Unity kann man Inline konfigurieren, oder per externer Konfiguration initiieren. Bei der Konfiguration per Code werden die Typen und Mappings direkt zum Container hinzugefügt. Die Inline Konfiguration nutze ich als Fallback, falls keine Konfiguration im Container vorhanden ist.

   1:  IUnityContainer container = new UnityContainer();
   2:  container.RegisterType<ITestInterface, TEstRunner>();
   3:  container.RegisterType<ITestInterface, TEstRunner>("specialVersion");
   4:  container.RegisterType<ITestInterface, TEstRunner>("singelton", new ContainerControlledLifetimeManager());
Prüfung, ob eine Definition vorhanden ist.
if(!container.IsRegistered<ITestInterface>())container.RegisterType<ITestInterface, TEstRunner>();

Zur Konfiguration mittels der Konfigurationsdatei muss der Container mit

new UnityContainer().LoadConfiguration();

initialisiert werden. Die Konfiguration könnte zum Beispiel so aussehen:

   1:  <configuration>
   2:    <configSections>
   3:      <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
   4:    </configSections>
   5:   
   6:    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
   7:      <alias alias="runner" type="PoC.Unity.Configuration.TEstRunner, PoC.Unity.Configuration"/>
   8:      <alias alias="Iservice" type="PoC.Unity.Configuration.ITestInterface, PoC.Unity.Configuration"/>
   9:      <container>
  10:        <register type="Iservice"  mapTo="runner"/>
  11:        <register type="Iservice"  mapTo="runner" name="specialVersion">
  12:          <lifetime type="perthread"/>
  13:        </register>
  14:        <register type="Iservice"  mapTo="runner" name="singelton">
  15:          <lifetime type="singleton"/>
  16:        </register>
  17:      </container>
  18:    </unity>
  19:  </configuration>

Beim Erstellen eines Objektes durch den Container, können notwendige Abhängigkeiten ohne weitere Aktionen übergeben werden, sofern diese dem Container bekannt sind. (Injection) Ich nutze meistens Konstruktur Injection, da dies aus meiner Sicht die richtige Anwendung für erforderliche Komponenten ist.

   1:  public class TEstRunner : ITestInterface
   2:  {
   3:      private readonly IUnityContainer _container;
   4:      public TEstRunner(IUnityContainer container)
   5:      {
   6:          _container = container;
   7:          Console.WriteLine("Do I have a container? - " + (ReferenceEquals(_container, null) ? "no" : "yes"));
   8:      }
   9:  ...
  10:  }
  11:  container.Resolve<ITestInterface>().Run();

AOP ist etwas komplizierter, sobald es einmal läuft, auch wieder ganz einfach. ;) Allerdings muss man einige Utility-Klassen erstellen, so ist eine Attribute-Klasse und eine Implementierung des ICallHandler-Interface notwendig. Hier zumindest mal die Unity-Konfiguration:

   1:  IUnityContainer uc = new UnityContainer();
   2:  uc.AddNewExtension<Interception>();
   3:  uc.RegisterType<ITestInterface, TEstRunner>();
   4:  uc.Configure<Interception>()
   5:      .SetDefaultInterceptorFor<ITestInterface>(new InterfaceInterceptor());

Eigentlich ist es egal welchen DI/IoC Container man nutzt, es hilft den Code testbarer zu machen und reduziert Abhängigkeiten deutlich. Das Beste Bespiel für die Reduzierung von Abhängigkeiten erlebt man, wenn man mit Prism arbeitet.

Hier noch der Link zu einem schönen Video zu Unity (etwas veraltet): Unity Features and Futures at the patterns and practices Summit 2009

Samples: poc.unity.zip