Blue Orange Green Pink Purple

Posts Tagged ‘Fluent’

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

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.

  • 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