Blue Orange Green Pink Purple

Posts Tagged ‘HowTo’

You can use the search form below to go through the content and find a specific post or page:

Jun 11

HowTo: Eigene Regeln für Microsoft StyleCop erstellen

Im ersten Teil der Serie habe ich erklärt, wie man StyleCop grundsätzlich in Verbindung mit MSBuild und Visual Studio verwenden kann. Heute möchte ich kurz erklären, wie man eigene Regeln für StyleCop definiert und diese mit dem Setup vom ersten Teil verwenden kann. Da ich selber ein verfechter davon bin, private Member (Properties, Klassen, Methoden, etc.) mit einem _ (Unterstrich) als Präfix zu versehen, möchte ich heute eine StyleCop Regel implementieren, die genau diesen Standard überprüft.

Eigene Regeln implementieren!
Um eigene Regeln für StyleCop zu implementieren, müssen wir zuerst einmal StyleCop herunterladen bzw. installieren, weil wir dabei 2 wichtige Assemblies bekommen, die wir für die Entwicklung benötigen. Als nächstes erstellen wir uns in Visual Studio eine leere Klassenbibliothek und referenzieren die 2 Assemblies

  • Microsoft.StyleCop.dll
  • Microsoft.StyleCop.CSharp.dll

Haben wir das, erstellen wir uns eine neue Klasse und leiten diese von Microsoft.StyleCop.SourceAnalyzer ab. Das ist die Basisklasse für alle Regeln, die in StyleCop verfügbar sein sollen. Zusätzlich definieren wir über der Klasse noch ein Attribut, das angibt, für welche Sprache die Regel verwendet werden kann: [SourceAnalyzer(typeof(CsParser))].

Die Basisklasse implementiert eine Methode namens AnalyzeDocument(…), die wir in unserer eigenen Klasse nun überschreiben sollten, um auch wirklich auf den Quellcode der einzelnen Dateien, die später von unserer Regel evaluiert werden sollen, Zugriff zu haben:


public override void AnalyzeDocument(CodeDocument document)
{
   CsDocument csdocument = document as CsDocument;
   if (csdocument == null) return;

   csdocument.WalkDocument(new CodeWalkerElementVisitor<object>(this._VisitElement));
}

Die Methode hat einen Parameter vom Type CodeDocument. Dieses Dokument können wir anschließend auf ein CsDocument casten, da wir ja wissen, dass unsere Regel nur für CSharp Dokumente verwendet werden kann. Um jetzt das Dokument durchlaufen zu können, bietet das SDK hierfür verschiedene Klassen an, mit den denen unterschiedliche Typen evaluiert werden können. Wir verwenden hier die Klasse CodeWalkerElementVisitor, weil wir nur an Elementen (Klassen, Properites, usw.) interessiert sind. Diese Klassen verwenden das Visitor Design Pattern. Das heißt, dass der Visitor das Quelldokument durchparst und sobald er auf ein (in unserem Falle) Element trifft, die Methode _VisitElement aufruft:

private bool _VisitElement(CsElement element, CsElement parentElement, object context)
{
   if (element.Generated) return true;

   if (element.AccessModifier == AccessModifierType.Public ||
       element.AccessModifier == AccessModifierType.Internal) return true;

   if (element.ElementType == ElementType.Field ||
       element.ElementType == ElementType.Accessor ||
       element.ElementType == ElementType.Constructor ||
       element.ElementType == ElementType.ConstructorInitializer ||
       element.ElementType == ElementType.Destructor ||
       element.ElementType == ElementType.EmptyElement ||
       element.ElementType == ElementType.EnumItem ||
       element.ElementType == ElementType.ExternAliasDirective ||
       element.ElementType == ElementType.File ||
       element.ElementType == ElementType.Indexer ||
       element.ElementType == ElementType.Namespace ||
       element.ElementType == ElementType.Root ||
       element.ElementType == ElementType.UsingDirective) return true;

   if (element.Declaration.Name.Trim().StartsWith("_")) return true;

   this.AddViolation(element, element.LineNumber, "NonPublicMemberUnderscorePrefix");

   return true;
}

Die Methode überprüft, ob es sich bei dem Element um einen Typ handelt, der für uns auch von Interesse ist. Falls ja, muss der Name des Elements mit einem Unterstrich beginnen. Tut es das nicht, verwenden wir die Methode AddViolation der Basisklasse, um StyleCop mitzuteilen, dass wir auf eine Regelverletzung gestossen sind. Der 3. Parameter “NonPublicMemberUnderscorePrefix” ist wichtig: Um diese Regel nun tatsächlich verwenden zu können, müssen wir sie noch mit einigen Metainformationen ausstatten:

MetaInfos für StyleCop Regeln
StyleCop verwendet für jede Regel ein XML File, das die notwendigen Metainformationen für jede Regel beinhaltet. Wir müssen daher für unsere eigene Regel noch ein XML File erstellen, dass genau diese Metainformationen beinhaltet. Zuerst fügen wir zu unserem VS Projekt ein neues XML File hinzu und ändern in den Properties die Eigenschaft Build Action auf Embedded Resource. Anschließend können wir im XML File die Metainformationen befüllen:

<?xml version="1.0" encoding="utf-8" ?>

<SourceAnalyzer Name="My Rules">
 <Description>
  My custom StyleCop rules.
 </Description>
 <Rules>
  <RuleGroup Name="Naming Rules">
   <Rule Name="NonPublicMemberUnderscorePrefix" CheckId="MY1001">
    <Context>
     NonPublic members must start with an underscore
     followed by an upper-case letter.
    </Context>
    <Description>
     NonPublic members must start with an
     underscore followed by an upper-case letter.
    </Description>
   </Rule>
  </RuleGroup>
 </Rules>
</SourceAnalyzer>

Der Aufbau dieses XMLs ist relativ einfach. Zuerst definieren wir einen neuen SourceAnalyser und geben ihm einen Namen und eine Beschreibung. Dann definieren wir eine neue Gruppe für unsere Regel (RuleGroup, wir könnten auf diese Gruppe auch verzichten). Und jetzt kommt der wirklich interessante Teil. Wir definieren unsere eigene Regel und geben ihr einen Namen: NonPublicMemberUnderscorePrefix. Dieser Name muss exakt mit dem übereinstimmen, den wir vorher bei der Methode AddViolation(…) im Sourcecode als 3. Parameter angegeben haben. Sind diese Namen unterschiedlich, kann StyleCop die Metainformationen nicht eindeutig zuordnen. WICHTIG: Der Name der XML Datei muss exakt der gleiche wie der Name der Sourcecode Datei sein. Also heißt die *.cs Datei MyRules.cs muss die XML auch MyRules.xml heißen.

Um diese Regel jetzt auch tatsächlich in StyleCop verwenden zu können, kopieren wir sie in das Installationsverzeichnis von StyleCop. Starten wir jetzt den StyleCopSettingsEditor, sollten wir unsere neue Regel bereits in der Liste sehen können:

Integration mit MSBuild
Um solche eigenen Regeln auch mit dem Setup aus dem ersten Teil verwenden zu können, müssen wir am *.targets File von StyleCop etwas verändern. Bei diesem Setup haben wir alle Dateien (*.dll und *.targets), die für StyleCop notwendig sind, in einem relativen Verzeichnispfad abgelegt. In das selbe Verzeichnis kopieren wir nun unsere soeben erstellte StyleCop-Assembly. Im selben Verzeichnis sollte eigentlich eine Datei namens Microsoft.StyleCop.Targets liegen. Die öffnen wir uns suchen nach der Zeile AdditionalAddinPaths=”…”. Der Pfad, der dort eingetragen ist, gibt an, wo zusätzliche Assemblies gespeichert sind, in denen eigene StyleCop Regeln definiert sind. Nun, da wir gerade unsere Assembly in das selbe Verzeichnis kopiert haben, können wir mit den VS Macros direkt relativ auf dieses Verzeichnis verweisen:

<StyleCopTask
   ProjectFullPath="$(MSBuildProjectFile)"
   SourceFiles="@(StyleCopFiles)"
   AdditionalAddinPaths="$(ProjectDir)\Integration\"
   ForceFullAnalysis="$(StyleCopForceFullAnalysis)"
   DefineConstants="$(DefineConstants)"
   TreatErrorsAsWarnings="$(StyleCopTreatErrorsAsWarnings)"
   CacheResults="$(StyleCopCacheResults)"
   OverrideSettingsFile="$(StyleCopOverrideSettingsFile)"
   OutputFile="$(StyleCopOutputFile)"
   MaxViolationCount="$(StyleCopMaxViolationCount)" />

Das wars. Der Sourcecode (den ich etwas erweitert habe) kann wie immer unter http://downloads.juergenoberngruber.at/blog/CustomStyleCopRules.zip herunter geladen werden.

In diesem Sinne.

Jun 05

HowTo: Microsoft StyleCop Integration mit Visual Studio und MSBuild

Microsoft StyleCop ist mal echt was tolles. Vielleicht kennt ja wer folgendes Szenario:

“Zu Beginn eines neuen Projektes nehmen sich alle Projektmitglieder vor, mal etwas mehr Codequalität in den Sourcecode zu bringen, weil sie bemerkt haben, wie schwierig es ist, einen Sourcecode zu verstehen, wenn er nicht aus seiner eigenen Hand kommt (bei manchen ist es sogar schwierig, den eigenen Code nach ein paar Wochen noch zu verstehen). Gut, man einigt sich darauf, ein paar Prinzipien aus der agilen Softwarentwicklung einzusetzen: Pair Programming und Code Reviews.

Anfangs funktioniert das alles wunderbar. Jeder kennt den Code des anderen, es etabliert sich ein gewisser Standard, weil jeder versucht, ähnlich zu programmieren. Im Laufe der Zeit wirds im Projekt stressig – es bleibt keine Zeit mehr, zusätzliche Arbeit zu leisten – hauptsache das Projekt wird irgendwie fertig. Dafür verzichtet das Team auf Qualität. Hauptsache in time and budget.”

Ich denke, dass viele Projektteams in der IT ähnliches schon einmal erlebt haben. Um sich einen wesentlichen Teil der Zeit von manuellen Code-Reviews zu sparen, kann man Tools wie StyleCop einsetzen. StyleCop wird als Visual Studio Addin und als MSBuild Task ausgeliefert. Einmal installiert, kann man StyleCop direkt über das Visual Studio UI starten.

Was macht StyleCop?
StyleCop ist ein Tool zur Codeanalyse von .NET Sourcecode (derzeit wird leider nur C# Code unterstützt). StyleCop analysiert tatsächlich den (unkompilierten) Sourcecode und unterscheidet sich somit von FxCop. StyleCop wird standardmäßig mit zahlreichen nützlichen Regeln geliefert, die zur Gänze auch den .NET Framework Coding Guidelines entsprechen. Das tolle an StyleCop ist die Möglichkeit, mit Hilfe dem mitgelieferten SDK eigene Regeln zu generieren und diese später mit den out-of-the-box Regeln zu kombinieren.

Lädt man sich StyleCop herunter, bekommt man ein MSI Paket, dass einerseits ein Visual Studio Addin und, wenn man es aktiviert, auch einen MSBuild Task installiert. Diesen MSBuild Task werden wir später noch benötigen.

stylecop

Nach der Installation kann man StyleCop direkt in Visual Studio ausführen:

StyleCop_1

Integration mit MSBuild
StyleCop erlaubt auch die Integration mit MSBuild. Man erreicht damit, dass sobald ein Visual Studio Projekt erstellt wird – also bei jedem kompilieren – auch StyleCop automatisch mit ausgeführt wird. Dadurch ersparen wir uns das manuelle Triggern von StyleCop. Es gibt im Internet wenige, aber dennoch einige Beschreibungen, wie StyleCop mit MSBuild integriert wird. Ich möchte hier meine eigene Interpretation vorstellen:

  1. Zuerst erstellen wir uns ein Projekt in Visual Studio und speichern es lokal ab.
  2. Dann gehen wir zum dem Verzeichnis, wo wir zuvor das Projekt gespeichert haben. Dort erstellen wir uns einen Ordner Integration.
  3. Jetzt müssen wir das Installationsverzeichnis von MSBuild die installierten StyleCop Dateien suchen. Standardmäßig sollte es unter %Program Files%\MSBuild\Microsoft\StyleCop\v4.3\
  4. Jetzt kopieren wir aus diesem Verzeichnis alle Dateien mit der Endung *.dll und *.targets in das erstellte Integation Verzeichnis im Projektordner.
  5. Wir öffnen das VS Projektfile *.csproj mit einem Texteditor und fügen folgende Zeilen hinzu: Nach
    <Import Project=”$(MSBuildToolsPath)\Microsoft.CSharp.targets” />

    kommt folgende Zeile
    <Import Project=”$(ProjectDir)\Integration\Microsoft.StyleCop.targets” />

    und in den ersten <PropertyGroup>Knoten kommt
    <StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>

  6. Jetzt speichern wir die Datei und öffnen das Projekt erneut in Visual Studio, kompilieren und bekommen wahrscheinlich eine lange Liste an Fehlern aufgelistet.

stylecop_error

Zur Erklärung: Wir kopieren uns die *.dll und *.targets Dateien relativ in den Projektordner, um das Projekt auch verteilen zu können und nicht zwingend davon auszugehen, dass auf dem Zielrechner auch StyleCop installiert sein muss. Im Projektfile geben wir anschließend an, dass der StyleCop Task bei jedem Erstellen ausgeführt werden soll. Um auf den relativen Pfad zu verweisen (/Integration/Microsoft.StyleCop.Targets) verwenden wir die VS Macros: $(ProjectDir). Anschließend geben wir noch an, das StyleCop jede Regelverletzung als Fehler behandeln soll. Somit erreichen wir, das ein Erstellen eines Projekts nicht möglich ist, sobald mindestens eine Regel verletzt wurde. Würden wir die Regelverletzungen nur als Warnungen behandeln, erreichen wir, dass ab einem bestimmten Zeitpunkt (sobald es im Projekt stressig wird) die Entwickler die Warnungen einfach ignorieren.

Es gibt noch mehr Eigenschaften, die gestetzt werden können. Dazu einfach im .Targets File nachsehen, welche Eigenschaften noch zur Verfügung stehen.

Um jetzt gewissen Regeln zu ignorieren, gibt es bei StyleCop ein Settingsfile names Settings.StyleCop File. Das wird normalerweise nach dem ersten Durchlauf von StyleCop direkt neben dem Projektfile erstellt. Diese Datei beinhaltet Xml Informationen über alle Regeln, die für das aktuelle Projekt deaktiviert werden sollen. Man hat jetzt zwei Möglichkeiten:

  • Entweder man modifiziert diese Datei mit einem Xml Editor manuell oder
  • man verwendet das mitgelieferte Tool StyleCopSettingsEditor.exe. Dazu ist aber eine lokale StyleCop Installation notwendig.stylecop_settings

Das wars. Im nächsten Teil gehts darum, wie man solche StyleCop Regeln selber erstellen kann und ebenfalls mit der MSBuild Integration verwenden kann.

Das Beispielprojekt gibts wie immer unter: http://downloads.juergenoberngruber.at/StyleCopDemo.zip

In diesem Sinne.

Mar 26

HowTo: Deploy ASP.NET MVC auf ASP.NET Hostings

So, heute mal kein Codelastiger Eintrag sondern eher einer in Richtung Konfiguration.

Für einen Freund war ich letztens auf der Suche nach guten Webhostings für ASP.NET MVC. Nachdem ich irgendwie nicht wirklich fündig geworden bin, hab ich mir mal die Frage gestellt: “Warum eigentlich ein spezielles Hosting für ASP.NET MVC?”

Ja warum eigentlich?
ASP.NET MVC ist eigentlich nur ein Aufsatz auf den bereits schon lange bestehenden ASP.NET Kern. Das heißt, es erweitert die bestehende Funktionalität ohne dabei jegliche Konfigurationen an der Hardware bzw. am OS vorzunehmen. Dieser Aufsatz befindet sich in drei kleinen Assemblies:

  • System.Web.Mvc
  • System.Web.Routing
  • System.Web.Abstractions

Deployt man jetzt diese Assemblies mit seiner ASP.NET MVC Anwendung mit auf den Server, muss dort auch nicht zwingend das ASP.NET MVC Framework über einen Installer (sprich im GAC) installiert sein. Wie das Deployment im Detail funktioniert kann man hier nachlesen http://msdn.microsoft.com/en-us/library/dd410407.aspx.

Die Moral von der Geschichte ist jetzt folgende: Um ASP.NET MVC Webseiten zu deployen reicht ein einfachs MS ASP.NET Hosting vollkommen aus. Einzig und alleine die Vorraussetzung, das .NET 3.5 auf dem Server laufen muss schränkt einem beim Suchen nach dem richtigen Webhosting Provider ein.

In diesem Sinne.

Feb 24

HowTo: Custom Cursor für Windows Forms

Heute wieder ein kurzer Eintrag: Letztens wollte ich in einer WinForms Anwendung bei gewissen Aktionen Cursor verändern. .NET liefert zwar ein paar Curors-Typen (System.Windows.Forms.Cursors) mit, die befriedigen bei sehr individuell angepassten und oft auch selbstgerenderten WinForms Anwendungen nicht ganz. Daher will ich heute kurz beschreiben, wie man seinen eigenen Cursor aus einem lokalen File bzw. Resource lädt und verwendet.

Was benötigen wir?
Das einzige, was wir brauchen ist eine einfache *.ico Datei, die wir später als Cursor darstellen wollen. Am besten erstellt man sich so ein File mit einem Bildverarbeitungsprogramm seines Vertrauens. Hat man dass, können wir bereits mit Visual Studio weitermachen. Wir erstellen uns ein WinForms Projekt und fügen das zuvor erstellte *.ico File zum Projekt hinzu und hängen es in die Resources des Projekts.

Jetzt können wir über den Properties-Namespace des Projekts auf die Resources zugreifen und sollten dort über Intellisense unsere soeben hinzugefügte Grafik finden. Die ist vom Type Icon — die wiederrum eine Property names “Handle” verfügt. Genau diesen IntPtr übergeben wir dem Konstruktor der Cursor-Klasse und weisen diese Cursor-Instanz auf den aktuellen Cursor der Anwendung zu. Fertig.

Cursor.Current = new Cursor(Properties.Resources.Cursor_Wait.Handle);

Aussehen tut das Ganze dann so:

Custom Cursor

Demoprojekt gibts wie immer hier zu finden: http://downloads.juergenoberngruber.at/blog/CustomCursor.zip

In diesem Sinne.

Feb 18

HowTo: Erzeugen einer Liste von aufeinanderfolgenden Jahreszahlten?

Heute mal ein kurzer Eintrag, der die Frage “Wie kann ich eine Liste von aufeinanderfolgenden Jahreszahlen einfach erzeugen?” beantworten soll. Out-of-the-box wird das Ganze nicht unterstützt, deshalb schreiben wir uns einfach eine kleine Hilfsmethode, die wir ja in eine Bibliothek oder dergleichen auslagern können:

public static List<DateTime> FindYearsBetween(DateTime start, DateTime end, bool includeStart, bool includeEnd)
{
   if (start == null) throw new ArgumentNullException("Start-DateTime is null.");
   if (end == null) throw new ArgumentNullException("End-DateTime is null.");

   // Zuerst überprüfen wir, ob das Start-Datum gültig ist ...
   if (start == System.DateTime.MaxValue || start == System.DateTime.MinValue)
      throw new ArgumentOutOfRangeException("Start-DateTime cannot be MaxValue or MinValue.");

   // ... und das gleiche für das End-Datum.
   if (end == System.DateTime.MaxValue || end == System.DateTime.MinValue)
      throw new ArgumentOutOfRangeException("End-DateTime cannot be MaxValue or MinValue.");

   // Das Start-Datum muss vor dem End-Datum liegen.
   if (start.CompareTo(end) >= 0)
      throw new ArgumentOutOfRangeException("End-DateTime must be later than Start-DateTime.");

    // Soll das Anfangsjahr nicht in die Ergebnismenge inkludiert werden, dann zählen
    // wir zu dem Anfangsjahr einfach ein Jahr dazu, somit beginnen wir beim Folgejahr.
    if (!includeStart) start = start.AddYears(1);

    // Soll das Endjahr nicht in die Ergebnismenge inkludiert werden, dann holen wir
    // uns das Vorgängerjahr. Somit beenden wir die Suche schon beim Vorgängerjahr.
    if (!includeEnd) end = end.AddYears(-1);

    List<System.DateTime> range = new List<System.DateTime>();

    // Jetzt fürgen wir solange neue Datumswerte hinzu, bis das
    // Anfangsjahr gleich dem Endjahr ist.
    while (start <= end)
    {
        range.Add(start);
        start = start.AddYears(1);
     }

     return range;
}

Der Code inkl. Kommentare ist großteils selbsterklärend. Wichtig ist, die übergebenen Datumswerte auf Gültigkeit zu prüfen – zB muss das Enddatum nach dem Startdatum liegen. Die beiden boolschen Parameter geben an, ob das Startdatum (also das Jahr) in die Ergebnismenge inkludiert werden soll — das gleiche gilt für das Enddatum.

Demoprojekt inklusive Unit.Tests (nunit) gibts wie immer als Download: http://downloads.juergenoberngruber.at/blog/DateTimeList.zip

In diesem Sinne.

Feb 17

HowTo: Existierende VHD im Bootmanager von Windows 7 registrieren

Da ich in letzter Zeit schon öfters gefragt wurde, wie man ein vorhandes VHD File im Bootmanager von Windows7 regiestrieren kann, hier eine kurze Beschreibung dazu.

Warum eigentlich?
Gerade in der Softwareentwicklung verwenden viele Entwickler virtualisierte Systeme, um isolierte Umgegebungen zu gewährleisten und den unterschiedlichen ansprüchen diverser Projekte gerecht zu werden. Beispielsweise sind bei zwei Projekten unterschiedliche Datenbanken im Einsatz, die dann man nich beide auf ein und dem selben System installieren möchte. Oder es sind grundsätzlich zwei verschiedene Technologie wie bspw. .NET und Java im Einsatz.

Daher versucht man, diese unterschiedlichen Anforderungen auf mehreren virtualisierten Systemen abzubilden –> in unserem Fall eben VHD Images.

Um diese Systeme nun zu verwenden, kann man sie entweder im Dualboot booten — also zusätzlich zum Host/Master-System. Dh, man hat zur gleichen Zeit zwei (oder vielleicht sogar mehrere) Systeme am laufen, die natürlich ein und den selber Speicher verwenden. Somit bekommt man vermutlich relativ bald mal Performanceprobleme, weil beide Systeme nicht mehr so tun, wie man das will. Daher gibts die Möglichkeit, diese Systeme im Bootmanager von Windows 7 zu registrieren. Somit hat man die Möglichkeit vor dem Booten das gewünschte System zu wählen, dass schlussendlich hochgefahren werden soll (das ist übrigens nichts neues — gabs auch schon bei Windows Vista).

Wie funktioniert das Ganze?
Es gibt dafür ein Kommando names “bcdedit”, das einfach über die Kommandozeile ausgeführt werden kann (WICHTIG: Die Kommandozeile muss “als Administrator” gestartet werden). Führt man das Kommando ohne Parameter aus, dann bekommt man eine Liste der bereits registrieten Systemen im Bootmanager (das gleiche passiert auch, wenn man es mit dem Parameter /v ausführt):

c:\bcdedit

So um nun ein neues VHD File in den Bootmanager reinzuhängen, brauchen wir ein paar mehr Befehle. Als erstes, um das Ganze zu vereinfachen, erstellen wir uns eine Kopie des aktuelle verwendeten Systems (dh wir kopieren uns aus dem Bootmanager alle Einstellungen des Systems, das wir gerade hochgefahren und laufen haben)

c:\bcdedit /copy {current} /d "Name des Eintrags"

Der Parameter /d beschreibt den Namen des Eintrags, der später beim Bootmanger angezeigt wird. Hier sollten wir sprechende Namen vergeben, um später im Bootmanager nicht den Überblick zu verlieren. Klappt der Befehl liefert uns das Kommande eine neue ID zurück, die wir uns in die Zwischenablage kopieren sollten. Führen wir jetzt erneut bcdedit ohne Parameter aus, sehen wir, das es zusätzlich einen neuen Eintrag mit dem von uns vergebenen Namen gibt.

Als nächstes müssen dem neu erstellten Eintrag einige Parameter setzen:

c:\bcdedit /set {guid} device vhd=[driveletter:]\{directory}\{vhdfilename}.vhd
c:\bcdedit /set {guid} osdevice vhd=[driveletter:]\{directory}\{vhdfilename}.vhd
c:\bcdedit /set {guid} detecthal on

Was passiert hier: Zuerst setzen wir den Parameter device und geben den Dateipfad zu dem VHD File an, das wir booten möchten. Das gleiche passiert für den Parameter osdevice. detecthal gibt an, dass sich der Bootmanager selbst darum kümmern soll, welchen Kernel und HAL (Hardware Abstraction Layer) er laden soll. guid ist die ID des Eintrags, die wir vorher nach dem kopieren bekommen haben und die eigentlich in der Zwischenablage liegen sollte. Der Verzeichnispfad muss einer gewissen Syntax entsprechen (siehe oben) und könnte bspw. so aussehen:

c:\bcdedit /set {5f9fb961-9357-11de-8ce0-86b14e4a0753} device vhd=[D:]\Images\W7_Evaluation.vhd
c:\bcdedit /set {5f9fb961-9357-11de-8ce0-86b14e4a0753} osdevice vhd=[D:]\Images\W7_Evaluation.vhd
c:\bcdedit /set {5f9fb961-9357-11de-8ce0-86b14e4a0753} detecthal on

Jetzt sind wir eigentlich fertig. Wir können nun das System runterfahren und wollten vor dem Booten den neuen Eintrag im Bootmanager sehen. Falls es das erste Image ist, das wir in den Bootmanager gehängt haben, brauchen wir sonst nichts tun. Der Bootmanager erkennt selbständig, dass es nun mehrere Optionen gibt und listet die möglichen Systeme von selbst auf.

Möchte man bestehende Systeme aus dem Bootmanager rauslöschen, geht das ebenfalls über bcdedit

c:\bcdedit /delete {guid} /cleanup

{guid} ist die ID des Eintrags, den wir löschen wollen. Die ID finden wir, wenn wir über bcdedit /v alle bestehenden Einträge auflisten.

Weitere Infos zu bcddedit findet man unter http://technet.microsoft.com/en-us/library/cc709667%28WS.10%29.aspx.

In diesem Sinne.

Jan 30

HowTo: Custom ToolStrip Rendering für Windows Forms

Heute möcht ich mal kurz und bündig erklären, wie man es ohne allzugroßen Aufwand schafft, ein Windows Forms ToolStrip-Control so zu customizen, das es dem Layout und Design der eigenen Anwendungen entspricht. Will man ein ToolStrip-Control an die eigenen Bedürfnisse anpassen, hat man grundsätzlich 2 Möglichkeiten. Beide Varianten haben gemeinsam, dass man zuerst ein CustomControl erstellt und dies von System.Windows.Forms.ToolStrip ableitet:


public partial class CustomToolStrip : System.Windows.Forms.ToolStrip
{
   public CustomToolStrip()
   {
      InitializeComponent();
   }
}

Nachdem wir das Control haben, möchten wir es anpassen. Jetzt können wir einerseits in der OnPaint() Methode das gesamte Control selbst neu rendern. Das führt dazu, dass wir alles neu und selbst zeichnen müssen (also alle Buttons, Items, Rahmen, Hintergrund, etc.) und die notwendige Funktionalität (Click, Hover, etc. – Events) implementieren. Das bedeutet natürlich einen ehrheblichen Aufwand. Daher verwenden wir eine bereits im .NET Framework mitgelieferte Klasse names System.Windows.Forms.ToolStripRenderer.

System.Windows.Forms.ToolStripRenderer
Leitet man eine Klasse von dieser Basisklasse ab, hat man zahlreiche Möglichkeiten, in den Renderprozess des ToolStrip-Controls einzugreifen (genaueres findet man hiert http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstriprenderer.aspx). Implementieren wir die Klasse, können wir zB in den Renderprozess des Rahmens eingreifen:


public class CustomToolStripRenderer : System.Windows.Forms.ToolStripRenderer
{
   protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
   {
      e.Graphics.DrawLine(new System.Drawing.Pen(Color.Red),
                          0, e.AffectedBounds.Height - 1,
                          e.AffectedBounds.Width, e.AffectedBounds.Height - 1);
   }
}

Damit wir den Renderer auch anwenden, reicht es, in dem CustomToolStrip Control die Property Renderer auf einen neue Instanz des ToolStripRenderers zu setzen:


this.Renderer = new CustomToolStripRenderer();

In diesem Sinne. Beispielcode gitbs wie immer hier.

Jan 11

HowTo: Dynamische Objekte mit System.Reflection.Emit

Um was gehts heute? Ich stand vor kurzem vor der Herausforderung dynamisch, generische SQL Queries für unterschiedliche Such-Abfragen abzusetzen. Was mein ich damit? Ich hab in einer Datenbank SQL-Select-Statements hinterlegt, die ich nun dynamisch ausführen will und die Ergebnismenge in einem WPF ListView anzeigen will.

Wie lösen wir das?
Der Großteil stellt uns vor keine allzugroßen Problemen. Wenn wir das Problem mal in kleine Teilprobleme aufstückeln, kommen wir auf folgende Bereich:

  • Dynamische SQL-Select-Statements ausführen;
  • Generische Ergebnismenge auslesen und im Speicher sinnvoll ablegen;
  • Generische Ergebnismenge anzeigen
  • [Das Ganze drum herum, das notwendig ist, um die vorherigen drei Punkte zu lösen]

Dynamische SQL-Select-Statements ausführen
Gehen wir mal davon aus, das wir bereits über ein Repository die notwendigen Daten für unsere SQL Statements aus einer Datenbank gelesen haben und folgendes Model daraus erzeugt haben:


public class SearchScript
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string Description { get; set; }
   public string Sql { get; set; }
   public DateTime CreatedAt { get; set; }
}

Logischerweise liegt in der Sql Property unser SQL-Select-Statement, das wir später ausführen wollen. Um das Skript jetzt auf der Datenbank jetzt abzusezten, haben wir mehrere Möglichkeiten: Entweder setzen wir das SQL Statement direkt über ADO.NET ab, rufen eine Stored Procedure auf, etc. Ich möchte für diese Beispiel den Weg der Stored Procedure gehen, weil ich denke, das es am wartungsfreundlichsten ist, da, möchte man zusätzliche Task vor oder nach dem SQL Statemant durchführen, man nur die Stored Procedure abändern muss und den .NET nicht neu kompilieren braucht.

Die Stored Procedure sieht relative einfach aus:


ALTER PROCEDURE dbo.ExecuteSqlScript
                @script AS nvarchar(4000)
AS

BEGIN
    SET NOCOUNT ON;

    EXECUTE sp_executesql @script
END

Wir übergeben der SP einfach das SQL Statement als String und führen dieses mit der bereits vorhandenen (out of the box) Stored Procedure sp_executesql (http://msdn.microsoft.com/de-de/library/ms188001.aspx)  aus. Ziemlich einfach und logisch. Wollen wir noch weitere Tasks in der SP ausführen, könnten wir diese ohne weiteres jetzt einfach und jederzeit hinzunehmen. Interessant ist auf alle Fälle, das uns die SP ein ResultSet des ausgeführeten SQL-Select-Statements zurückliefert.

Generische Ergebnismenge auslesen und im Speicher sinnvoll ablegen
Um nicht mit untypisierten Listen bzw. zu tiefen Listen- Hierachien arbeiten zu müssen, versuchen wir, aus der generischen Ergebnismenge typisierte Datentypen zu erzeugen. Da gibts einerseits mal das Konzept der anonymen Typen (http://msdn.microsoft.com/de-de/library/bb397696.aspx): Problem dabei ist, dass wir diese nicht dynamisch zur Laufzeit erstellen können. Wir gehen daher einen anderen Weg: Verwendung von System.Reflection.Emit.

Mit Hilfe des Namespaces ist es uns möglich, zur Laufzeit dynamisch Assemblies, Typen, etc. zu generieren. Als erstes müssen wir mal unsere Stored Procedure mithilfe von ADO.NET aufrufen, um auf das ResultSet, das uns die SP liefert, zugreifen zu können:


SqlConnection connection = null;
SqlDataReader reader = null;

connection = new SqlConnection("_ConnectionString_");
connection.Open();

SqlCommand cmd = new SqlCommand("Name_Of_Stored_Procedure", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@script", SqlDbType.NVarChar).Value = "select * from table_name";

reader = cmd.ExecuteReader();

Nachdem wir jetzt unser SqlReader haben, können wir anfangen aufgrund der Ergebnismenge den dynamischen Typ zu erzeugen. Zuerst erzeugen wir uns mal das Assembly und die notwendige Klasse:


AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("AssemblyName"), AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = ab.DefineDynamicModule("ModuleName");
TypeBuilder tb = modBuilder.DefineType("ClassName", TypeAttributes.Class | TypeAttributes.Public);

Den zweiten Parameter der Methode DefineDynamicAssembly() setzen wir auf Run, weil wir das dynamische Assembly nicht lokal am Filesystem speichern wollen sondern nur zur Laufzeit benötigen. Als nächstes müssen wir der neuen Klasse die notwendigen Properties zuweisen indem wir alle Felder (Spaltennamen) des SqlReaders auslesen und für jede gelesene Spalte eine öffentliche Property erzeugen:

for (int i = 0; i < reader.FieldCount; i++)
{
   string propertyName = reader.GetName(i);
   Type propertyType = reader.GetFieldType(i);

   FieldBuilder propertyFieldBuilder = tb.DefineField(string.Format("_{0}", propertyName.ToLower()), typeof(string), FieldAttributes.Private);

   PropertyBuilder propertyPropertyBuilder = tb.DefineProperty(propertyName, System.Reflection.PropertyAttributes.None, propertyType, null);

   MethodBuilder propertyGetter = tb.DefineMethod(string.Format("get_{0}", propertyName),
                                                  MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                                                  propertyType, Type.EmptyTypes);

   ILGenerator custNameGetIL = propertyGetter.GetILGenerator();
   custNameGetIL.Emit(OpCodes.Ldarg_0);
   custNameGetIL.Emit(OpCodes.Ldfld, propertyFieldBuilder);
   custNameGetIL.Emit(OpCodes.Ret);

   MethodBuilder propertySetter = tb.DefineMethod(string.Format("set_{0}", propertyName),
                                                  MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                                                  null, new Type[] { propertyType });

   ILGenerator custNameSetIL = propertySetter.GetILGenerator();
   custNameSetIL.Emit(OpCodes.Ldarg_0);
   custNameSetIL.Emit(OpCodes.Ldarg_1);
   custNameSetIL.Emit(OpCodes.Stfld, propertyFieldBuilder);
   custNameSetIL.Emit(OpCodes.Ret);

   propertyPropertyBuilder.SetGetMethod(propertyGetter);
   propertyPropertyBuilder.SetSetMethod(propertySetter);
}

Der Code ist relativ einfach: Wir iterieren über alle Spalten des SqlReaders und erzeugen uns eine Property, die wir auf die Klasse setzen. Als erstes erzeugen wir uns ein privates Feld und eine öffentliche Property. Jetzt brauchen wir für die Property noch Getter und Setter Methoden, die wir ganz am Ende auch dementsprechend auf die Property setzen (SetGetMethod() und SetSetMethod()). Die Parameter der ILGenerator.Emit() Methode sind nicht ganz einfach zu verstehen und können unter http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes_members.aspx nachgelesen werden. Haben wir die dynamische Klasse, können wir aus dem TypeBuilder einen Typ bilden, mit dem wir später Instanzen der soeben erzeugten Klasse erzeugen können:


   Type dynamicType = tb.CreateType();

Nachdem wir jetzt unsere dynamische Klasse aufgrund des ResultSets des SQL-Select-Statements erzeugt haben, müssen wir noch Instanzen der dynamischen Klasse erzeugen und mit dem Ergebnis des Select-Statements befüllen:


   List<object> result = new List<object>();

   while (reader.Read())
   {
      object item = Activator.CreateInstance(dynamicType);
      if (item == null) throw new InvalidProgramException("Cannot create instance of dynamic type.");

      for (int i = 0; i < reader.FieldCount; i++)
      {
         PropertyInfo property = scriptType.GetProperty(reader.GetName(i));
         if (property == null || reader.IsDBNull(i)) continue;

         property.SetValue(item, reader.GetValue(i), null);
   }

   result.Add(item);
 }

Der Code erzeugt zunächst eine Instanz der dynamischen Klasse. Anschließend iterieren wir über alle verfügbaren Spalten des SqlReaders und holen uns die Property der dynamischen Klasse, die dem aktuellen Spaltennamen entspricht (da wir zuvor die dynamische Klasse genauso erzeugt haben, sollten eigentlich alle Properties zur Verfügung stehen). Haben wir die Property, dann befüllen wir sie mit dem Wert des aktuellen Datensatzes des SqlReaders. Sobald wir über alle Felder des SqlReaders interiert haben, können wir die Instanz der dynamischen Klasse zu einer Liste hinzufügen.

Generische Ergebnismenge anzeigen
Nachdem wir die gelesenen Daten nun in einem mehr oder weniger typisierten Objekt gespeichert haben, wollen wir die Liste der Resultate auch über WPF visualisieren.  Als erstes verwenden wir eine Instanz von ListView und müssen leider feststellen, das WPF mit dem dynamischen Objekt nicht zurecht kommt (warum ist mir nach wie vor noch nicht ganz klar). Daher erzeugen wir uns unser eigenes CustomControl das wir von ListView ableiten. Als ersten interessieren wir uns für den Loaded-Event, bei dem wir per Code die notwendigen Spalten auf die ListView setzen:


private void _GenerateColumns()
{
   GridView view = new GridView();

   PropertyInfo[] properties = dynamicType.GetProperties();
   if (properties == null || properties.Count() <= 0) return;

   foreach (var property in properties)
   {
      GridViewColumn column = new GridViewColumn();

      column.Header = property.Name;
      column.DisplayMemberBinding = new Binding(property.Name);

      view.Columns.Add(column);
   }

   this.View = view;
}

public CustomListView()
{
   this.Loaded += (sender, e) => _GenerateColumns();
}

Wir lesen einfach alle Properties des dynamischen Typs aus und erzeugen uns für jede Property eine Spalte in unserer ListView, indem wir den Namen der Property als Header/Bezeichnung für die Spalte verwenden und die DisplayMemberBinding-Property setzen (damit die ListView ihr Datenbinding richtig darstellen kann).

Das wars im Großen und Ganzen. Die CustomListView kann jetzt in jeder XAML Datei verwendet werden und (verwendet man MVVM) auf eine Property eines ViewModels “datagebindet” werden. Demoprojekt gibts hier.

In diesem Sinne.

Jan 05

Auflösen von VSS Abhängigkeiten bei Continuous Integration mit Teamcity

Wer hat schon mal eine Visual Studio Solution erstellt und darin ein oder mehrere Projekte aus einer anderen VSS Location dazugehängt? Das Ganze ist ziemlich praktisch, weil man so erreichen kann, gewisse Funktionalität auszulagern und in einer eigenen Bibiliothek zusammen zu fassen (ich denk da an firmeninterne Bibiliotheken) – die man zentral im VSS ablegen kann und später bei jedem neuen Projekt einfach aus VSS heraus laden und verwenden kann.

Wo gibts jetzt ein Problem?

Problematisch wirds, sobald man das Projekt als CI laufen hat und dort unter anderem einen Build (Release || Debug) des Projektes erzeugen will.

Warum: Der Build Runner (Nant) ist nicht in der Lage, die logischen Abhängigkeiten, die im *.sln File angegeben sind, richtig aufzulösen. MSBuild geht beim Kompilieren davon aus, das auf einer gewissen Filelocation eigentlich eine VS Projekt Datei liegt, die das Tool aber dann nicht findet — klar, weil es nicht aus dem VSS geladen wurde (Visual Studio würde solche Abhängigkeiten erkennen und den Benutzer darüber informieren, dass er die Abhängigkeiten nachladen muss). Somit schlägt der Kompilier-Versuch fehl und der Build Runner beendet den Build mit irgendeinem Fehler.

Nicht gut, was tun?
Um diese Abhängigkeiten trotzdem aufzulösen gibts im Contrib-Projekt (http://nantcontrib.sourceforge.net/) für NAnt einen paar gute Tasks, die sich mit VSS beschäftigen:

  • vssadd
  • vsscheckin
  • vsscheckout
  • vssdelete
  • vssget
  • vsshistory
  • vsslabel
  • vssundocheckout

Für unser Problem verwenden wir “vssget”, da wir mit dem Task im die aktuelle Version einer Datei oder eines ganzen VS Projekts laden können. Konfiguration ist ziemlich einfach:

</p>
<property name="vss.user" value="tiger"/>
<property name="vss.password" value="woods"/>
<property name="vss.dbpath" value="\\servername\srcsafe.ini"/>

<vssget username="${vss.user}" password="${vss.user}" dbpath="${vss.dbpath}"
        recursive="true" replace="true" writable="false"
        localpath="Relative_LocalPath_On_BuildServer"
        path="$/Path_In_Source/" /><br />

Was erreichen wir damit: Wir laden ein Projekt aus dem VSS und legen es lokal auf dem Build Server ab, damit beim Kompilieren über den MSBuild Task (http://nantcontrib.sourceforge.net/release/latest/help/tasks/msbuild.html) alle Abhängigkeiten, die ja in der VS Solution bestehen, gefunden und aufgelöst werden können.

Ziemlich einfach — nur sollte man es wissen.
In diesem Sinne.

Nov 28

HowTo: Fluent Interface in Repositories einsetzen

Heute möchte ich mich mal mit der Idee der Fluent Interfaces beschäftigen. Mit Fluent Interfaces ist es möglich, Programmcode in “Satzform” niederzuschreiben und somit wesentlich lesbarer wird.

Wie funktioniert ein Fluent Interface?
Schauen wir uns zuerst ein einfaches Beispiel an:

var obj = new Foo();
obj.Start()
   .Execute()
   .Stop();

In dem Beispielcode wird auf ein Objekt vom Typ Foo zuerst eine Methode Start() aufgerufen. Diese Methode liefert als Rückgabewert wiederrum ein Objekt vom Typ Foo. Dadurch ist es möglich, auf die Methode wiederrum eine Methode (nämlich Execute()) des Objekts Foo aufzurufen. Da auch diese Methode wieder ein Objekt vom Typ Foo liefert, kann darauf die letzte Methode Stop() aufgerufen werden. Ziemlich easy und der Code wird um einiges lesbarer. Die Implementierung der Klasse Foo sieht so aus:

public class Foo
{
   public Foo Start()
   {
      return this;
   }

   public Foo Execute()
   {
      return this;
   }

   public Foo Stop()
   {
      return this;
   }
}

Das Ganze ist auch unter Method Chaining bekannt und wird zum Beispiel bei der beliebten Javascript Bibliothek jQuery eingesetzt. Mehr Infos zu Fluent Interfaces gibts hier http://www.bjoernrochel.de/2008/08/10/implementing-a-fluent-api-for-the-ribbon.

Fluent Interfaces für Repositories
Neben den vielen Vorteilen, die Fluent Interfaces mit sich bringen und den vielen Einsatzmöglichkeiten, die sich für Fluent Interfaces ergeben, möchte ich auf  eine detailierter eingehen, da diese bei jeder Business- und datengetriebenen Anwendung zum Einsatz kommt (sollte): Das Repository Pattern. Wer noch keine Erfahrung mit diesem Pattern hat, kann sich mal unter schlau http://martinfowler.com/eaaCatalog/repository.html und http://blog.wekeroad.com/mvc-storefront/asp-net-mvc-mvc-storefront-part-2/ machen.

Sehen wir uns einfach folgendes Beispiel an, dass auf einen LINQ2SQL Context zurückgreift:


public class PersonRepository
{
   private static DataContext _DataContext { get; set; }

   public IQueryable<Person> Find()
   {
      return from person in _DataContext.Persons
             select person;
   }

   static PersonRepository()
   {
      _DataContext = new FluentDataContext();
   }
}

Über die Extension-Methods, die mit C# 3.0 gekommen sind, kann man jetzt so ein Fluent Interface ganz einfach implementieren:


public static class PersonExtensions
{
   public static IQueryable<Person> WithName(this IQueryable<Person> items,
                                             string name)
   {
      if (items == null)
         throw new ArgumentNullException("IQueryable<Person> is null.");
      if (string.IsNullOrEmpty(name))
         throw new ArgumentNullException("Found invalid compare-string for name.");

      return items.Where(person => person.Name.CompareTo(name) == 0);
   }

   public static IQueryable<Person> WithName(this IQueryable<Person> items,
                                             DateTime birthday)
   {
      if (items == null)
         throw new ArgumentNullException("IQueryable<Person> is null.");

      return items.Where(person => person.Birthday.Equals(birthday));
   }
}

Rob Conery hat in einem Screencast diese Idee genauer beschrieben (er nennt es hier Pipes & Filters): http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Das Problem mit POCOs!
Ein Problem, das in meinem Projekten immer wieder auftritt, ist, dass ich solche Pipes & Filters nur dann einsetzen kann, wenn ich in meinem Model-Layer keine POCOs einsetze und bei LINQ2SQL direkt mit den automatisch generierten Model-Klassen arbeite. Nur genau das will ich nicht: Ich will mit meinen eigenen Domain-Model-Klassen arbeiten, um keine Abhängigkeiten in den restlichen Layern auf das LINQ Model zu bekommen. Um das zu erreichen, müssen wir unser Repository ändern (wobei <Person> unser eigenes Domainmodel ist und <DBPerson> das LINQ2SQL Model ist):


public IQueryable<Person> Find()
{
   return from person in _DataContext.DBPersons
          select new Person
                {
                   Id = person.Id,
                   Name = person.Name,
                   Birthday = person.Birthday
                };
}

Gut so. Jetzt mappen wir direkt im Repository auf unser Domainmodel. Das Problem, dass jetzt aber entsteht ist, das wir unsere Extension-Methods so nicht mehr verwenden können, da die nur mit dem LINQ2SQL Model funktionieren. Jetzt können wir diese Methoden auf unser Domainmodel umändern und wir hätten die Möglichkeit über Fluent Interfaces die Daten abzufragen:


var rep = new PersonRepository();
var items = rep.Find().WithName("John");

Bei diesem Codefragment erkennt man schon die Problematik, die wir nun schlußendlich haben: Wir müssen zuerst alle Daten aus der Datenquelle lesen, auf unser Domainmodel mappen und erst dann über die Extension-Methods weiter aussortieren. Bei einer SQL Tabelle, die mehrere Tausend Datensätze speichert ist dieser Workflow definitiv nicht gewünscht und auch nicht empfehlenswert.

Implicit Cast Operator
Um dieses Problem zu lösen, verwenden wir ein tolles Feature von C#: Cast Operatoren (http://msdn.microsoft.com/en-us/library/85w54y0a%28VS.80%29.aspx). Sehen wir uns folgende Klasse an:


public class FluentPerson
{
   private IQueryable<DBPerson> _Items { get; set; }

   public FluentPerson WithName(string name)
   {
      _Items = _Items.Where(person => person.Name.CompareTo(name) == 0);
      return this;
   }

   public FluentPerson WithBirthday(DateTime birthday)
   {
      _Items = _Items.Where(person => person.Birthday.Equals(birthday));
      return this;
   }

   public static implicit operator List<Person>(FluentPerson fluent)
   {
      return (from person in fluent._Items
              select new Person
                  {
                     Id = person.Id,
                     Name = person.Name,
                     Birthday = person.Birthday
                  }).ToList<Person>();
   }

   public FluentPerson(IQueryable<DBPerson> items)
   {
      _Items = items;
   }
}

Diese Klasse verwaltet eine Liste von DBPerson, die immer die aktuellen, noch verfügbaren DBPersons beinhaltet. In den einzelnen Methoden geben wir immer die Instanz der Klasse selbst zurück – somit haben wir ein Fluent Interface. Im Implicit Cast Operator erzeugen wir nun aus der Liste der LINQ2SQL Daten eine Liste unseres Domainmodels. Das tolle dabei nun, dass LINQ die Query auf die Datenbank erst dann absetzt, sobald die Daten, die gelesen werden sollen, im Programmcode auch wirklich verwendet werden – dh, in unserem Fall wird die Query nicht in den Fluent Interface Methoden (WithName() oder WithBirthday()) sondern erst im Implict Cast Operator abgesetzt (weil wir dort auch tatsächlich auf die Daten der Datenbank zugreifen). Tolle Sache. Um das Ganze noch verwenden zu können, speichert nun unser Repository eine Instanz dieser FluentPerson Klasse. Verwenden können wir das Ganze nun so:


var repository = new PersonRepository();
List<Person> persons = repository.Fluent.WithName("Joana").
                                        .WithBirtday(new DateTime(2000, 12, 24));
persons.ForEach(person => Console.WriteLine("{0}, {1}", person.Name, person.Birthday.ToShortDateString()));

Sehr sinnvoll das Ganze. Beispiel gibts wie immer hier: http://downloads.juergenoberngruber.at/blog/FluentRepositories.zip

In diesem Sinne.

Older Posts »
  • Recent Posts
    • HowTo: Eigene Regeln für Microsoft StyleCop erstellen
    • HowTo: Microsoft StyleCop Integration mit Visual Studio und MSBuild
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 2
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 1
    • Performance des Cassini Webservers in Kombination mit Firefox
  • Archives
    • June 2010 (2)
    • May 2010 (3)
    • March 2010 (3)
    • February 2010 (4)
    • January 2010 (3)
    • December 2009 (1)
    • November 2009 (9)
  • Tags
    .net AOP ASP.NET ASP.NET MVC blend Bootmanager C# ci Codequality Configuration Continuous Integration css Cursor DateTime DDD Deployment dynamisch emit Exrpession Extensibility Fluent HowTo Microsoft MVVM Pattern PostSharp Reflection Repository ruby silverlight Software Design StyleCop System teamcity Templates VHD Virtual Images Visual Studio Vorlagen web Windows 7 Windows Mobile WinForms WPF XAML
  • About

    Jürgen Oberngruber is a project manager and software architect living in Wels, Austria and currently working at ecomplexx Austria, Wels. During his study at the University of Applied Sciences in Hagenberg, Austria he gained a deep knowledge in the field of software engineering using a lot of different programming languages. Since a few years he's focusing on the Microsofts .NET platform including all relevant technologies. One of his passion is to explore, to test and to evaluate new technologies and programming languages (mostly in the field of Microsofts .NET platform). Checkout more information on www.juergenoberngruber.at

  • Meta
    • Log in
    • Entries RSS
    • Comments RSS
    • WordPress.org
  • Archives
    • June 2010
    • May 2010
    • March 2010
    • February 2010
    • January 2010
    • December 2009
    • November 2009
  • Search






  • Home

© Copyright Jürgen Oberngruber's Blog. All rights reserved.
Designed by FTL WordPress Themes brought to you by DT Web Template

Back to Top