Dienstag, 30. August 2011

ASP.NET Skillscamp 2011–Der Tag danach

Das war es also, das erste ASP.NET Skillscamp (@Skillscamp) 2011 in Köln und gleichzeitig das erste Community Event, an dem ich teilgenommen habe. Alles in allem kann ich, zumindest für mich, eine positive Bilanz ziehen: Ich durfte interessante Personen kennen lernen, deren Bilder und Artikel ich bisher nur kannte, und an vielen, zum Teil spannenden Gesprächen teilnehmen.

Hier folgt nun meine kurze, persönliche Zusammenfassung der Tage.

Freitag - Das Vorspiel

Der Freitag Abend begann um 19.00h in Peters Brauhaus in der Kölner Altstadt und Albert Weinert (seines Zeichens der Organisator) nahm die Teilnehmer in Empfang. Nach und nach (je nach Verspätung der öffentlichen Verkehrsmittel) trudelten die Meisten bis auf wenige Ausnahmen auch ein. Mit zunehmenden Kennenlernen (wobei sich viele der üblichen Verdächtigen bereits kannten), ersten Gesprächen und auch den ersten Kölsch kamen die Themen für den Open Space Samstag zusammen. Es kristallisierte sich relativ schnell heraus, dass ein großer Schwerpunkt auf Javascript und den verschiedenen Frameworks, vor allem Knockout.js, Backbone.js und Node.js, liegen würde. Nicht Fehlen durften natürlich auch Themen wie ASP.NET MVC, HTML5 und REST mit WCF.

Mein persönliches (Negativ-)Highlight des abends hatte allerdings nicht primär mit der Veranstaltung zu tun, sondern dem Chaos und Anarchie ÖPNV. Der hat dafür gesorgt, dass ich nachts die Strecke vom Kölner Bahnhof nach Bonn-Beuel im Fabelrekord von unglaublichen 2,75 Stunden (in Worten: zwei Stunden und fünfundvierzig Minuten) zurückgelegt habe. Vielen Dank noch an dieser Stelle für die unglaubliche Unterstützung des VRS, ohne den diese Leistung nie möglich gewesen wäre!

Samstag - Der Open Space

Samstag morgen standen wir erst einmal mit versammelter Mannschaft vor verschlossener Tür, weil irgendein Spaßvogel die Tür zufallen lassen hat. Macht nix, wir standen schließlich fast alle draußen Zwinkerndes Smiley

Nach der Begrüßung und allgemeinen Vorrede zum Skillscamp durch Albert, wurde der Session Plan, vorerst nur für den Vormittag, zusammen gestellt. Aufgrund des allgemeinen Interesses und der relativ kleinen Zahl an Teilnehmern, gab es dann morgens auch nur eine erste Session zum Thema Javascript Frameworks - die auch gleich den größten der bereitstellten Räume der GFU sprengte. Diese erste Session war dann vor allem durch die Ausführungen von Albert geprägt.

Die zweite Session konnte auf zwei Slots aufgeteilt werden. Für die Einen gab es Node.js, für die Anderen Html5 (darüber kann ich leider nichts berichten). Der Versuch in der zweiten Session, unter der Schirmherrschaft von Daniel Fisher, IISNode.js auf dem IIS zum Laufen zu bringen, scheiterte leider. Es war zwar relativ schnell eine lauffähige Version anhand des Blogs von Tomek (der die letzten Tage ziemlich Kreise gelaufen hat) aufgesetzt, der Versuch eine eigene Seite anzuzeigen scheiterte jedoch. Und leider nicht nur das: Abgesehen davon, dass der Webserver einen 503er Fehler generierte, wurden zudem keinerlei brauchbare Loginformationen in die Logdatei oder das Eventlog geschrieben. Damit war relativ schnell (vorerst) der Stempel “noch nicht produktionstauglich” vergeben (Vielleicht war es aber auch nur die lang überfällige Zigarettenpause Zwinkerndes Smiley ! Mittlerweile ist ein Lösungsansatz von LennyBacon bei Scott Hanselman gefunden worden)…
Weiter ging es zur Lokalisation mit ASP.NET MVC in Session 3, der letzten vor dem Mittagessen. Hier war es mal wieder Daniel Fisher, der, auf eine konkrete Frage zur Lokalisation von Thomas Bandt hin, alle Möglichkeiten der Lokalisierung vom Server bis zum Client vorstellte.

Nach dem Mittagessen, wurde die Planung des Nachmittags vorgenommen. Schon etwas auffällig war, dass Daniel Fisher einen sehr unruhigen Arm hat, wenn es um die Frage geht, wer mit einer bestimmten Technologie/einem bestimmten Framework bereits Erfahrung hat und darüber auch berichten möchte/kann. Dementsprechend wurde der Nachmittag wieder vor allem durch Albert und Daniel geprägt, insgesamt fand aber viel mehr Interaktion statt als noch am Vormittag. Grund war sicherlich auch, dass das erste Thema nach dem Mittag “Best-Practices in MVC” war und fast jeder in Form von Erfahrungen oder aber konkreter Fragen dazu beitragen konnte.

Auf den Themenkomplex REST und WCF wurde bewusst verzichtet. Der einzige, der zu diesem Thema aktiv mitgestalten konnte/wollte, war, wie konnte es auch mal wieder anders sein: Daniel Fisher… Und da er seine Meinung als nicht unbedingt massenkompatibel bezeichnete und Alexander Zeitler kurzfristig abgesagt hatte, wurde das Thema fallen lassen (kleiner Wink mit dem Zaunpfahl an dieser Stelle an Alexander Zeitler!).

Den Abschluss des Tages bildete schließlich ein Rundflug durch das Javascript-Tooling und sämtliche Javascript Frameworks, die von den Teilnehmern schon einmal eingesetzt wurden (für besondere Erheiterung sorgte: raptorize your screen Smiley) mit einer kleinen Vorstellung über Inhalt und Funktionsweise. Hier kam wirklich fast alles mal kurz auf den Tisch, ich könnte bestimmt nur noch die Hälfte nennen…

Das Nachspiel

Den abendlichen Nachgesang konnte ich dann aus persönlichen Gründen nicht mehr aktiv miterleben (gut unterrichteten Quellen war aber zu entnehmen, dass es bis spät in die Nacht ging). Ich habe mir aber für das nächste Mal vorgenommen auch hier dabei zu sein.

Zum Schluss bleibt mir nur noch anzumerken, dass es mir persönlich sehr gefallen hat. Allein schon deshalb, dass man Gesicherten und Texten reale Personen zuordnen und diese kennen lernen konnte. Organisatorisch hätte mir persönlich eine Aufteilung in kleinere Gruppen besser gefallen, da das mit Sicherheit eine intensivere, tiefer gehende Diskussion in der einzelnen Session erlaubt hätte, als es letztlich in den/der großen Gruppe möglich gewesen ist. Und ansonsten ließe sich sicher auch die eine oder Andere kleine Optimierung finden. Entgegen des häufig geäußerten Wunsches von Albert, dass sich alle in die Runde einbringen, lief es doch zu Teil zu sehr auf ein reines konsumieren hinaus. Alles in allem war es aber aus meiner Sicht ein gelungenes erstes (Wink an Albert!) Skillscamp und ich freue mich schon auf die Fortsetzung.

Sollte ich etwas vergessen haben, könnt ihr mich gerne anschreiben und ergänzen oder einfach selbst noch einmal etwas dazu schreiben.

Es bleibt mir nur noch einmal Albert Weinert herzlich für die Idee, Planung/Organisation und Durchführung zu Danken und ebenso allen Sponsoren, die das erste Skillscamp überhaupt möglich gemacht haben. Bis zum nächsten Mal!

Kick It on dotnet-kicks.de

Dienstag, 19. Juli 2011

jQuery auf Koffein

Der Name CoffeeScript geistert schon eine Weile umher. Viele haben bereits in Verbindung mit Node.js damit gearbeitet. Da ich in letzter Zeit weder selbige noch große Lust hatte mir ein Ubuntu oder eine andere Linux Distribution meines Vertrauens zu installieren, habe ich es einfach vorerst ignoriert… Jetzt habe ich vor kurzem Mindscapes Web Workbench entdeckt.

Die Web Workbench ist eine Visual Studio 2010 Erweiterung zur Integration von LESS, SASS und Coffee Script. Und gerade das letzte, ohne die Notwendigkeit Node.js installieren zu müssen. Jetzt doch etwas neugierig habe ich es von Microsoft heruntergeladen, installiert und losgelegt.

Zum ersten Herumspielen musste einfach eine ASP.NET MVC Anwendung herhalten. Die Anwendung blieb soweit unverändert und gleich erstmal eine CoffeeScript Datei hinzufügen. Wie man in der folgenden Abbildung sehen kann, werden nun drei neue Dateitypen angeboten: sass SCSS Style Sheet, Less Style Sheet und CoffeeScript File.

CoffeeScript

Also letzteres hinzugefügt und es erscheinen in dem Projekt zwei neue Dateien. Eine Datei mit der Endung .coffee und eine mit der Endung .js. Die .coffee Datei ist natürlich die Datei, die das Coffee Script enthalten wird. Die .js Datei enthält den automatisch aus dem Coffee Script erzeugten Javascript Code.

CoffeeScriptFiles

Als einfaches, kurzes Beispiel wollte ich einfach eine Slideshow mit jQuery erstellen und laufen lassen. Meine Index.cshtml habe ich dazu um die für die Slideshow notwendigen Basis-Divs erweitert. Da ich erst einmal nur ein Gefühl dafür bekommen wollte, wurde ein div zu drei…

   1:  <div id="slideShow"> 
   2:      <div> 
   3:          <img src="@Url.Content("~/public/images/SlideShowPicture.png")" width="200" 
   4:              height="200" alt="Ein Park" /> 
   5:          <p> 
   6:              Hier ist ein schönes Sommerbild</p> 
   7:          <p> 
   8:              Dieser Park war ein schöner Spielplatz für alle Kinder. 
   9:          </p> 
  10:      </div> 
  11:      <div> 
  12:          <img src="@Url.Content("~/public/images/SlideShowPicture.png")" width="200" 
  13:              height="200" alt="Ein Park" /> 
  14:          <p> 
  15:              Hier ist ein schönes Sommerbild</p> 
  16:          <p> 
  17:              Dieser Park war ein schöner Spielplatz für alle Kinder. 
  18:          </p> 
  19:      </div> 
  20:      <div> 
  21:          <img src="@Url.Content("~/public/images/SlideShowPicture.png")" width="200" 
  22:              height="200" alt="Ein Park" /> 
  23:          <p> 
  24:              Hier ist ein schönes Sommerbild</p> 
  25:          <p> 
  26:              Dieser Park war ein schöner Spielplatz für alle Kinder. 
  27:          </p> 
  28:      </div> 
  29:  </div> 

Jetzt nur noch das entsprechende Coffee Script für einen einfachen Slideshow Aufruf schreiben. Dabei habe ich mich wirklich nicht überfordert und den folgenden Einzeiler geschrieben.

   1:  $('#slideShow').cycle 
   2:    fx:    'scrollRight',
   3:    delay: -2000 

Meine Neugier trieb mich gleich in die Javascript Datei uns siehe da: es war bereits umgewandelt.

   1:  (function() {
   2:    $('#slideShow').cycle({
   3:      fx: 'scrollRight',
   4:      delay: -2000
   5:    });
   6:  }).call(this);

Nun noch schnell die Javascript Datei in die Webseite eingebunden und schnell F5 gedrückt. Nach einem kurzen Augenblick zeigte sich dann die Standard Webseite – aber um eine von links nach rechts durchschiebende Slideshow reicher…

FertigeSlideShow

Was kann man zu so einem Beispiel schon groß sagen? Es funktioniert einwandfrei und erfüllt in diesem Einfachstbeispiel seinen Zweck. Inwiefern komplexere Anwendungen damit entwickelt werden können, wird sich noch zeigen.

Es ist aber definitiv eine interessante Alternative… und ich kann endlich mit Coffee Script arbeiten. Mit Kaffee arbeitet es sich einfach besser Zwinkerndes Smiley.

Kick It on dotnet-kicks.de

Montag, 4. Juli 2011

Alles neu macht der Mai

Es ist zwar nicht mehr Mai, aber die Schatten ziehen sich noch immer etwas.

Nachdem ich Ende Mai meinen Arbeitgeber gewechselt habe, hat sich auch meine berufliche Fokussierung etwas gewandelt. Damit einhergehend wird sich in Zukunft mein Schwerpunkt immer weiter weg von VSTO und OpenXml Themen verschieben.

Ich freue mich auch darauf, denn etwas von dem, was ich gelernt habe ist: Automatisierte Dokumentgenerierung mit einfachen, überschaubaren Dokumenten ist durchaus möglich – Aber komplexe Dokumentstrukturen auf Basis des Office OpenXml kann einen in den Wahnsinn treiben. Es ist schon schwer genug die Dokumentstruktur beherrschen zu wollen, aber die zum Teil sehr eigenen Verhaltensweisen von Word können für psychotische Schübe sorgen... Ich sage nur Fußnoten, Tabellen und/oder Abschnittswechsel (auch in Verbindung mit unsichtbarem Text).

Ich bin auf jeden Fall gespannt, was die nähere Zukunft mit sich bringt…

Kick It on dotnet-kicks.de

Samstag, 9. April 2011

Alle Bälle in der Luft halten

Die magischen Buchstaben, wenn es darum geht Anwendungen mit Benutzeroberflächen zu entwickeln, kennt wohl jeder: MVP.  Und wer sie nicht kennt, sollte sich schnellstmöglich damit vertraut machen! Es gibt viele schöne Beispiele hierzu, eines ist sicherlich auch das von Stefan Kölle. Woran es, meiner Meinung nach, aber wie die meisten Beispiele auch krankt, ist die Steuerung zwischen den einzelnen Views/Presenters.

Besonders deutlich wurde mir dieser Mangel, als ich vor Kurzem folgenden Quellcodeauszug sah:

public class MyClass
{
   ...
   public void ShowOtherForm()
   {
      using(MyPresenter presenter = new MyPresenter())
      {
         presenter.Initialize();
      }
   }
}

public class MyPresenter()
{
   public void Initialize()
   {
      ...
      this.View.Show();
   }
}

Don’t do this at home, kids!

Den Presenter direkt im Code zu erzeugen bereitet mir schon ein Gefühl von Unbehagen, aber den View dann auch noch im Initialize anzuzeigen – das bereitet mir Magenschmerzen. Auf der Suche nach eleganteren Lösungen ist mir aufgefallen, dass viele Beispiele gerade in diesem Kontext Ihre Schwächen haben. Aber wie kann eine Lösung hierzu sehen?

Meine Antwort lautet: ApplicationController. Martin Fowler hats und Microsoft hat es auch.

Der ApplicationController enthält die Logik, welche View anzuzeigen ist, die vorher über die verschiedenen Presenter verteilt war. Zum Einen wird so die Logik zentralisiert zum Anderen werden damit aber auch etwaige Quellcode Dopplungen aufgehoben.

Wenn nun zwei Presenter die gleiche View anzeigen sollen, erhalten diese Presenter nur noch eine Abhängigkeit zum ApplicationController und rufen darauf eine gemeinsame Methode auf. So wird der anwendungsweite Ablauf nur noch über eine zentrale Komponente kontrolliert.

Ich habe eine Weile darüber nachgedacht, wie ich das Kind am besten beim Namen nennen kann. Dabei entstand u.a. die Idee ViewNavigator, aber letztlich trifft es der Name ViewConductor am besten. Und so sieht mein erster Versuch eines ViewConductors aus.

public class ViewConductor : IViewConductor
{
   private readonly IMainView _mainView;
   private readonly IMainViewService _mainViewService;
   private readonly IPresenterFactory _presenterFactory;

   public ViewConductor(IMainView mainView, IMainViewService mainViewService, IPresenterFactory   presenterFactory)
   {
      _mainView = mainView;
      _mainViewService = mainViewService;
      _presenterFactory = presenterFactory;
   }

   public void OpenView<TPresenterType>() where TPresenterType : IPresenter
   {
      var presenter = this._presenterFactory.CreatePresenter<TPresenterType>();
      presenter.Activate();
   }

   public void ShowPersonDetails()
   {
      this.OpenView<IPersonDetailPresenter>();
   }
}

public class PersonDetailPresenter : IPersonDetailPresenter
{
   private IPersonService _personService;

   public PersonDetailPresenter(IPersonDetailView personDetailView, IPersonService personService)
   {
      this._personService = personService;
      this.View = personDetailView;
   }

   public IPersonDetailView View { get; private set; }

   public void Activate()
   {
      this.View.Presenter = this;
      this.View.Show();
   }
}

Ja, es ist die Anwendung eines MVP Patterns, aber müssen deshalb die Komponenten auch danach benannt werden? Ich nenne meinen Hund auch nicht Hund (oder Mr. Hund Zwinkerndes Smiley). Ist es vielleicht nicht sinnvoller die einzelnen Komponenten nach ihrer Verwendung zu benennen? Aus meiner Sicht unterstützt das zudem ein Presenter-First Vorgehen, weil der Ablauf dadurch offensichtlicher wird.

Hier werde ich mit meinem nächsten Beitrag wieder ansetzen.

Kick It on dotnet-kicks.de

Donnerstag, 7. April 2011

Gedankengang zur IoC Container Registrierung

Das folgenden Beispiel hat vermutlich (fast) jeder schon mal gesehen – so, oder so ähnlich zumindest, sieht eine landläufige Registrierung von Typen in einem IoC Container aus…

public TestRegistry()
{
   Scan(x =>
   {
      x.AssembliesFromApplicationBaseDirectory();
      x.TheCallingAssembly();
   });

   ForSingletonOf<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current));
   For<IMyView>().Use<MyDRYView>();
   For<IService>().Use<SuperService>();
   For<IRepository>().Use<WhatSoEverRepository>();
}

Es werden mehr oder viele Projekte angelegt, um eine saubere Trennung zwischen Schichten und Verantwortlichkeiten zu gewährleisten. Es dürfen alle Schichten nur über Kontrakte miteinander kommunizieren. Eine der typischen Abbildungen, die in diesem Zusammenhang häufig zu sehen ist, ist die schematische DIP Darstellung von Uncle Bob, die dem folgenden entspricht.

ClassDiagram1

Jede Schicht kommuniziert mit der unterliegenden über Kontrakte, die Abhängigkeiten werden wasserfallartig nach unten durchgereicht. Was passiert aber durch eine solche Registrierung? Die Isolation der einzelnen Schichten wird dahin gehend konterkariert, dass wir eine Über-Topschicht haben, die ALLE unterliegenden kennt.

ClassDiagram1 - Copy

Die erste Frage, die sich mir stellt: Warum trenne ich dann überhaupt in verschiedene Projektdateien? Diese Trennung ist doch schließlich ohnehin nur eine logische, die durch die Aufteilung in verscheidene Projektdateien nur unterstützt werden soll. Interessant wird eine solche Trennung erst dann, wenn das Entwicklerteam groß genug ist, dass isoliert an verschiedenen Komponenten gearbeitet wird oder aber verteilt gearbeitet wird.

Ich aber habe diese Struktur nun schließlich gewählt, also warum dann nicht die Registrierung zumindest auf die Komponenten aufteilen. Jede Komponente registriert Typen der ihr untergeordneten Komponente. Es gilt ohnehin als best-practice die Registrierung inhaltlich auf verschiedene Teilregistrierungen aufzuteilen – warum dann nicht auch auf die Komponenten?

Die Mechanism-Schicht wird also sie Registrierung für die Utility-Schicht beinhalten, die Policy-Schicht enthält die Informationen über die Mechanism-Schicht. Aufgrund der auto-discovery Mechanismen der verschiedenen Container sollte dies damit keine großes Problem darstellen.

In Zukunft werden die Registrierungen dann anders aussehen - oder aber die Anzahl der Projekte einfach eingedampft.

public WebRegistry()
{
   Scan(x =>
   {
      x.AssembliesFromApplicationBaseDirectory();
      x.TheCallingAssembly();
      x.LookForRegistries();
   });

   ForSingletonOf<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current));
   For<IMyView>().Use<MyDRYView>();
}

public ServiceRegistry()
{
   Scan(x =>
   {
      x.AssembliesFromApplicationBaseDirectory();
      x.TheCallingAssembly();
   });

   For<IService>().Use<SuperService>();
}

public DataRegistry()
{
   Scan(x =>
   {
      x.AssembliesFromApplicationBaseDirectory();
      x.TheCallingAssembly();
   });

   For<IRepository>().Use<WhatSoEverRepository>();
}

Kick It on dotnet-kicks.de

Sonntag, 3. April 2011

Javascript aus WebParts für Sharepoint und Web Projekte registrieren

Javascript, wie etwa JQuery,  aus WebParts heraus zu rendern ist eine häufige Anforderung. Wie so etwas, beispielsweise mit Hilfe eines DelegateControls, gelöst werden kann, zeigt ein Artikel von Thorsten Hans.

Nun stand ich aber vor der Anforderung den WebPart in Sharepoint sowie eine WebForms Seite einzubinden. Grundsätzlich ist dies auch mit dem DelegateControl zu realisieren. Mein Vorgehen ist aber etwas Quellcode-zentrischer und ich möchte alles Renderverhalten in einem WebPart aus dem Quellcode heraus steuern.

Für Sharepoint gibt es hierfür die Klasse ScriptLink, in Web Projekten hingegen wird aber der ScriptManager verwendet. Um dennoch beide bedienen zu können habe ich einen Kontrakt IScriptRegistrar erstellt, der nur eine Methode RegisterScript beinhaltet.

public interface IScriptRegistrar
{
    void RegisterScript(IScriptRegistrationParameters registrationParameters);
}

Als Parameter wird eine Klasse erwartet, die die zur Registrierung des Skripts notwendigen Informationen beinhaltet. Der Kontrakt dazu sieht wie folgt aus.

public interface IScriptRegistrationParameters
{
    Page Page { get; set; }
    Type Type { get; set; }
    string Key { get; set; }
    string Url { get; set; }
    bool Localizable { get; set; }
}

In dem Sharepoint Projekt wird nun die konkrete Implementierung SharepointScriptRegistrar mit der Klasse ScriptLink hinzugefügt.

public class SharepointScriptRegistrar : IScriptRegistrar
{
    public void RegisterScript(IScriptRegistrationParameters registrationParameters)
    {
        ScriptLink.Register(
        registrationParameters.Page, 
        registrationParameters.Url, 
        registrationParameters.Localizable);
    }
}

Das Webprojekt dagegen erhält eine weitere Klasse WebScriptRegistrar, die das Skript mit Hilfe des ScriptManagers registriert.

public class WebScriptRegistrar : IScriptRegistrar
{
    public void RegisterScript(IScriptRegistrationParameters registrationParameters)
    {
        var scriptManager = registrationParameters.Page.ClientScript;
        scriptManager.RegisterClientScriptInclude(
        registrationParameters.Type,
        registrationParameters.Key,
        registrationParameters.Url);
    }
}

Der Aufruf des jeweiligen Registrars erfolgt kontextabhängig durch einen IoC Container. So halte ich das Wissen über die Implementierung aus dem WebPart raus und die Registrierung erfolgt nur in der Konfiguration des Containers. Als IoC Tool nutze ich in diesem Fall StructureMap.

So beschreibt der folgende Ein-Zeiler die Konfiguration innerhalb des Sharepoint Projekts.

For<IScriptRegistrar>().Use<SharepointScriptRegistrar>();

Die Registrierung für das Web Projekt sieht dem entsprechend angepaßt aus.

For<IScriptRegistrar>().Use<WebScriptRegistrar>();

Mit Hilfe eines ServiceLocators wird dann der registrierte IScriptRegistrar aus dem Container geholt und die Skriptregistrierung mit dem Aufruf RegisterScript ausgeführt.

var scriptRegistrar = ServiceLocator.Get<IScriptRegistrar>();
IScriptRegistrationParameters scriptRegistrationParameters = new ScriptRegistrationParameters
{
    Page = this.Page,
    Type = this.GetType(),
    Key = "javascript",
    Url = "necrtt/jsgantt.js",
    Localizable = false
};
scriptRegistrar.RegisterScript(scriptRegistrationParameters);

Falls es noch andere Ideen gibt, wie dieses Szenario auch umgesetzt werden könnte, bin ich immer daran interessiert…

Kick It on dotnet-kicks.de

Mittwoch, 30. März 2011

Einfach mal abschalten

Es gibt etwas sehr wichtiges, dass vor einiger Zeit von mir viel zu sträflich vernachlässigt wurde: disconnecting.

Rob Conery und Scott Hanselman berichten über Ihre “ungewöhnliche” Erfahrung in This Developers Life. Jeder, der es noch nicht kennt und/oder von sich der Meinung ist, er arbeite (zu) viel, sollte es sich mal anhören. Ich für meinen Teil konnte mich in einigem davon wiederfinden…

Meiner Meinung nach wertvolle, auf jeden Fall aber auch unterhaltsame 54 Minuten.

Kick It on dotnet-kicks.de