Verfasst von: Michael | 06/02/2011

Unförmige 14km

… will heissen, die Laufform lässt noch zu wünschen übrig. Aller (Jahres)-Anfang ist schwer.

image

Advertisements
Verfasst von: Michael | 03/01/2011

S21-Lauf am 2.1.

… von Ditzingen nach S-Hauptbahnhof. Das Zeltdorf der Gegner verschandelt weiterhin den verschneiten Schlosspark und die noch stehenden Bäume (ich frage mich ernsthaft, was an Bäume angekettete Stofftiere ausdrücken sollen). Auffällig war, dass mir nicht wenige Menschen mit K21-Buttons entgegenkamen; vermutlich wird es heute wieder zum Demo-Regeltermin am Hauptbahnhof kommen, alte Gewohnheiten lassen sich auch im neuen Jahr nur schwer ablegen.

image

Distanz: 16,03 km
Tempo: 5:21 min/km

Verfasst von: Michael | 31/12/2010

Jahresabschluß Running

Heute bin ich nochmal einen 12er gelaufen (bzw gestapft, gerutscht, …), sodass ich jetzt einen versöhnlichen Jahresabschluß machen kann.

Gesamtdistanz in 2010: 1002km
… weniger als 2009 (1085km), ich schiebe es einfach mal auf die verletzungsbedingte Pause im November und Dezember. Also bis Neapel hätte es dieses Mal nicht gereicht.

Durschnittliches Tempo in 2010: 5:28min/km
… damit eine halbe Minute schneller als 2009, aber immer noch ausbaufähig.

Verbrauchte Kalorien: 97142
… oder ca. 184 Tafeln Milka-Schokolade

Verfasst von: Michael | 30/12/2010

Extend NHibernate-LINQ for Regex-Matching

The time between Christmas and New Year’s Eve traditionally is a great time to take a look at .NET-Stuff which is (yet) not part of my daily work. This time I wanted to dig a little deeper into the newest NHibernate version 3.0. I was especially interested in its LINQ-Implementation which was rewritten from scratch for the 3.0-GA release.

Today I tried to extend NH-LINQ with Regex-Matching. What first sounds quite demanding is really easy as NH has many extension points, including the LINQ-Provider.

Add RegexMatch-Function to SQL-Server

SQL-Server doesn’t have Regex-Support out-of-the-box, you need to implement it in .NET as a User-Defined-Function (UDF) and then deploy the assembly to SQL-Server. Here is the very basic implementation which is not optimized for performance in any way (there are some ways to precompile regular expressions):

public class UserDefinedFunctions
{
  [SqlFunction(IsDeterministic = true, IsPrecise = true)]
  public static bool RegexMatch(SqlString input, SqlString pattern)
  {
    return Regex.IsMatch(input.ToString(), pattern.ToString());
  }
};

To deploy that function the fastest way is to create a “Visual C# SQL CLR Database Project” which contains the deployment step. I won’t describe it here in details, there are a lot of resources on the net for this topic. Just two remarks: 1) the project must target .NET 3.5 and 2) the CLR-Support in SQL-Server must be activated via

sp_configure ‚clr enabled‘, 1
reconfigure

Implement the Extension-Method

I now add an Extension-Method called “RegexMatch” to the string-Class which has the same implementation as the UDF.

static class RegexExtensions
{
  public static bool RegexMatch(this string s, string pattern)
  {
    return Regex.IsMatch(s, pattern);
  }
}

Now “Mike”.RegexMatch(“Mi.e”) evaluates to true while “Mike”.RegexMatch(“Mo.e”) returns false.

Write HQL-Generator for RegexMatch-Method

I have a small NHibernate-Project with one mapped class called “MyUser”:

class MyUser
{
  public virtual int Id { get; set; }
  public virtual string Name { get; set; }
}

Not very exciting and I think you can guess how the table looks like. Now I want to do this:

session.Query<MyUser>()
  
.Where(u => u.Name.RegexMatch("Mi.e"))
   .ToList();

Certainly this compiles because of the available Extension-Method but when I execute it I get the following error from NHibernate:

Unhandled Exception: System.NotSupportedException: Boolean RegexMatch(System.String, System.String)

NHibernate doesn’t know what to do with the RegexMatch-Call. We have to tell NHibernate how to translate the LINQ-Expression to a HQL-Expression for the actual UDF-Call.

First we must implement a new so-called HqlGenerator:

public class RegexMatchGenerator : BaseHqlGeneratorForMethod
{
  public RegexMatchGenerator()
  {
    var methodDefinition =
       ReflectionHelper.GetMethodDefinition(() => 
         RegexExtensions.RegexMatch(null, null));

    SupportedMethods = new[] { methodDefinition };
  }

  public override HqlTreeNode BuildHql(
    MethodInfo method,
    Expression targetObject,
    ReadOnlyCollection<Expression> arguments,
    HqlTreeBuilder treeBuilder,
    IHqlExpressionVisitor visitor
  )
  {
    return treeBuilder.Equality(
      treeBuilder.MethodCall("dbo.RegexMatch", new[]
      {
        visitor.Visit(arguments[0]).AsExpression(),
        visitor.Visit(arguments[1]).AsExpression()
      }),
      treeBuilder.Constant(1)
    );
  }
}

In the constructor our RegexMatch-Method is registered by using this very cool ReflectionHelper-Class (I must add this to my toolbelt).

In the BuildHql-Method now the magic happens. This code basically is the programmatic representation of “dbo.RegexMatch(@p1, @p2) = 1”. The comparison against 1 is necessary as SQL-Server doesn’t seem to handle RegexMatch as a valid boolean function.

Add HQL-Generator to NHibernate-Configuration

OK, the heavy part is implemented, now we need to register the new HQL-Generator somewhere. That’s easy too. All default HQL-Generators are registered in the class DefaultLinqToHqlGeneratorsRegistry. We must only extend this and add our own HQL-Generator to it:

public class MyLinqToHqlGeneratorsRegistry :
  DefaultLinqToHqlGeneratorsRegistry
{
  public MyLinqToHqlGeneratorsRegistry()
  {
    this.Merge(new RegexMatchGenerator());
  }
}

Last but not least we must set our class as the new HQL-Generator-Registry in the NHibernate-Configuration:

cfg.LinqToHqlGeneratorsRegistry<MyLinqToHqlGeneratorsRegistry>();

Now when I run the code once more

session.Query<MyUser>()
  
.Where(u => u.Name.RegexMatch("Mi.e"))
   .ToList();

I see this SQL-Query executed:

select myuser0_.Id as Id0_, myuser0_.Name as Name0_
from MyUser myuser0_
where dbo.RegexMatch(myuser0_.Name, @p0)=1;@p0 = ‚Mi.e‘

It works !

Summary

The masterminds behind NHibernate seem to have invested a lot of effort to make it as extensible as possible. Great work, guys !

Verfasst von: Michael | 24/12/2010

LINQ: Versuche mit Expression Trees

LINQ (Language Integrated Query) ist eine coole Sache, keine Frage. Das Filtern von Collections wird zum Kinderspiel, Umformungen lassen sich wunderbar einfach ausdrücken und zusammen mit den LINQ-Providern für SQL-Datenbanken oder OR-Mappern wie NHibernate kann man für den Datenbank-Zugriff auf SQL weitestgehend verzichten.

Wenn man hinter die Kulissen von LINQ schaut, entdeckt man schnell Komponenten, mit denen aber noch deutlich fortgeschrittenere Szenarien möglich sind. Zum Beispiel rückt die Implementierung einer eigenen Abfragesprache (z. B. für DSLs) in greifbare Nähe.

Ganz von vorne …

Ich fang mal bei Null an. Nehmen wir an, wir haben folgendes Array mit Namen:

var names = new[] {
  "Axel", "Bernd", "Claus",
  "Dieter", "Erich", "Fritz"
};

Jetzt möchte ich rausfinden, welcher Name die Buchstaben “e” und “r” enthält:

var somenames = names.Where(n => n.Contains("e") && n.Contains("r"));

Der mit großem “E” geschriebene Erich fällt durch, daher bleiben Bernd und Dieter übrig. Für die weitere Untersuchung ziehe ich den Lambda-Ausdruck jetzt raus:

Func<string, bool> p = n => n.Contains("e") && n.Contains("r");
var somenames = names.Where(p);

Was auffällt: der Variable p muss explizit der Typ “Func<string, bool>” zugewiesen werden, ein “var p = …” funktioniert nicht. Das hat seinen Grund, ich komm später darauf zurück. Wenn ich die Variable p auf der Konsole ausgebe, kommt folgendes heraus:

System.Func`2[System.String,System.Boolean]

Also in der Tat ein Delegate vom Typ Func<string, bool>.

Aus Delegate wird Expression

Jetzt ändere ich den Variablentyp ab in

Expression<Func<string, bool>> p =
  n => n.Contains("e") && n.Contains("r");

Der Compiler meckert nicht, das ist eine legale Zuweisung. Was hier passiert: der Compiler merkt anhand des Variablentyps, dass der Lambda-Ausdruck nicht in ein Delegate umgewandelt wird, sondern statt dessen ein sog. Expression-Tree erzeugt wird. Aus diesem Grund muss der Variablen-Typ auch explizit angegeben werden, da der Compiler wissen muss, welche Umwandlung er machen soll.

Schauen wir uns den Expression-Tree mit Console.WriteLine an:

n => (n.Contains("e") AndAlso n.Contains("r"))

Es sieht fast genauso aus wie unser Lambda-Ausdruck, allerdings statt des && ein etwas gequältes “AndAlso” (“And” gibt es auch, dass drückt aber ein bitweises UND aus).

Aus IEnumerable wird IQueryable

Leider kommt es jetzt in der Where-Condition zu einem Fehler. Die Where-Bedingung für IEnumerable erwartet einen Delegate und keinen Expression-Tree. Um den Expression-Tree verwenden zu können, muss IEnumerable in IQueryable umgewandelt werden:

var somenames = names.AsQueryable().Where(p);

IQueryable ist das Interface, dass sog. LINQ-Provider implementieren müssen, die eigene Abfragesprachen besitzen. Z. B. produziert LINQ to SQL aus einem Expression-Tree eine SQL-Abfrage.

Expression-Tree im Eigenbau

Das Programm funktioniert wieder mit demselben Ergebnis. Aber was bringt mir das jetzt ?

Das Schöne an Expression-Trees ist, dass man sie nicht nur aus Lambda-Ausdrücken erzeugen kann, sondern recht einfach selber zusammenbauen kann. Ich will folgendes versuchen: anstatt die Suchbedingungen hart zu kodieren, möchte ich eine eigene kleine Abfragesprache definieren, mit der ich mein Namens-Array abfragen kann. Die soll z. B. so aussehen:

e;r;!D

Damit sollen alle Namen ausgegeben werden, die ein “e” und ein “r” enthalten, aber kein “D”.

Zuerst definiere ich dazu eine Methode ParseQuery, die aus dem Query-String ein Expression-Tree in der für IQueryable benötigten Bauweise macht:

static Expression<Func<string, bool>> ParseQuery(string s)

Das Zusammenbauen der Expression besteht aus einer Schachtelung (daher auch Expression-Tree) von Sub-Expressions, die die notwendigen Bestandteile enthalten. Zuerst benötige ich eine Expression, die die Prüfvariable definiert, also den Teil, der von “aussen” in die Query mitgegeben wird und der dann auf die verschiedenen Bedingungen geprüft wird.

var paramEx = Expression.Parameter(typeof(string), "p");

Die Klasse Expression bietet zahlreiche solcher Factory-Methoden für Expressions an, wir werden jetzt noch weitere verwenden. Jetzt nehmen wir den Query-String auseinander:

foreach (var part in s.Split(‚;‘))

Für jeden part müssen wir eine Expression erzeugen und alle entstandenen Expressions mit logischem UND verknüpfen.

Für die Parts ohne NOT-Operator genügt folgende Expressions:

partEx = Expression.Call(
  paramEx,
  typeof (string).GetMethod("Contains"),
  Expression.Constant(part)
);

Wie ist das zu verstehen ? Diese Expression entspricht einem Aufruf (“Call”) einer Methode, die einen booleschen Wert zurückliefert. Diese Methode ist die Methode “Contains” der Klasse string und wird auf unserer Prüfvariablen aufgerufen (paramEx). Als Parameter bekommt die Contains-Methode unseren Part als konstanten Ausdruck. Auf gut Deutsch entspricht diese Expression etwa folgender C#-Zeile:

paramEx.Contains(part)

Für die Unterstützung des !-Operators müssen wir einfach nur diese Expression wieder in eine Expression, der Not-Expression, verpacken.

partEx = Expression.Not(
  Expression.Call(
    paramEx,
    typeof (string).GetMethod("Contains"),
    Expression.Constant(part.Substring(1))
  )
);

Dabei müssen wir vom Part das erste Zeichen ! abschneiden. In der Schleife erzeugen wir jetzt nacheinander diese Expressions und verknüpfen sie mit logischem UND. Das ist sehr einfach:

andEx = Expression.AndAlso(leftEx, rightEx);

Die fertige Methode sieht so aus:

static Expression<Func<string, bool>> ParseQuery(string s)
{
  var paramEx = Expression.Parameter(typeof (string), "p");

  Expression completeEx = null;

  foreach (var part in s.Split(‚;‘))
  {
    Expression partEx;

    if(part.StartsWith("!"))
    {
      partEx = Expression.Not(
        Expression.Call(
          paramEx,
          typeof (string).GetMethod("Contains"),
          Expression.Constant(part.Substring(1))
        )
      );
    }
    else
    {
      partEx = Expression.Call(
        paramEx,
        typeof (string).GetMethod("Contains"),
        Expression.Constant(part)
      );
    }

    completeEx = completeEx == null
          ?
partEx
          : Expression.AndAlso(completeEx, partEx);
  }

}

Halt, eins fehlt noch: das Return-Statement. Dazu muss die fertige boolesche Expression noch in eine Lambda-Expression verpackt werden:

return Expression.Lambda<Func<string, bool>>(completeEx, paramEx);

So, fertig. Mal schauen, was die Methode für verschiedene Query-Ausdrücke zurückliefert.

e;r: p => (p.Contains("e") AndAlso p.Contains("r"))
e;r;!D: p => ((p.Contains("e") AndAlso p.Contains("r")) AndAlso Not(p.Contains("D")))

Sieht gut aus, jetzt kann ich das Resultat als Where-Bedingung verwenden.

var somenames = names.AsQueryable().Where(ParseQuery("e;r;!D"));
=> Bernd

var somenames = names.AsQueryable().Where(ParseQuery("!F;r;i"));
=> Dieter, Erich

Das war’s. Wenn man das Prinzip verstanden hat, ist es eigentlich ganz einfach. Mit Expression-Trees bekommt der Entwickler ein mächtiges Werkzeug in die Hand, um die ganze Power der LINQ-Infrastruktur ausnutzen zu können.

Verfasst von: Michael | 21/12/2010

System Restore API in .NET

Ein selten genutztes Feature von Windows sind die Wiederherstellungspunkte (WHP, immerhin seit Windows 2000 verfügbar). Normalerweise braucht man sie nicht, im Ernstfall können sie aber sehr nützlich sein. Unter Ernstfall verstehe ich eine fehlerhafte Treiberinstallation, eine amoklaufende Applikation, kurzum alles, was das System destabilisieren kann. Ist das System in einem solchen Zustand, kann auf einen WHPs zurückgefahren werden und das System funktioniert wieder wie zu dem Zeitpunkt der Erzeugung des WHP.

Was weniger bekannt ist: man kann auch selbst WHPs erzeugen. Das geht zum einen per Mausklick, zum anderen gibt es aber auch das System Restore API, dass sich von C++-Programmen nutzen lässt. Leider gibt es im .NET-Framework keine entsprechenden Wrapper-Klassen, daher habe ich ausgehend von einem OpenSource-Projekt einen eigenen gebastelt (Download Source).

Die Benutzung ist sehr einfach:

using (var sysChg = SystemChange.ForInstallation("My Restore Point"))
{
  Console.WriteLine("Press <RETURN> when finished");
  Console.ReadLine();
  sysChg.Finish();
  Console.WriteLine("Restore-Point saved");
}

Dabei wird der WHP namens “My Restore Point” angelegt. Wenn die Installation (also die Systemveränderung, die u. U. rückgängig gemacht werden soll) fertig ist, wird der SystemChange mit Finish() abgeschlossen. Die SystemChange-Klasse kapselt für die einfachere Verwendung das eigentliche Low-Level-API SystemRestore. Wie am Screenshot zu sehen ist, wurde der WHP angelegt.

image

Um einen WHP herstellen zu können, muss man lokaler Administrator sein. Ansonsten bricht das Programm mit einer AccessDeniedException ab.

Verfasst von: Michael | 19/12/2010

Die Sehne hält …

… zumindest einen ersten Belastungstest über 10km (nach einem Monat Zwangspause). Teilweise war es wegen dem Tauwetter etwas rutschig, war aber kein Problem, da ich es hab langsam angehen lassen.

image

Distanz: 10,26 km
Tempo: 5:30 min/km
Zeit: 56:23

 

 

Und zum Feste noch eine musikalische Empfehlung: Udos Weihnachtsalbum “Es werde Licht”. Eine Mischung aus Klassikern (Stille Nacht; Leise rieselt der Schnee), stimmungsvollen Eigenkompositionen (Eisblumen; Das Jahr deiner Träume) und Veralberungen (Merry Christmas Allerseits; Ach Papi, geh doch heuer nicht auf die Weihnachtsfeier).

Merry Christmas Allerseits !

Verfasst von: Michael | 21/11/2010

10,72 km

… an den Bärenseen bei knapp 4°C (56 min). Ab 9km Schmerzen im linken Achilles, knapp vor 11 war daher Schluss.

Verfasst von: Michael | 06/11/2010

Läufe

31.10. … 13,98 km, 5:16 min/km
06.11. … 11,76 km, 4:57 min/km

Und zwischendurch auch mal ein paar Kilometer für Stuttgart 21.

Verfasst von: Michael | 18/10/2010

Bottwartal-Halbmarathon

Endlich geschafft ! Beim HM im Bottwartal habe ich zum ersten Mal die 1:45h-Grenze geknackt. Knapp war es aber trotzdem: 1:44:29.

Bis Kilometer 17 hatte ich sogar die 1:43 im Visier. Danach wurden die Rundenzeiten aber merklich schlechter, was an der leichten, aber trotzdem schmerzenden Steigung zurück nach Großbottwar lag. In Summe hat es aber dann doch noch gereicht.

image

Zeit: 1:44:29
Tempo: 4:57 min/km
Höhe: +59,5 / -42,0
Platzierung: 27 AK / 154 Gesamt

Wie beim letzten Mal danke an die gute Organisation und die reichliche Verpflegung nach dem Rennen.

Older Posts »

Kategorien