.
Anmeldung | Registrieren | Hilfe

.NET-Blogs Archiv Juli 2008

Asynchronen AJAX PostBack erkennen

31.07.2008 16:42:00 | Jürgen Gutsch

Um zu erkennen ob der aktuelle Request durch einen ASP.NET AJAX PostBack ausgelöst wurde, muss man nur den ScriptManager fragen:

if(ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
{
    // Aufruf ist ein AsyncPostBack :-)
}

zudem kann man über den ScriptManager erfahren, welches Control den Asysnchronen PostBack ausgelößt hat:

if(ScriptManager.GetCurrent(Page).AsyncPostBackSourceElementID == "ctl001$myButton")
{
    // Aufruf wurde von "myButton" ausgelößt :-)
}

Die Abfrage der AsyncPostBackSourceElementID liefert die UniqueID (mehr dazu) des auslösenden Controls. Triggernde Controls werden ebenfalls zurückgegeben.

Asynchronen AJAX PostBack erkennen

31.07.2008 16:42:00 | Jürgen Gutsch

Um zu erkennen ob der aktuelle Request durch einen ASP.NET AJAX PostBack ausgelöst wurde, muss man nur den ScriptManager fragen:

if(ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
{
    // Aufruf ist ein AsyncPostBack :-)
}

zudem kann man über den ScriptManager erfahren, welches Control den Asysnchronen PostBack ausgelößt hat:

if(ScriptManager.GetCurrent(Page).AsyncPostBackSourceElementID == "ctl001$myButton")
{
    // Aufruf wurde von "myButton" ausgelößt :-)
}

Die Abfrage der AsyncPostBackSourceElementID liefert die UniqueID (mehr dazu) des auslösenden Controls. Triggernde Controls werden ebenfalls zurückgegeben.

AsyncPostBackTrigger per Code erzeugen

31.07.2008 16:30:00 | Jürgen Gutsch

Entgegen der Dokumentation auf http://www.asp.net/ lassen sich PostBackTrigger und AsyncPostBackTrigger sehr wohl per Code erzeugen:

Folgender Code funktioniert einwandfrei, wenn:
a) dieser in bei Page_Init ausgeführt wird und
b) das triggernde Control Existiert ;-)

AsyncPostBackTrigger trigger = new AsyncPostBackTrigger();
trigger.ControlID = "lbTriggerButton";
this.UpdatePanel1.Triggers.Add(trigger);

Die Eigenschaft EventName braucht nur angegeben werden, wenn nicht das Standard-Event des Controls genutzt wird. (Beim Button wäre das Standard-Event z. B: Click).

Bei ControlID kann sowohl die ID als auch die UniqueID des triggernden Controls angegeben werden. Bei der Angabe der ClientID wird ein Fehler erzeugt.

Mehr zu den Control IDs weis Peter: Artikel: Identifizierung von Controls: Control.ID / .ClientID / .UniqueID

AsyncPostBackTrigger per Code erzeugen

31.07.2008 16:30:00 | Jürgen Gutsch

Entgegen der Dokumentation auf http://www.asp.net/ lassen sich PostBackTrigger und AsyncPostBackTrigger sehr wohl per Code erzeugen:

Folgender Code funktioniert einwandfrei, wenn:
a) dieser in bei Page_Init ausgeführt wird und
b) das triggernde Control existiert ;-)

AsyncPostBackTrigger trigger = new AsyncPostBackTrigger();
trigger.ControlID = "lbTriggerButton";
this.UpdatePanel1.Triggers.Add(trigger);

Die Eigenschaft EventName braucht nur angegeben werden, wenn nicht das Standard-Event des Controls genutzt wird. (Beim Button wäre das Standard-Event z. B: Click).

Bei ControlID kann sowohl die ID als auch die UniqueID des triggernden Controls angegeben werden. Bei der Angabe der ClientID wird ein Fehler erzeugt.

Mehr zu den Control IDs weis Peter: Artikel: Identifizierung von Controls: Control.ID / .ClientID / .UniqueID

Channel 9: ASP.NET AJAX Browser History

31.07.2008 06:40:29 | Oliver Scheer

Ein Nachteil von AJAX-Anwendungen ist es, das der Browserverlauf nicht aktualisiert wird, wenn ein Teil der Webseite sich verändert. Das bedeutet, man kann die Vorwärts- und Rückwärtsbutton nicht wie gewohnt verwenden.
Dieses Video zeigt die neuen Funktionalitäten des ScriptManagers, der jetzt einen direkten Zugriff auf den Browserverlauf bietet und mit wenigen Zeilen Code, das gewünschte Verhalten von "normalen" Webseiten wieder herstellt.


ASP.NET AJAX: Browser History

sehr cooles WPF Video über das 3.5 SP1

30.07.2008 18:10:07 | Lars Keller

Wer mal sehen möchte was das neue SP1 für WPF 3.5 bringt, der sollte sich unbedingt dieses Video anschauen. Ich sag nur "WOW !" :)

http://channel9.msdn.com/posts/AdamKinney/WPF-35-SP1-Graphics-with-David-Teitlebaum/

Dank an Florian für den Link!

How deep is your clone?

30.07.2008 13:35:57 | Andre Loker

Someone asked in a forum:

Why is there no ICloneable<T> only ICloneable?

Good question. It wouldn't have taken too much effort to introduce a generic version when .NET 2.0 came out. But I think MS had a good reason not to introduce ICloneable<T>. And that's because they realized that ICloneable sucks in the first place! Why is that? Because it is effectively undefined what cloning does.

Let's have a look at the MSDN library. It has this to say about ICloneable:

Supports cloning, which creates a new instance of a class with the same value as an existing instance.

And this about ICloneable.Clone:

Creates a new object that is a copy of the current instance.

...

Clone can be implemented either as a deep copy or a shallow copy. In a deep copy, all objects are duplicated; whereas, in a shallow copy, only the top-level objects are duplicated and the lower levels contain references.

All right, doesn't sound too bad, does it. There are two interesting points, though:

Point 1: Clone returns a new object. Really? Not necessarily. System.String implements ICloneable.Clone like this:

 public object Clone(){
   return this;
 }

Not necessarily problematic, as strings are immutable, but still explicitly against the documentation of ICloneable.Clone.

Point 2: shallow vs. deep copy. This is hell, trust me. Any implementor of ICloneable is free to choose "how deeply" it copies itself. This can give a multitude of different meanings to Clone().

Let us have a look at an example of shallow copy. To implement Clone as a shallow copy method create a new instance of the class and set all instance variables to the value of the original class. This is in fact what Object.MemberwiseClone() does, so let's just use that:

 class Place {
   public string Name { get; set; }
   public string Postcode { get; set; }
 }
  
 class Address {
   public string Street { get; set; }
   public string HouseNumber { get; set; } // string to support '23b'
   public Place Place { get; set; }
 }
  
 class Order : ICloneable {
   public Address ShippingAddress { get; set; }
  
   public object Clone() {
     // shallow copy
     return MemberwiseClone();
   }
 }

It's easy to do. MemberwiseClone() creates a new instance of Order. The returned instance uses the same Address object as the original Order. That's fine until someone does this:

 Order order = // order from database 
 Order similarOrder = (Order) order.Clone();
 similarOrder.ShippingAddress.Street = "Somewhere";
 similarOrder.ShippingAddress.HouseNumber = "12c";

By changing the Address instance of similarOrder (which is the same as order.Address) we changed the shipping address of the original order. Whoops. Might be better to do a deep copy! Here's the modified code that does a deep copy:

 class Place : ICloneable {
   public string Name { get; set; }
   public string Postcode { get; set; }
  
   public object Clone() {
     // shallow copy is enough here
     return MemberwiseClone();
   }
 }
  
 class Address : ICloneable {
   public string Street { get; set; }
   public string HouseNumber { get; set; } // string to support '23b'
   public Place Place { get; set; }
  
   public object Clone() {
     return new Address() {
       Street = Street,
       HouseNumber = HouseNumber,
       Place = (Place) Place.Clone()
     };
   }
 }
  
 class Order : ICloneable {
   public Address ShippingAddress { get; set; }
  
   public object Clone() {
     // deep copy
     return new Order {
       ShippingAddress = (Address)ShippingAddress.Clone()
     };
   }
 }

Now we are on the safe side. We can mess with the address of a cloned order anyway we like without affecting the original order. But wait... all of a sudden we realize that the postcode of the address was wrong, so we fix that:

 Order order = // order from database 
 Order similarOrder = (Order)order.Clone();
 similarOrder.ShippingAddress.Place.Postcode = "1234";

But now we have a new problem: by doing a deep copy we duplicated the Place instance as well. If we change the postcode in one of the instances, it won't affect the other one - but it should! So what we actually need in this case is a semi-deep copy. Some parts of the object graph have to be copied deeply (the Address), some parts need a shallow copy (the Place). There's clearly no general pattern in this.

While this example is a bit made up you might find such situations in practice. Sometimes you won't have any chance to avoid it. But you see that it can get complicated. More complicated than a single one-size-fits-all interface like ICloneable could handle. In general "cloning" an object is by far not as transparent as ICloneable.Clone might suggest. If you need some sort of copying function, go ahead. Give it a clear name and implement it in a reasonable way. But don't implement ICloneable as it can rise false assumptions.

MS probably realized this problem. They did not want to advertise "general purpose cloning" by introducing another ICloneable interface which would make cloning even more convenient for the user.

Silverlight 2 Event Showcase

30.07.2008 10:38:04 | Oliver Guhr

Für das Dresden Future Forum haben meine Kollegen ein Silverlight 2 Showcase umgesetzt. Unter http://silverlight.t-systems-mms.eu/dzf/ kann man sich die Videos der Vorträge (u.a. von Tim O’Reilly) inkl. der Slides anschauen. Ziemlich cool finde ich die Deep-Zoom-Bilderwand mit Bildern vom Future Forum.

image

ShareThis

sehr cooles WPF Video über das 3.5 SP1

30.07.2008 09:10:00 | Lars Keller

Wer mal sehen möchte was das neue SP1 für WPF 3.5 bringt, der sollte sich unbedingt dieses Video anschauen. Ich sag nur "WOW !" :)

http://channel9.msdn.com/posts/AdamKinney/WPF-35-SP1-Graphics-with-David-Teitlebaum/

Dank an Florian für den Link!

Die nächste Windows Version - Das Projekt Mojave ;-)

29.07.2008 19:33:37 | Oliver Scheer

Es gibt diverse Meinungen über Windows Vista. Interessant ist, das viele Meinungen vom "Hören-Sagen" her kommen und nicht auf eigenen Erfahrungen. Okay ... Vista hat ... sagen wir mal ... an einer oder anderen Stelle Potential, aber ich liebe es und arbeite meine gesamte Microsoft Zeit nur noch mit Vista (bzw. diversen Betaversionen von Vista). Ohne das Suchtext-Feld unten links käme ich auch gar nicht mehr zurecht, geschweige denn möchte ich auch nur einen Tagen ohne meine geliebten Gadgets auskommen müssen.

Aber was passiert, wenn man Personen Vista zeigt, die Vista nur vom "Hören-Sagen" kennen und ihre Meinung bereits gebildet haben, Vista mal in Action zeigt? Das geht natürlich nicht mit dem Namen Vista, sondern man benötigt einen "coolen" Namen. Okay ... (Wild-)Tiernamen gehören meiner Meinung nach nicht in die Software-Branche ;-) Bei Microsoft ist es eine lose Tradition neuen Projekten Namen nach Landschaften, Bergen oder Orten zu geben.

In dem Mojave-Projekt ist man nun hingegangen hat Ahnungslosen, die angeblich nächste Version von Windows vorgestellt. Was dabei heraus gekommen ist, hat selbst Microsoft überrascht.

Einfach mal selber reinschauen: http://www.mojaveexperiment.com/.

Die Community Termine immer im Blick.

28.07.2008 23:05:52 | Jan Welker

Im dotnet-forum.de gibt es seit heute einen Terminkalender für die .NET Community.
In einer Monatsübersicht werden Usergrouptreffen, Stammtische und Konferenzen angezeigt.
Ist man im Forum registriert und hat seinen Wohnort im eigenen Profil gespeichert, wird zu jeder Veranstaltung die Entfernung in Kilometern angezeigt.
Damit man keinen Termin mehr verpassen kann, kann jeder Termin nach Outlook exportiert werden.
Über ein Eingabeformular können jederzeit  neue Termine vorgeschlagen werden.

overview

detail

Viel Spaß!

Fiddler mit Firefox und anderen Programmen benutzen

28.07.2008 15:34:00 | Peter Bucher

Wer kennt Fiddler nicht?

Fiddler ist ein HTTP Debugging Proxy.
Damit kann der HTTP-Traffic mitsamt allem was dazugehört (Request- / Response-Headers, Bilder, CSS, …) analysiert werden.

Original Zitat von der Hersteller-Seite (Hersteller ist übrigens Microsoft):

Fiddler is a HTTP Debugging Proxy which logs all HTTP traffic between your computer and the Internet. Fiddler allows you to inspect all HTTP Traffic, set breakpoints, and "fiddle" with incoming or outgoing data. Fiddler includes a powerful event-based scripting subsystem, and can be extended using any .NET language.

Fiddler is freeware and can debug traffic from virtually any application, including Internet Explorer, Mozilla Firefox, Opera, and thousands more.

Fiddler ist eine Standalone-Software, alle Microsoft Programme d.h. Internet Explorer, Outlook Express, etc... sind automatisch so konfiguriert, dass der Verkehr analysiert werden kann.
Um auch den Verkehr mit Firefox analysieren zu können, muss dieser konfiguriert werden.

Erste Möglichkeit (Für Firefox):

  1. Fiddler installieren
  2. Zu "Eigene Dateien/Dokumente" navigieren, dort in den Ordner "Fiddler\Scripts" wechseln
  3. In diesem Verzeichnis gibt es eine Datei Namens "BrowserPAC.js"
  4. Den kompletten Pfad mit der genannten Datei in die Zwischenablage kopieren
  5. Im Firefox auf Extras -> Einstellungen -> Erweitert
  6. Dort das Tab "Netzwerk" auswählen und im Verbindungsbereich auf "Einstellungen" klicken
  7. Im nächsten Fenster die Option "Automatische Proxy-Konfigurations-URL auswählen, den vorher kopierten Pfad (inkl. Dateinamen) einfügen und auf "Neu laden" klicken.

Ein solcher Pfad kann bspw. so lauten: C:/Users/<Benutzername>/Documents/Fiddler2/Scripts/BrowserPAC.js

Ab sofort wird auch der Verkehr mit Firefox im Fiddler analysiert.

Zweite Möglichkeit (Auch für alle andere Programme):

  1. Im Firefox ins selbe Menü wie oben wechseln
  2. "Manuelle Proxy-Konfiguration" auswählen und dort die IP "127.0.0.1" und den Port "8888" festlegen

Hierbei sollte die Einstellung nach der "Fiddler-Session" wieder rausgenommen werden.
Wichtig: Auch die automatische Konfiguration muss wieder herausgenommen werden, da Firefox ansonsten keine Verbindung herstellen kann (Wenn Fiddler nicht läuft).

Siehe auch:

Firebug Lite für IE, Opera und Safari

28.07.2008 14:58:00 | Jürgen Gutsch

wer den Firebug auch im Internet Explorer, in Opera und im Safari nutzen möchte kann das mit dem Firebug Lite tun. Firebug Lite basiert komplett auf JavaScript und kann als Referenz in die zu "debuggende" Seite eingebunden werden oder (die bequeme Variante) als Bookmarklet. Einfach auf das Bookmark klicken und schon erscheint Firebug Lite am unteren Seitenrand:

FireBug Lite in Aktion

http://getfirebug.com/lite.html

(Via: Pixelgangster)

Firebug Lite für IE, Opera und Safari

28.07.2008 14:58:00 | Jürgen Gutsch

wer den Firebug auch im Internet Explorer, in Opera und im Safari nutzen möchte kann das mit dem Firebug Lite tun. Firebug Lite basiert komplett auf JavaScript und kann als Referenz in die zu "debuggende" Seite eingebunden werden oder (die bequeme Variante) als Bookmarklet. Einfach auf das Bookmark klicken und schon erscheint Firebug Lite am unteren Seitenrand:

FireBug Lite in Aktion

http://getfirebug.com/lite.html

(Via: Pixelgangster)

WinDbg und SOS Vortrag bei der DNUG Koblenz

27.07.2008 19:53:26 | Andre Kraemer

Kommenden Mittwoch, den 30.07.2008 werde ich bei der .NET User Group Koblenz in einem Vortrag zeigen, wie man .NET Applikationen im Fehlerfall auch ohne Visual Studio mit Hilfe der Windows Debugging Tools und der Sons-Of-Strike Extension debuggen kann.

Über zahlreiche Besucher würde ich mich freuen ;-)

Anmelden kann man sich hier



blog.codemurai.de © André Krämer |Impressum | Abonieren

Traue niemandem! - oder wie Design By Contract zwar keine Leben, aber Zeit retten kann.

27.07.2008 19:48:24 | Andre Kraemer

Protagonisten von Kriminalfilmen, die die Weisheit "Traue niemandem" beherzigen, leben länger und gehören am Ende des Films meist zu denjenigen, die nicht tot sind.  Was im Film gut ist, kann in der Softwareentwicklung doch nicht nicht schlecht sein, oder? Genau! Dies ist der Grund, weshalb man aufrufendem Code grundsätzlich misstrauen sollte! Glücklicherweise führt zuwiderhandeln in der Softwareentwicklung zwar in den seltendsten Fällen zum Tod, zu einem Haufen Überstunden kann es aber sehr schnell führen.

Anhand folgenden Beispiels möchte ich dies gerne verdeutlichen:

Unsere Aufgabe ist es, eine Methode zu schreiben, die eine angegebene, temporäre Datei löscht. Im einfachsten Fall könnte dies so aussehen:

    1     class FileHelper

    2     {

    3         /// <summary>

    4         /// Deletes the file.

    5         /// </summary>

    6         /// <param name="fileName">Please enter valid  (*.tmp)

    7         /// and existing file names only!</param>

    8         public static void DeleteTempFile(string fileName)

    8         {

   10             System.IO.File.Delete(fileName);

   11         }

   12     }

Wie wir sehen, wurde ab der Zeile 8 die Methode "DeleteTempFile" implementiert, die in Ihrem Kommentar höflich darum bittet, nur gültige und vor allem nur temporäre Dateinamen zu übergeben.

So freundlich der Kommentar zwar ist, so wenig nützlich ist er aber auch. Dem Aufrufer unserer Methode wäre es ein leichtes, folgendes hinein zu geben:

    1   FileHelper.DeleteTempFile(null);

    2   FileHelper.DeleteTempFile(@"c:\diese\Datei\gibt\es\nicht.hahaha");

    3   FileHelper.DeleteTempFile(@"c:\Diplomarbeit.doc");

Während die Aufrufe Eins und zwei "nur" zu störenden Exceptions führt, wäre Aufruf Nr. Drei sicherlich schon ärgerlicher.

Was tun, lautet also die Frage! "Validierung der Argumente", oder Einhalten der Regel CA1062 - wie fxCop so schön sagen würde - lautet die Antwort!

Im einfachsten Fall sähe dies wie folgt aus:

    1         /// <summary>

    2         /// Deletes the file.

    3         /// </summary>

    4         /// <param name="fileName">Please enter valid  (*.tmp)

    5         /// and existing file names only!</param>

    6         public static void DeleteTempFile(string fileName)

    7         {

    8             if (fileName == null)

    9             {

   10                 throw new ArgumentNullException("fileName");

   11             }

   12             if (System.IO.Path.GetExtension(fileName) != ".tmp")

   13             {

   14                 throw new ArgumentOutOfRangeException("fileName", "Only temporary (*.tmp) files are allowed!");

   15             }

   16             if (!System.IO.File.Exists(fileName))

   17             {

   18                 throw new ArgumentException("File Not Found", "fileName",

   19                     new System.IO.FileNotFoundException());

   20             }

   21             System.IO.File.Delete(fileName);

   22         }

Vom technischen Standpunkt aus gesehen gibt es an diesem Code nichts auszusetzen. Das Argument fileName wurde ausreichend geprüft und abgesehen von unzureichenden Berechtigungen auf Dateisystemebene dürfte eigentlich nichts mehr schief gehen.

In der Praxis zeigt sich jedoch häufig, dass hier die Bequemheit siegt. Wirft man noch ein Mal einen Blick auf den Quellcode, wird schnell deutlich, dass wir zur Validierung eines Argumentes 13 Zeilen Quellcode geschrieben haben. Die Anzahl der Methoden, die ihre Eingaben nur unzureichend validieren dürfte also klar in der Überzahl sein.

Eleganter und vor allem zeitsparender wäre es doch, wenn ich meinem Code auf dem "kurzen Dienstweg" sagen könnte, welche Anforderungen ich an meine Argumente habe, damit mein Code überhaupt laufen kann. Sahnebonbon wäre es auch, wenn ich zusätzlich noch eine Möglichkeit hätte, dem aufrufenden Code sicher mitzuteilen, ob das was mein Code versucht hat auch wirklich funktioniert hat.

Glücklicherweise gibt es all dies schon. Es hört auf den schönen Namen Design By Contract und hat seinen Ursprung in der Programmiersprache Eiffel. Design By Contract baut auf Vorbedingungen (Preconditions), die erfüllt sein müssen, damit eine Methode laufen kann, und auf Nachbedingungen (Postconditions) auf, die angeben ob meine Methode erfolgreich war. Beides zusammen ergibt den Vertrag (Contract)

Hier gibt es einen sehr schönen Code Project Artikel inklusive einer DBC C# Library (basierend auf .NET 1.x) zu diesem Thema. Übertragen auf unseren Fall sähe dies wie folgt aus:

    1         public static void DeleteTempFile(string fileName)

    2         {

    3             // Postconditions validieren

    4             Check.Require(fileName != null, "fileName may not be null");

    5             Check.Require(System.IO.Path.GetExtension(fileName) == ".tmp",

    6                 "Only temporary (*.tmp) files are allowed!");

    7             Check.Require(System.IO.File.Exists(fileName), "File not found");

    8 

    9             System.IO.File.Delete(fileName);

   10 

   11             // Preconditions validieren

   12             Check.Ensure(!System.IO.File.Exists(fileName), "File could not be deleted!");

   13         }

Eigentlich sehr schön, oder? Sobald eine der Prüfungen fehlschlägt wird übrigens eine PreconditionException bzw. PostconditionExceptoin geworfen. Es gibt übrigens noch eine zusätzliche Überladung, die Angabe einer InnerException, wie z. B. ArgumentNullException oder FileNotFoundExceptoin erlaubt.

Noch schöner fände ich es übrigens, wenn die Pre-/Postconditions nicht im Quellcode der Methode stehen, sondern als Attribut annotiert werden würden.

Eine kurze Suche bei Goolge ergab, dass es dazu bereits (mindestens) zwei Ansätze gibt:

Bisher hatte ich leider noch nicht die Zeit, mir eins von beidem anzusehen. Sollte ein Leser dieses Blog-Posts bereits Erfahrung mit einer der beiden Varianten, oder aber einer anderen Attribut-Basierten DBC Implmentierung Erfahrung haben, würde ich mich über einen Kommentar freuen.

... in allen anderen Fällen freue ich mich natürlich auch über Kommentare ;-)



blog.codemurai.de © André Krämer |Impressum | Abonieren

Shared Memory Dependency mit dynamischem Abfrage-Intervall

27.07.2008 18:14:39 | Klaus Bock

Wie bereits im vorherigen Beitrag als Idee angesprochen, habe ich die Möglichkeit der dynamischen Anpassung des Abfrage-Intervalls der SharedMemoyDependency-Klasse umgesetzt. Um diese Idee umzusetzen mussten einige Änderungen sowohl in der SharedMemory-Klasse als auch in der SharedMemoryDependency-Klasse eingebracht werden. Im Zuge der Änderungen habe ich die Methode AddObject() der SharedMemory-Klasse in InsertObject() umbenannt. Meiner Meinung nach beschreibt der neue Methodenname die Funktion der Methode besser da ja kein Objekt einer bestehenden Auflistung hinzugefügt, sondern in das SharedMemory-Segment eingefügt wird. Dabei wurde auch gleich für die beiden Methoden InsertObject(object) und GetObject() eine neue Überladung mit einem zusätzlichen Parameter von Typ bool eingefügt. Der zusätzliche Parameter autoLock legt fest ob die Sperre des Mutex der Klasse von der jeweiligen Methode verwaltet wird, oder vom Aufrufer der Methode. Auch habe ich die InsertObject()-Methode dahingehend Überarbeitet, dass jetzt nur noch alle 200 Millisekunden ein Objekt in das SharedMemory-Segment eingefügt werden kann. Sollte der Zeitintervall seit dem letzten Einfügen kürzer sein, wird in einer Schleife die Methode blockiert, bis der Zeitintervall erreicht ist. Eine ähnlich Begrenzung als minimalen Zeitintervall zwischen zwei Abfragen wird auch in der SharedMemoryDependency-Klasse verwendet. Hier wird der Zeitraum mittels einer Konstanten auf 20 Millisekunden begrenzt. Dies sind Werte die ich für sinnvoll erachte. Bei kleinen Objekten im SharedMemory-Segment können kürzere Intervalle vielleicht Sinn machen. Doch in der Regel sind es meist größere und komplexere Objekte die zwischen zwei Prozessen ausgetauscht werden sollen und ein DataSet oder ähnliches Objekt alle 200 Millisekunden zu aktualisieren müsste mehr als ausreichend sein. Im folgenden Diagramm sind noch einmal die Klassen SharedMemory und SharedMemoryDependency sowie die verwendetet Enumeration zu sehen.

SharedMemory Klassendiagramm

In der Klasse SharedMemory ist die neue Eigenschaft SerializedObjektSize vom Typ Int32 verfügbar. Mit ihr kann die serialisierte Größe des Objekts im Speicher abgerufen werden. Diese ist in sofern von Interesse, da die Größe eines serialisierten Objekts doch sehr stark von der Größe des Objekts vor der Serialisierung abweicht. Der Typ Int32 benötigt normalerweise 4 byte auf einer x86 Plattform. Serialisiert belegt ein Int32-Wert 54 byte im Speicher auf derselben Plattform. In der Regel braucht sich nicht um die Größe des Objekts welches im SharedMemory-Segment gespeichert werden soll gekümmert zu werden, da die benötigte Größe von der Methode InsertObject(object) automatisch ermittelt wird. Sollte doch einmal die serialisierte Größe eines Objekts ermittelt werden müssen, stehen hierfür die statischen Methode GetMinMemorySize(object) und GetOptMemorySize(object) zur Verfügung. Die erste Methode gibt die genaue serialisierte Größe des Objekts zurück. Die zweite Methode gibt die Größe optimiert auf 8 byte Blöcke zurück wobei der zurückgegebene Wert immer auf auf die nächsten 8 byte aufgerundet wird. Im Falle eines In32-Wertes gibt die erste Methode 54 und die zweite Methode 56 byte zurück. Die Größe des Objekts, welches im SharedMemory-Segment gespeichert werden kann wurde auf 500 Mb begrenzt. Größere Objekte dürften nur in den wenigsten Fällen benötigt werden. Auch gibt es für größere Objekte bessere Methoden wie etwa die Verwendung eines Memory-mapped File.

Soweit zu den Anpassungen und nun zur eigentlichen Dynamisierung des Abfrage-Intervalls der SharedMemoryDependency-Klasse. Das ganze kann nur funktionieren, wenn ein Verkürzen des Intervalls sofort geschieht und die Verlängerung in Abständen vorgenommen wird. Das es geklappt hat kann man an folgendem Video sehen.

private void CheckSegment(object state)
{
    // den aufrufenden Timer als neue Instanz erzeugen.
    Timer stateTimer = (Timer)state;

    // den aktuellen Wert aus dem Schlüssel-Segment lesen
    int i = this.segment.GetStatus();

    // wenn Rückgabewert 1, würde das Objekt im
    // SharedMemory Segment geändert
    if (i == 1)
    {
        // prüfen ob autoAdjust aktiv ist
        if (this.autoAdjust)
        {
            // prüfen ob die stopWatch läuft.
            if (this.stopWatch.IsRunning)
            {
                // stopWatch anhalten
                this.stopWatch.Stop();

                // und die verstrichene Zeit in Millisekunden an die
                // Optimierungs-Methode übergeben
                this.OptimizeIntervall(this.stopWatch.ElapsedMilliseconds);

                // und die geänderte pollTime einstellen
                stateTimer.Change(0, this.pollTime);
            }
        }

        // das Objekt aus dem SharedMemory-Segment holen
        object data = this.segment.GetObject(true);

        // EventHandler initialisieren
        EventHandler<SharedMemoryNotificationEventArgs> handler = this.OnChanged;
        if (handler != null)
        {
            // Event auslösen
            handler(this, new SharedMemoryNotificationEventArgs(
                data, this.segment.SerializedObjectSize, this.pollTime));
        }

        // Änderung signalisieren
        this.hasChanged = true;

        // wenn autoAdjust aktiv
        if (this.autoAdjust)
        {
            // stopWatch rücksetzen und neu starten.
            this.stopWatch = Stopwatch.StartNew();
        }
    }
    else if (i == -1)
    {
        // Objekt nicht im Speicher vorhanden oder es kann nicht
        // darauf zugegriffen werden.
        // Den aufrufenden Timer zerstören
        stateTimer.Dispose();

        // Überwachung als beendet signalisieren
        this.watching = false;

        // Ausnahme auslösen
        throw new SharedMemoryException(
            "Auf das Schlüssel-Segment im Speicher kann nicht zugegriffen werden.");
    }
}

Nachdem die Ermittlung der benötigten Zeit zwischen zwei Änderungen geklärt ist, kommt jetzt die Optimierung des Zeitintervalls an die Reihe. Um den ganzen Vorgang erst einmal zu starten, wird beim Initialisieren der Klasse der kleinste erlaubte Intervall von 20 Millisekunden verwendet. Bei Eintritt der ersten erkannten Änderung, liegt jetzt ein Zeitraum vor. Als Intervall für die Abfragen auf Änderungen, habe ich 10% des verstrichenen Zeitraums veranschlagt; also theoretisch immer 10 Abfragen zwischen zwei erkannten Änderungen. 10 Abfragen deshalb, um auch auf einen kürzeren Zeitraum zwischen zwei Änderungen regieren zu können. Zunächst wird also ein temporärer Intervall aus dem verstrichenen Zeitraum berechnet. Jetzt wird geprüft ob dieser temporäre Intervall kleiner als der aktuelle und größer als der minimal erlaubte Intervall ist. Sollte diese Bedingung erfüllt sein, wird der temporäre Intervall als neue Polltime verwendet und somit der Abfrageintervall sofort verkürzt. Um auch die maximale Polltime zu verringern, wird nach dreimaligem Unterschreiten der maximalen Polltime der Wert dieser mit dem gerade ermittelten Intervall ersetzt. Sollte der temporäre Intervall größer als der maximal ermittelte Intervall sein, wird nach dreimaligem überschreiten des Maximalwertes der Wert neu gesetzt. Somit ist gewährleistet, dass eine Verkürzung des Intervalls sofort eintritt und eine Verlängerung erst nach mehrmaligem Überschreiten des Maximalwertes. Hier nun das Listing der Methode OptimizeIntervall:

private void OptimizeIntervall(long intervall)
{
    // prüfen ob der Parameter intervall eine zulässige Größe aufweist.
    if (intervall > (long)int.MaxValue)
    {
        // intervall ist zu groß für einen int32-Wert.
        // auf den größtmöglichen Integerwert setzen.
        intervall = (long)int.MaxValue;
    }

    // intervall in einen int32-Wert casten.
    int tempTime = (int)intervall;

    // temporäre Polltime erstellen mit theoretisch 10 Abfragen
    // zwischen zwei erkannten Änderungen
    int tempPollTime = tempTime / 10;

    // prüfen ob der aktuelle Intervall größer als der minimale Intervall ist
    if (this.pollTime == minPollTime)
    {
        // aktuellen Intervall auf ermittelten Intervall setzen
        this.pollTime = tempPollTime;
    }

    // prüfen ob die temporäre Polltime kleiner als die aktuelle Polltime
    // und größer als der minimal erlaubte Intervall
    if (tempPollTime < this.pollTime && tempPollTime > minPollTime)
    {
        // tempPollTime ist kleiner, pollTime auf aktuellen Wert setzen.
        this.pollTime = tempPollTime;

        // prüfen wie oft der Intervall schon kleiner war
        // als die maxPollTime
        // Wenn der Intervall bereits 3 mal kleiner war,
        if (this.minTimeCounter >= 3)
        {
            // maxPollTime auf aktuellen Wert als neuen Vergleich setzen
            this.maxPollTime = tempPollTime;

            // Counter zurücksetzen
            this.minTimeCounter = 0;
        }
        else
        {
            // Counter erhöhen
            this.minTimeCounter++;
        }
    }

    // prüfen ob der aktuelle Intervall größer ist als
    // der größte gemessene Intervall.
    if (tempPollTime > this.maxPollTime)
    {
        // prüfen wie oft der Intervall schon größer war
        // als die maxPollTime.
        // Wenn der Intervall bereits 3 mal größer war,
        if (this.maxTimeCounter >= 3)
        {
            // maxPollTime auf aktuellen Wert als neuen Vergleich setzen
            this.maxPollTime = tempPollTime;

            // aktuelle pollTime auf aktuellen Wert setzen
            this.pollTime = tempPollTime;

            // Counter zurücksetzen
            this.maxTimeCounter = 0;
        }
        else
        {
            // Counter erhöhen
            this.maxTimeCounter++;
        }
    }
}

Im angehängten Demo sowie im obigen Video kann man sehr gut erkennen wie die Dependency-Klasse auf  Veränderungen des Intervalls zwischen den Änderungen im SharedMemory-Segment reagiert. Natürlich können in der vorliegenden Lösung auch feste Abfrageintervalle verwendet werden. Es braucht im Konstruktor nur ein Wert größer 0 angegeben zu werden.

IpcTest_Dynamic_PollTime

Technorati-Tags: | | |

UI Automation Events - der erste Schritt zum Record and Play?

25.07.2008 19:56:29 | Christian Binder

Letzte Woche haben Thomas Schissler und Ich wieder mal was zum Thema UIA ein Webcast aufgenommen, den Ihr nicht verpassen solltet.
Da ich bis nächste Woche in Redmond bin, um mich hauptsächlich mit dem VSTS Produkteam zu treffen.
Der Webcast aber ab dem 8.8 unter folgendem Link verfügbar sein und ich wahrscheinlich noch ein wenig
plannlos mit dem Jetlag kämpfe, hat Thomas auf seim Blog Hier schon mal den Source Code abgelegt.

Grüsse aus Redmond und schickt uns mal Feedback :-)

Chris

Die FrontPage-Hilfe zieht um in den Bastelgarten

23.07.2008 16:49:00 | Steffen Ritter

Ein Favorit, der in keiner Bookmarkliste eines jeden FrontPage- und Expression Web-Anwenders fehlen sollte ist Anita Conrads FrontPage-Hilfe. Eine seit vielen Jahren gepflegte Seite mit einem riesigen Schatz an Tutorials, Beispielen und Vorlagen und einem erstklassigen Forum zu FrontPage und Expression Web in dem sich alle Erfahrungsstufen, vom Anfänger bis zum Profi tummeln.

Die FrontPage-Hilfe ist diese Woche auf einen neuen Server gezogen und ist ab sofort hier erreichbar: http://frontpagehilfe.bastelgarten.de, das Forum ist ab sofort hier verfügbar: http://frontpageforum.bastelgarten.de/portal.php. Auf http://www.homepage-basteln.de finden sich weitere neue Tutorials die Anita in den letzten Monaten  geschrieben hat.

Wer die FrontPage-Hilfe schon kennt sollte daher schleunigst seine Favoriten aktualisieren, allen anderen empfehle ich dringend einen Besuch!

Entdeckungen am Nachmittag

23.07.2008 16:03:47 | Rainer Schuster

Entdeckungen am Vormittag

23.07.2008 11:55:59 | Rainer Schuster

Das Kulturereignis 2008

22.07.2008 14:58:00 | Steffen Ritter

850 Jahre München hin oder her, der kulturelle Höhepunkt des Jahres 2008 findet am Mittwoch, 23. Juli in der TitanicCity in München statt: Die Release Party zu „Terror From The Sky“, dem brandneuen Album von Suicide Booth (dem Münchner Elektro-Duo, das vergangenes Jahr mit der Debut-EP Aura reihenweise Preise und „Best of 2007“-Awards abgeräumt hat).

Terror From The Sky überrascht durch perfekt aufeinander abgestimmte Sounds, modulierte Stimmen im Robot-Space brechen die geistigen Grenzen des Zuhörers und lassen der eigenen Empfindung freien Lauf, um mittendrin ins Nirgendwo abzudriften. Feinster Synthpop trifft auf frühe Kraftwerk-Arrangements – und definiert New Wave neu. Wer hier nicht abhebt, ist sicherlich schon tot (…schreibt Negatief).

Ganz, ganz heißer Tipp für alle, die minimale Retro-Sounds mögen. Im Gegensatz zu anderen Bands, die den 80er-Jahre-Kult zum Geschäftsmodell entwickelt haben, klingen Suicide Booth jedoch nie eintönig. Kaum ein Einfluss aus den 80ern, der sich nicht wiederentdecken lässt. Suicide Booth erinnern manchmal an Italo-Pop-Perlen von Interpreten wie Den Harrow, Gazebo, P. Lion oder Raggio di Luna. An anderer Stelle hört man den Einfluss deutscher Musik, z.B. von den ganz frühen Alphaville (bevor sie mit Big in Japan ihren ersten Charterfolg hatten) oder dem Duo „The Twins". Aber auch wer mit Musik von „Der Plan" und „Die Doraus und die Marinas" etwas anfangen kann, wird Suicide Booth mögen. Es ist im Grunde genommen für alle Anhänger (elektronischer) Musik aus den 80ern etwas dabei, an manchen Stellen hört man sogar eindeutig Melodien von The Cure heraus („Arrival" etwa erinnert unweigerlich an „Pictures of you" ...schreibt das Mindestverzehr-Magazin).

Also, wer am Mittwoch noch nichts vor hat sollte die Gelegenheit wahrnehmen und auf die Release Party kommen, vermutlich eine der letzen Gelegenheiten die beiden Helden von Suicide Booth noch einmal zu treffen bevor die Platte Platin wird und ihnen der Erfolg zu Kopf steigt. Ort des Geschehens ist TitanicCity in München, ab ca. 22.00 Uhr: http://www.titaniccity.de

Zur Vorbereitung (und für die wenigen, die sich bisher noch nicht mit Suicide Booth beschäftigt haben) Suicide Booth bei MySpace: http://www.myspace.com/suicideboothmusic    

Video-Trainings zu Silverlight und Expression Blend

22.07.2008 13:37:46 | Rainer Schuster

Neben den bekannten Quellen auf silverlight.net, mymsdn.de und channel9 rund um Silverlight und Expression Blend stehen auf lynda.com interessierten Silverlight-Entwicklern kostenlose Video-Trainings  zur Verfügung.

Nach einer kurzen Registrierung (nur die Eingabe von Nachname, Vorname und E-Mail Adresse notwendig) können die Videos im Quicktime-Format angesehen werden. Sie beziehen sich zwar auf die jeweils erste Version, sind aber vom Umfang größer und eignen sicher daher besonders gut einen Überblick von Silverlight und Expression zu bekommen. Um sich dann speziell in die Silverlight 2.0 Entwicklung einzuarbeiten empfehle ich die Videos von http://silverlight.net/learn/.

Installation einer MOSS 2007 - Microsoft Office SharePoint Server 2007 -Demo Umgebung

22.07.2008 10:19:00 | Ozgur Aytekin

In diesem Beitrag habe ich für Euch diverse URLs zum Thema "Installation einer MOSS 2007 - Microsoft Office SharePoint Server 2007 -Demo Umgebung / Environment" zusammengestellt.

Bei einer Demo-Umgebung können auch virtuelle Maschinen (VirtualPC oder VMWare) verwendet werden:
- Virtual PC 2007 (Deutsch - German)
- Virtual PC 2007 (Englisch - English)

Download URL für die .NET Framework 3.0-Datei findet Ihr unter:
- Microsoft .NET Framework 3.0 Redistributable Package (Deutsch - German) Full-Package
- Microsoft .NET Framework 3.0 Redistributable Package (Englisch - English) Full-Package

Die Microsoft Office SharePoint Server 2007-Testversion findet Ihr unter:
- Microsoft Office SharePoint Server 2007-Testversion (Deutsch - German)
- Microsoft Office SharePoint Server 2007 Trial Version (Englisch - English)

Mit Hilfe von Language-Packs können die Websites und Websitesammlungen (Website-Collections) in mehreren Sprachen erstellt werden. So ist es möglich, dass Ihr Euren MOSS 2007 in englischer Sprache installiert und mit Hilfe von Language-Packs ein Website in Deutsch erstellt.

Die Language-Packs findet Ihr unter:
- 2007 Office System Language Packs für SharePoint Server 2007, Forms Server 2007, Project Server 2007 und SharePoint Server 2007 for Search (Deutsch - German)

Die folgende Installationsanleitung / Installationsguide kann ich Euch sehr empfehlen:
- How to Create a MOSS 2007 VPC Image: Part 1 - IIS, .NET Framework 2.0
- How to Create a MOSS 2007 VPC Image: Part 2 - POP3-Email
- How to Create a MOSS 2007 VPC Image: Part 3 - Outlook 2007
- How to Create a MOSS 2007 VPC Image: Part 4 - .NET Framework 3.0
- How to Create a MOSS 2007 VPC Image: Part 5 - SQL Server 2005 - I
- How to Create a MOSS 2007 VPC Image: Part 6 - SQL Server 2005 - II
- How to Create a MOSS 2007 VPC Image: Part 7 - SQL Server 2005 SP1
- How to Create a MOSS 2007 VPC Image: Part 8 - MOSS Accounts
- How to Create a MOSS 2007 VPC Image: Part 9 - MOSS 2007 Application
- How to Create a MOSS 2007 VPC Image: Part 10 - Internet Explorer Web Browser Security
- How to Create a MOSS 2007 VPC Image: Part 11 - Services
- How to Create a MOSS 2007 VPC Image: Part 12 - Shared Services Provider
- How to Create a MOSS 2007 VPC Image: Part 13 - Search Settings
- How to Create a MOSS 2007 VPC Image: Part 14 - Outgoing E-mail
- How to Create a MOSS 2007 VPC Image: Part 15 - Portal
- How to Create a MOSS 2007 VPC Image: Part 16 - Installing SharePoint Designer 2007
- How to Create a MOSS 2007 VPC Image: Part 17 - Installing Office 2007 Applications
- How to Create a MOSS 2007 VPC Image: Part 18 - Warm-up Scripts
- How to Create a MOSS 2007 VPC Image: Part 19 - Optimizing the MOSS 2007 VPC Image
- How to Create a MOSS 2007 VPC Image: Part 20 - Virtual PC Differencing Disk

Alternative Installationsanleitungen / Installationguides findet Ihr unter:
- Deploy Office SharePoint Server 2007 in a server farm environment (Microsoft Technet)
- Installing a New Microsoft Office SharePoint Server 2007 Portal: Step-by-Step Instructions

Für Windows Server 2008:
- How to move a SharePoint Server 2007 32-bit environment to a 64-bit environment on Windows Server 2008

Viel Spass...

IronEditor - Editor für DLR Sprachen released

20.07.2008 01:56:13 | Rainer Schuster

Wer wie ich regelmäßig die Codehosting Plattform Codeplex von Microsoft beobachtet findet immer wieder interessante Tools und Komponenten. Eines dieser Tools habe ich heute gefunden. Es ist IronEditor. Dynamische Sprachen wie Python, Ruby, PHP und Konsorten gibt es nicht erst seit gestern. Sie werden immer populärer und haben mittlerweile eine große Anhängerschaft.

Die Implementation auf der .NET Plattform wie z.B. IronPython und IronRuby finden ihren Weg in die Entwicklergemeinde mit großer Verbreitung wahrscheinlich spätestens ab Silverlight 2.0, denn hier werden genannte Sprachen - auf der DLR implementiert - ihren offiziellen Einzug in die Welt der Microsoft Produkte finden. Ein komplettes Studio mit der Visual Studio 2008 Shell gibt es bereits mit dem IronPython Studio.

Mit dem IronEditor hat Ben Hall - bekannt durch TDD als Commit Member für MbUnit - eine kleine Spielwiese für schnelles Prototyping entwickelt. Er soll dazu dienen, sich schnell mit den Sprachen vertraut zu machen und eine bequemere Umgebung als die bloßen Konsolenanwendung zu bieten. Wem es also zu kompliziert ist das gesamte Studio zu laden sei dieser Download wärmstes Empfohlen. Viele Spaß beim Testen.

LINQ to SQL und WinForm - DataBinding mit BindingSource (DataGridView, TextBox, formattingEnabled Parameter)

18.07.2008 22:36:00 | Ozgur Aytekin

Ein Leser von meinem Buch LINQ hat mir die Frage gestellt, wie er in einer WinForm Applikation (WinForm Application) ein numerisches Feld (numeric field) in der Datenbank (database) aktualisieren kann.

Er schreibt, dass er nur die String-Felder (string fields) in der Datenbank (database) aktualisieren kann aber die Änderungen an Numeric-Felder ignoriert werden.

In dem folgenden Beispiel wird der SQL Server als Datenbank (database) und LINQ to SQL wird für den Datenzugriff verwendet. Die Daten werden in einem DataGridView-Control aufgelistet, die mit Hilfe einer BindingSource-Komponente mit den Daten verknüpft ist. Zusätzlich ermöglichen die zwei TextBox-Controls die Bearbeitung der Daten. Die beiden TextBox-Controls sind ebenfalls mit Hilfe der BindingSource-Komponente mit den Daten verknüpft.

Die Datenbanktabelle Person im Design-Modus:

Table Person Design Mode

Script für die Datenbanktabelle (database table) Person:

USE [SampleDb]
GO
/****** Object: Table [dbo].[Person] Script Date: 07/18/2008 21:56:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Person](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Age] [int] NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]


WinForm im Desing-Modus sieht wie folgt aus:



image



Und Source-Code innerhalb unser WinForm sieht wie folgt aus:





using System;
using System.Linq;
using System.Windows.Forms;

namespace LinqToSqlDataBindingSample
{
public partial class Form1 : Form
{
private SampleDbDataContext db;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
this.db = new SampleDbDataContext();

this.loadData();
}

private void loadData()
{
var qry = from p in db.Persons
select p;

this.bindingSource1 = new BindingSource();
this.bindingSource1.DataSource = qry;

this.dataGridView1.DataSource = this.bindingSource1;

this.txtName.DataBindings.Add("Text", this.bindingSource1, "Name");
this.txtAge.DataBindings.Add("Text", this.bindingSource1, "Age", true);
}

private void btnOK_Click(object sender, EventArgs e)
{
MessageBox.Show(this.db.GetChangeSet().ToString());

this.db.SubmitChanges();
}
}
}



Das Age-Feld ist hier als Numeric bzw. als Int definiert. Wie in der folgende Zeilen auch ersichtlich, unterscheiden sich die DataBindings. Beim Feld Age wird noch der Parameter formattingEnabled verwendet.




this.txtName.DataBindings.Add("Text", this.bindingSource1, "Name");
this.txtAge.DataBindings.Add("Text", this.bindingSource1, "Age", true);



ControlBindingsCollection Add formattingEnabled



Wenn der Parameter formattingEnabled den Wert false besitzt oder nicht verwendet wird, dann wird die eingegebene Zahl in das TextBox-Control vom DataBinding ignoriert und der alte Wert wird wieder in das Feld zurück geschrieben.



Ein kleiner Ausschnitt aus einem MSDN-Artikel zu diesem Thema:



Automatic Formatting Using Code


In earlier versions of the .NET Framework you had to manually perform the type conversions and formatting using the Format and Parse events of the Binding object. You can now do this by enabling formatting on the Binding object, either by setting the FormattingEnabled property directly or passing true to the Add method of the ControlBindingsCollection. In addition, you will need to specify a format string, either using the Add method, or by setting the FormatString property of the Binding object.



The following code includes two formatting examples. The first formatting example shows how to create a binding between a DateTime value in the data source and the Text value of a TextBox control. The FormattingEnabled property is set on the Binding object, the FormatString value specifies that the DateTime should be formatted as a long date/time string, and the binding is added to the collection.



Behind the Scenes: Improvements to Windows Forms Data Binding in the .NET Framework 2.0, Part 1



Das Beispielprojekt findet Ihr unter: LinqToSqlDataBindingSample



Viel Spass

RibbonX kennt .NET Images

18.07.2008 13:10:55 | Jens Häupel

Das ist irgendwie total an mir vorbei gegangen. Während in der Beta (VS 2008 Office Integration) bei dynamisch befüllten Ribbon Controls:

 

<dropDown id="MyDropDown" getItemID="getDDItemID" getItemCount="getDDItemCount" getItemLabel="getDDItemLabel" getItemImage="getDDItemImage" onAction="onDDAction"> </dropDown>

immer noch die Images als COM Struktur IPictureDisp übergeben:

 

public stdole.IPictureDisp getDDItemImage(Office.IRibbonControl control, int index) { MemoryStream iconStream = new MemoryStream(...); return ImageConverter.ImageToPictureDisp(Image.FromStream(iconStream)); }

und dann datürlich eine Umwandlungsroutine geschrieben werden mußte, die Image in IPictureDisp konvertiert:

 

internal class ImageConverter : System.Windows.Forms.AxHost { public ImageConverter() : base(string.Empty) { } static public stdole.IPictureDisp ImageToPictureDisp(Image image) { return (stdole.IPictureDisp)GetIPictureDispFromPicture(image); } static public stdole.IPictureDisp IconToPictureDisp(Icon icon) { return ImageToPictureDisp(icon.ToBitmap()); } }

kann jetzt das Image direkt übergeben werden:

 

public Image getDDItemImage(Office.IRibbonControl control, int index) { MemoryStream iconStream = new MemoryStream(...); return Image.FromStream(iconStream); }

Leider geht das nicht mit Icons. Also wer schön freigestellte Icons verwendet, wird nach wie vor die Umwandlungsroutine nutzen müssen (Was nicht wirklich viel mehr Aufwand darstellt).

OT: neues Blog

17.07.2008 12:36:00 | Jürgen Gutsch

Für alternative Themen, die nicht zu ASP, ASP.NET, .NET und Webentwicklung passen, habe ich mal ein neues Blog unter http://juergen.gutsch-online.de/ aufgemacht. Falls es also jemanden interessiert, was ich noch so zu schreiben habe... ;-)

SQL Server: hotfix KB948109 fails with error 1920

17.07.2008 11:42:06 | Andre Loker

It seems that KB948109 has caused some trouble for many people. On my Windows Server 2003 machine the installation failed with error code 1920:

MSP Error: 1920  Service 'SQL Server VSS Writer' (SQLWriter) failed to start.  Verify that you have sufficient privileges to start system services.

I checked the SQL Server related services and found that the SQLWriter service and other services were disabled.

image

This wasn't really surprising, given that I had disabled all services I don't use. However it caused the installation to fail, becausen the installer wasn't able to start the disabled services.

image

So I set the service startup type to manual, manually re-ran the installer and everything worked out fine. After installation I disabled the unneeded services again.

Mehr Informationen zu Silverlight und dynamische Sprachen

17.07.2008 10:39:35 | Oliver Scheer

Wer Fan von dynamischen Sprachen ist (mein Chef ist einer der größten Fans), den wird es sicherlich freuen, das Silverlight dies auch beherrscht.

Mehr Informationen gibt es dazu unter den folgenden Links:

Silverlight SDK mit DRL-Support
http://silverlight.net/GetStarted/

Dynamic Languages in Silverlight
http://msdn.microsoft.com/en-us/library/cc189024(VS.95).aspx

Quickstart to Programming Silverlight in DLR
http://silverlight.net/quickstarts/ProgramDlr.aspx

Alles zu IronPython und IronRuby auf einen Blick
http://blogs.msdn.com/ironpython/

Background Infos zur DLR
http://msdn.microsoft.com/en-us/library/microsoft.scripting.silverlight.dynamicapplication(VS.95).aspx

Allgemeine Infos zu ASP und DLR
http://quickstarts.asp.net/futures/dlr/default.aspx

Scott Guthrie zu IronRuby
http://weblogs.asp.net/scottgu/archive/2007/07/23/first-look-at-ironruby.aspx

Neues TFS Power Tool July Release ist da!

17.07.2008 10:33:51 | Christian Binder

Download hier und was ist Neu?

A new UI for subscribing for TFS Alerts (NEW!)

A VS plugin (and therefore much more friendly than the BisSubscribe command line tool) and enables much more flexible subscriptions.
Alerts can be based on Checkin, Work Item change or Build completion
Removed "Disconnect from Team Foundation server" from Menu

Best Practices Analyzer (BPA Tool) (Updated)

Support for SQLServer 2008
Incremental improvements and bug fixes

TFSUsers  (NEW!)

This tool allows administrators to update TFS Work Items and event subscriptions when names are changed in Active Directory.

Work Item Templates and Process Template Editor (Updated)

Bug fixes

TFPT (Updated)

tfpt treeclean was an existing command that will delete files on your local disk that are in source controlled folders but are not in source control.  The primary difference is that it is now dramatically faster.  tfpt treeclean now replaces tfpt online /purge, which is no longer available.

A new tfpt scorch command - scorch is like treeclean except that additionally it re-downloads content that has been changed locally without pending an edit and content that has been deleted locally without pending a delete.

Important bug fixes in tfpt unshelve /migrate where the base versions for a 3 way merge were sometimes being computed incorrectly.

Improvements in online, scorch and treeclean to now handle folders as well as files, to properly handle single file mappings and proper treatment of items with pending changes other than "edit".

Viel Spass

Chris

ASP.NET MVC Preview 4 auf Codeplex

17.07.2008 08:04:57 | Robert Mühsig

Das ASP.NET MVC Team hat die Preview 4 auf Codeplex veröffentlicht.

Infos am Rande:
Das Thema AJAX + ASP.NET MVC wird in einem kurzen Blogpost von Scott Hanselman aufgegriffen.
Der ComponentController ist nun ebenfalls etwas verändert wurden, wie Rob auf seinen Blog schreibt.
Als letztes noch ein paar Hinweise von Phil Haack, was z.B. unter der neuen Assembly Microsoft.Web.Mvc zu verstehen ist.

ShareThis

VB.NET: Lustige Effekte mit dem "With-Block"

17.07.2008 07:35:44 | Andre Kraemer

Wie in meinem letzten Blog-Post geschrieben, arbeite ich derzeit nach langer Abstinenz wieder an einigen VB.NET Projekten. Dabei stieß ich auch auf den "With-Block", einen alten Bekannten aus VB 6 Zeiten, von dem in diesen Projekten intensiver Gebrauch gemacht wurde. Ein solches Konstrukt erlaubt es innerhalb eines Blocks ein Objekt anzugeben, auf das alle Statements ausgeführt werden, die nicht näher qualifiziert werden. So kann man einiges an Tipp-Arbeit sparen. Folgendes Beispiel soll dies verdeutlichen:

  Sub Main()

    Dim p As Person = New Person

    With p

      .Name = "Mueller"

      .Age = 29

      Console.WriteLine("Name:{0}, Alter:{1}", .Name, .Age)

    End With

 

    Console.Read()

  End Sub

So gerne ich das With ... End With auch unter VB 6 genutzt hatte, so zuwider war mir nach nun mehr als sechs Jahren C# die Nutzung unter VB.NET. Ich hatte das Gefühl, dass mein Code durch den Einsatz des "With-Blocks" Struktur und Lesbarkeit verlor und eine Hintertür für unnötige Fehlerquellen geöffnet wird.

Bestätigt wurde mein Gefühl als ich Zeilen in folgender Art las:

  Sub Main()

    Dim p As Person = New Person

    With p

      .Name = "Mueller"

      .Age = 29

      p = New Person

      p.Age = 21

      p.Name = "Meier"

      Console.WriteLine("Name:{0}, Alter:{1}", .Name, .Age)  ' Wird hier nun Mueller oder Meier ausgegeben?

      Console.WriteLine("Name:{0}, Alter:{1}", p.Name, p.Age) ' Gleicht die Ausgabe dieser Zeile der vorherigen?

    End With

 

    Console.Read()

  End Sub

An dieser Stelle war sich das gesamte Team unsicher, welchen Effekt die Zeile p = new Person innerhalb des Blocks haben wird. Es herrschte rege Diskussion, ob nur die Anweisungen, die über "p." qualifiziert werden das neue Objekt verändern, oder ob auch die Anweisungen die nur über den "." qualifiziert werden das neue Objekt verändern.

Um die Antwort vorweg zu nehmen: Wir haben nun zwei Objekte von der Klasse Person im Speicher, die beide referenziert werden. Das ursprüngliche Objekt wird durch "." angesprochen, dass neu erstelle Objekt durch "p.".

Sehr schön sieht man das ganze auch wenn man sich den zugehörigen IL-Code ansieht:

    1 .method public static void Main() cil managed

    2 {

    3     .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()

    4     .entrypoint

    5     .maxstack 3

    6     .locals init (

    7         [0] class WithTest.Person p,

    8         [1] class WithTest.Person VB$t_ref$L0)

    9     L_0000: nop

   10     L_0001: newobj instance void WithTest.Person::.ctor()

   11     L_0006: stloc.0

   12     L_0007: ldloc.0

   13     L_0008: stloc.1

   14     L_0009: ldloc.1

   15     L_000a: ldstr "Mueller"

   16     L_000f: callvirt instance void WithTest.Person::set_Name(object)

   17     L_0014: nop

   18     L_0015: ldloc.1

   19     L_0016: ldc.i4.s 0x1d

   20     L_0018: box int32

   21     L_001d: callvirt instance void WithTest.Person::set_Age(object)

   22     L_0022: nop

   23     L_0023: newobj instance void WithTest.Person::.ctor()

   24     L_0028: stloc.0

   25     L_0029: ldloc.0

   26     L_002a: ldstr "Meier"

   27     L_002f: callvirt instance void WithTest.Person::set_Name(object)

   28     L_0034: nop

   29     L_0035: ldloc.0

   30     L_0036: ldc.i4.s 0x15

   31     L_0038: box int32

   32     L_003d: callvirt instance void WithTest.Person::set_Age(object)

   33     L_0042: nop

   34     L_0043: ldstr "Name:{0}, Alter:{1}"

   35     L_0048: ldloc.1

   36     L_0049: callvirt instance object WithTest.Person::get_Name()

   37     L_004e: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)

   38     L_0053: ldloc.1

   39     L_0054: callvirt instance object WithTest.Person::get_Age()

   40     L_0059: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)

   41     L_005e: call void [mscorlib]System.Console::WriteLine(string, object, object)

   42     L_0063: nop

   43     L_0064: ldstr "Name:{0}, Alter:{1}"

   44     L_0069: ldloc.0

   45     L_006a: callvirt instance object WithTest.Person::get_Name()

   46     L_006f: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)

   47     L_0074: ldloc.0

   48     L_0075: callvirt instance object WithTest.Person::get_Age()

   49     L_007a: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)

   50     L_007f: call void [mscorlib]System.Console::WriteLine(string, object, object)

   51     L_0084: nop

   52     L_0085: ldnull

   53     L_0086: stloc.1

   54     L_0087: call int32 [mscorlib]System.Console::Read()

   55     L_008c: pop

   56     L_008d: nop

   57     L_008e: ret

   58 }

Wie man in den Zeilen 6 - 8 sieht werden zunächst zwei Variablen vom Typ Person auf dem Stack abgelegt. Die in unserem VB.NET SourceCode deklarierte Variable p wird an der Index Position 0 des Stacks abgelegt (Zeile 7) und zusätzlich wird eine Variable VB$t_ref$L0 an der Position 1 abgelegt. Letzte Variable wird später zur Umsetzung unseres "With-Blocks" genutzt. In der Zeile 10 wird nun ein neues Objekt der Klasse Person erstellt und auf dem Evaluationsstack abgelegt.

In der Zeile 11 wird dieses Objekt der lokalen Variablen am Index 0, also unserer Variablen p, zugewiesen. In der Zeile 12 wird der Inhalt der Variablen am Index 0, also unser Personen Objekt, wieder auf den Evaluationsstack geladen, um in Zeile 13 schließlich der Variablen am Index 1 (VB$t_ref$L0) zugewiesen zu werden.

In den Zeilen 15 - 22 werden anschließend die Werte für den Namen und das Alter auf dem Stack abgelegt und die entsprechenden Setter-Methoden des durch die Variable am Index 1 (VB$t_ref$L0) referenzierten Objektes aufgerufen.

Bis hier hin ist die Welt noch in Ordnung. Wir haben zwei Variablen vom Typ Person, die beide das selbe Objekt referenzieren. Die Freude währt jedoch nur bis zur Zeile 23. In dieser Zeile wird nämlich ein neues Objekt vom Typ Person erstellt und in den folgenden Zeilen der lokalen Variablen am Index 0 (p) zugewiesen. Ab diesem Zeitpunkt greifen die Anweisungen, die über "." und die, die über "p." qualifizieren auf verschiedene Objekte zu!

Welche Fehler sich in solch einem Szenario bei kleinen Unachtsamkeiten  einschleichen können, liegt auf der Hand.

Daher lautet mein persönliches Fazit:

Die Zeit, die ich durchs Tippen beim Einsatz des "With-Blocks" spare, büße ich während der Fehlersuche unter Umständen mehrfach wieder ein. Daher lautet meine Devise für die Zukunft: Finger weg von "with... end with".

Kommentare willkommen :-)



blog.codemurai.de © André Krämer |Impressum | Abonieren

Wie rufe ich die Hilfe aus einer WindowsApplikation auf?

15.07.2008 16:34:15 | Rainer Schuster

NamingMethod

Unter benanntem Thema gibt es in der Hilfe zum SHFB eine Beschreibung welche Möglichkeiten es zur Benennung der Indentifizierer gibt. Anders gesagt, welche Methode benutzt wird um die Namen für Verweise zu erzeugen. Die Themen, die aus den XML-Kommentaren erstellt werden speichert Sandcastle beim Buildprocess in eigenen HTML-Dateien. Der Name dieser Dateien richtet sich nach den in diesem Artikel genannten Möglichkeiten. Um die Hilfe zu generieren muss Sandcastle für die Verweise der Klassen und der vorliegenden Codestruktur entsprechende IDs erzeugen. Dazu gibt es im wesentlichen 3 Möglichkeiten.

  1. GUID
  2. MemberName
  3. HashedMemberName

CodeSnippets

Die Berechnung der Topics kann durch folgende Snippets erzeugt werden (Auszug aus einer älteren SHFB-Hilfe)!

1. für GUID :

// Compute the MD5 hash of the member name.  Assume
// memberName contains the member name of the topic
// to show.
HashAlgorithm md5 = HashAlgorithm.Create("MD5");
 
byte[] input = Encoding.UTF8.GetBytes(memberName);
byte[] output = md5.ComputeHash(input);
 
// Format it as a GUID value
Guid pageId = new Guid(output);
 
// Show the topic in the help file.  Remember to add
// the "html\" folder name and the ".htm" extension.
Help.ShowHelp(parentForm, helpFilePath,
    HelpNavigator.Topic, @"html\" + pageId.ToString() + ".htm");

2. für MemberName:

Regex reInvalidChars = new Regex("[:.`#<>]");
 
// Assume memberName contains the member name of the
// topic to show.
string pageName = reInvalidChars.Replace(
    memberName, "_");
 
// Show the topic in the help file.  Remember to add
// the "html\" folder name and the ".htm" extension.
Help.ShowHelp(parentForm, helpFilePath,
    HelpNavigator.Topic, @"html\" + pageName + ".htm");

3. für HashedMemberName

// Assume memberName contains the member name of the
// topic to show.
string pageName = String.Format("{0:X}",
    memberName.GetHashCode());
 
// Show the topic in the help file.  Remember to add
// the "html\" folder name and the ".htm" extension.
Help.ShowHelp(parentForm, helpFilePath,
    HelpNavigator.Topic, @"html\" + pageName + ".htm");

 

Erklärung

Alle Hilfe-Themen besitzen ein Prefix um zu kennzeichnen welchen Bereichen sie zugeordnet sind. Diese sind nun wichtig. Deshalb hier besonderen Augenmerk darauf. Aus der Hilfe dazu hier der Auszug.

Note that each help topic ID will have a prefix denoting what it represents:

  • R: represents the root namespace page. There will always be a single entry named R:Project.
  • N: represents a namespace help topic that lists all of the members of the given namespace.
  • T: represents a type help topic that lists all of the members of the given type.
  • Overload: represents a help topic that lists all of the overloads for a particular class member.
  • F: represents a field member.
  • E: represents an event member.
  • P: represents a property.
  • M: represents a method.

Kritisch wird das bei tief verschachtelten Namesräumen bei den der Name zu lang wird. Dann sollte auf eine der anderen Möglichkeiten ausgewichen werden. Schwierig wird dann die eindeutige Referenzierung des z.B. gewünschten Members, da nur noch eine Kryptische ID vorhanden ist.

Welchen Vorteil bringt mir das noch aus eine schön strukturierte Hilfe? Wird eine Dokumentation für ein Windowsprogramm erstellt kann über den Hilfekontext mit der F1 Taste je nach gewählter Methode (siehe 1., 2., oder  3.) das Hilfethema referenziert werden.

Wer über den Index die Hilfe aufrufen will, muss einen anderen Member der HelpNavigator Enumeration aufrufen. Details dazu siehe MSDN

Aspektorientierung mit ASP.NET MVC

15.07.2008 11:50:21 | Rainer Schuster

Wer sich schon mit Aspektorientierter Programmierung (AOP) beschäftigt hat, weiß die Vorteile von Aspekten zu schätzen. Sie helfen einem die querschneidenden Belange wie Logging oder Caching zentraler zu verwalten. Der Code wird modularer und dadurch leichter wartbar. Frameworks für einen AOP Ansatz unter .NET gibt es schon länger (PostSharp, Spring.NET ...). Microsoft selbst benutzt auch AOP Ansätze mit den Attributen. AOP kann im vereinfachten Sinne als eine Weiterentwicklung der Metaprogrammierung gesehen werden. Als neuestes Beispiel ist hier ASP.NET MVC zu nennen. In dem Post von Scott Gu für die Preview 4 stellt er einige neue Filter vor. Was hier als Filter bezeichnet wird, kann auch ganz einfach als Aspekt gesehen werden. Es wird Code in eine Komponente verlagert und kann zentral verwaltet werden. Das einweben des Codes - das Hinzufügen der Funktionalität - geschieht durch die Attributierung der Klassen und Methoden. Somit wird der agile und modulare Ansatz von ASP.NET MVC noch weiter unterstrichen.

Channel 9 – Volker Will – Evangelism Manager

15.07.2008 11:04:00 | Oliver Scheer

Beim meinem letzten Besuch in Redmond habe ich viele deutsche Kollegen getroffen und nach ihren Erfahrungen mit Microsoft und Amerika befragt. Volker Will ist Evangelism Manager in Redmond und ist für Windows Server 2008 (insb. HPC) bzgl Evangelisierung zuständig. Er lebt bereits seit 5 Jahren in Redmond, war davor aber auch lange Jahre in Deutschland tätig.


Deutsche Corp Kollegen: Volker Will - Evangelism Manager

Hier geht es zum Video.

Visual Studio 2008 SP1 TFS Features im Überblick

15.07.2008 09:11:00 | Christian Binder

Mit VS SP1 gibt es auch neue TFS Features, hauptsächlich im Version Control Bereich, z.B Drag&Drop
files in den SCE.

Mit VS Sp1 unterstützen wird die aktuellen SQL2008 Builds. Als TFS2008 im November 2007 ge-launched wurde, waren wir noch SQL2008 kompatibel. In den nachfolgenden SQL2008 CTP's wurden einige Details geändert, so dass die Installation mit diesen CTP's nicht mehr funktionierte.

Hier in Brian's Blog findet Ihr eine detailierte Auflistung der Features mit Screenshots.

Viel Spass

Chris

ASP.NET MVC - Preview 4 kommt demnächst

14.07.2008 23:09:20 | Robert Mühsig

ScottGu hat in seinem Blog die neusten Informationen zur kommenden Preview 4 des ASP.NET MVC Frameworks bereitgestellt.

Mit dem “Preview 4″ kommen einige High-Level-Features hinzu:

  • Built-In Filter:
    • OutputCache: Cachingfeature
    • HandleError: Errorhandling
    • Authorization: Nutzt das Membership System / Formauthentication und bringt Standardfunktionalität mit
  • Erweitertes Template:
    • Standard-Fehlerseite unter “Shared”
  • Built-In Authorization:
    • Im Zusammenhang mit dem AuthorizationFilter gibt es auch gleich direkt einen AccountController der die Standardfunktionalität hat:
      • Nutzer anmelden
      • Passwort vergessen
      • Registrieren
  • AJAX Support:
    • Scott wird dazu noch einen seperaten Blogpost schreiben
  • Testing-Support für TempData und sicherlich noch kleine weitere Verbesserungen

Das eingebaute Membership-System soll später noch abstrahiert werden - sodass man auch sein eigenes System (ohne ein Membership-Provider zu schreiben) nutzen kann. Das ideale für mein ReadYou Projekt :)

Die neuen Features konnten bereits mit Preview 3 bereitgestellt werden - mit dieser Preview sollen einige nette Features der Allgemeinheit leicht zur Verfügung gestellt werden - auf die AJAX Funktionalität bin ich auch schon gespannt :)

Fazit: Nix weltbewegendes, aber eine gute Fortführung des Projekts um es später für viele leicht nutzbar zu machen :)

ShareThis

LINQ to Objects und DataBinding mit DataGridView

14.07.2008 19:56:00 | Ozgur Aytekin

Ein Leser von meinem Buch LINQ hat mir die Frage gestellt, wie er mit LINQ to Objects erstellte Objekte (Objects) in einem DataGridView Control auflisten kann.

Beispiel (Sample) 1

Im Beispiel-Code haben wir die Klasse (Class) Person zwei Public Felder (Fields) mit dem Namen Age und Name.

public class Person
{
public int Age;
public string Name;
}



Als nächstes erstellen wir unsere Daten wie folgt:




List<Person> firends = new List<Person>();

Person friend1 = new Person();
friend1.Name = "Hans";
friend1.Age = 38;
firends.Add(friend1);

Person friend2 = new Person();
friend2.Name = "Karin";
friend2.Age = 26;
firends.Add(friend2);



Bevor wird die Daten (friends) an die DataSource-Eigenschaft (Property) des DataGridView-Controls zuweisen, ist es wichtig, dass die gewünschten Felder mit Hilfe einer Abfrage bestimmt werden.




var friend = from f in friends
select new { f.Age, f.Name };



Durch die Verwendung der ToList() Methode werden unsere Daten DataGridView-Control tauglich.




this.dataGridView1.AutoGenerateColumns = true;
this.dataGridView1.DataSource = friend.ToList();



Nach der Zuweisung werden die Daten automatisch im Control aufgelistet.image



Den kompletten Source-Code findet Ihr wie folgt:




using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace LinqToObjectsDataGridView
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnShowData_Click(object sender, EventArgs e)
{
List<Person> friends = new List<Person>();

Person friend1 = new Person();
friend1.Name = "Hans";
friend1.Age = 38;
friends.Add(friend1);

Person friend2 = new Person();
friend2.Name = "Karin";
friend2.Age = 26;
friends.Add(friend2);

var qry = from f in friends
select new { f.Age, f.Name };

this.dataGridView1.AutoGenerateColumns = true;
this.dataGridView1.DataSource = qry.ToList();
}
}

public class Person
{
public int Age;
public string Name;
}
}



Beispiel (Sample) 2



Wenn Sie die Getter-und-Setter in Ihrer Klasse verwenden, dann müssen Sie die Namen der Felder nicht explizit definieren.




public class PersonExtended
{
private int _Age;
public int Age
{
get { return _Age; }
set { _Age = value; }
}

private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}



Die Abfrage sieht in diesem Beispiel wie folgt aus:




IEnumerable<PersonExtended> qry = from f in friends
select f;



Und anschließend die Zuweisung an die DataSource-Eigenschaft:




this.dataGridView1.AutoGenerateColumns = true;
this.dataGridView1.DataSource = qry.ToList();



Als Resultat erhalten wir die gleichen Einträge:



image



Source-Code sieht wie folgt aus:




using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace LinqToObjectsDataGridView
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnShowData_Click(object sender, EventArgs e)
{
List<PersonExtended> friends = new List<PersonExtended>();
PersonExtended friend1 = new PersonExtended();
friend1.Name = "Hans";
friend1.Age = 38;
friends.Add(friend1);

PersonExtended friend2 = new PersonExtended();
friend2.Name = "Karin";
friend2.Age = 26;
friends.Add(friend2);

IEnumerable<PersonExtended> qry = from f in friends
select f;

this.dataGridView1.AutoGenerateColumns = true;
this.dataGridView1.DataSource = qry.ToList();
}
}

public class PersonExtended
{
private int _Age;
public int Age
{
get { return _Age; }
set { _Age = value; }
}

private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
}



Das komplette ZIP-File mit Source findet Ihr unter: LinqToObjectsDataGridView



Viel Spaß...

Channel 9 – Walter von Koch Program Manager IE8

14.07.2008 10:51:29 | Oliver Scheer

Vor einiger Zeit war ich Redmond auf dem Campus und habe deutsche Kollegen besucht. Walter von Koch ist seit 6 Jahr bei Microsoft und arbeitet seit ca. 3 Jahren als Program Manager im Internet Explorer Team. In diesem Interview erzählt etwas aus dem Nähkosten zum neuen IE8. Er erzählt über seinen Werdegang vor und bei Microsoft.


Deutsche Corp-Kollegen: Walter von Koch - IE 8 Program Manager

Hier geht es zum Video.

Shared Memory Dependency - bei Änderungen benachrichtigen

13.07.2008 17:54:27 | Klaus Bock

Im vorherigen Artikel habe ich die Verwendung eines SharedMemory Segment mit Hilfe zweier Konsolenanwendungen gezeigt. Dort wurde in der Client-Anwendung reagiert, indem ein Objekt aus dem SharedMemory-Segment geladen und mit einem gespeicherten Objekt verglichen wurde. Das funktioniert ja mit kleineren Objekten im Shared Memory ganz gut. Doch was ist wenn eine größere DataTable oder gar ein mehre Mb großes DataSet, oder ein ähnlich komplexes Objekt, im SharedMemory gespeichert ist? Allein das laden der Objekte zum vergleichen, sowie der Vergleich an sich dürften sich auf die Reaktionsgeschwindigkeit dieser Anwendung sehr nachteilig auswirken. Zur Lösung dieses Problems bietet sich eine Dependency-Klasse, ähnlich der SqlDependency-Klasse, an. Nur wie muss die Implementierung solch einer Dependency-Klasse aussehen? Die grundlegenden Funktionen sollten sein:

  • Einen Mechanismus zur Benachrichtigung des Abonnenten.
  • Der Abonnent braucht keine Kenntnis vom zugrundeliegenden SharedMemory Segment zu haben.
  • Der Intervall in dem das SharedMemory Segment abgefragt wird muss einstellbar sein.
  • Der Benachrichtigung's-Mechanismus sollte das geänderte Objekt aus dem SharedMemory Segment liefern.
  • Es sollte nur der Namen des zu überwachenden SharedMemory Segments angegeben werden müssen.
  • Eine Möglichkeit das SharedMemory Segment in sehr kurzen Abständen auf Änderungen zu überprüfen.

Um den letztgenannten Punkt zu realisieren habe ich das Konzept der SharedMemory-Klasse noch einmal dahingehen überarbeitet, so dass jetzt zwei Segmente erzeugt werden. Zusätzlich zum bereits bestehenden Segment wird jetzt ein zweites Segment erzeugt, welches lediglich einen Integer-Wert speichert der den Status des Daten-Segments darstellt. Beim speichern eines Objekts in das Daten-Segment wird der Wert 1 in das Schlüssel-Segment geschrieben, welcher eine Änderung darstellt. Wird nun das Objekt von der GetObjekt() Methode abgerufen, schreibt diese den Wert 0 in das Schlüssel-Segment und kennzeichnet es so als gelesen. Solange der Abfrage-Mechanismus nun den Wert 0 liest, brauchen die Daten nicht aus dem Daten-Segment abgerufen zu werden. Das Abrufen des Integer-Wertes aus dem Schlüssel-Segment geht wirklich sehr schnell. Real weit unter einer Millisekunde. Dadurch lasse sich sehr kurze Abfrage-Intervalle realisieren. Um zu zeigen wie schnell die Dependency-Klasse auf Änderungen im Daten-Segment reagiert, habe ich ein Demo erstellt in dem in zufälligen Intervallen in das Daten-Segment geschrieben wird. Hier vorab ein kleines Video:

// neue Instanz der SharedMemoryDependency initialisieren
// mit einem Abfrage-Intervall von 15 Millisekunden
SharedMemoryDependency dependency = new SharedMemoryDependency("testing", 15);

// OnChanged Eventhandler registrieren
dependency.OnChanged +=
    new EventHandler<SharedMemoryNotificationEventArgs>(DependencyOnChanged);

// prüfen ob dependency bereits gestartet wurde
if (!dependency.Enabled)
{
    // dependency jetzt starten
    dependency.Start();
}

Beim registrieren des EventHandler OnChanged kann automatisch ein Delegat erzeugt werden. In der Methode kann das geänderte Objekt aus dem Daten-Segment direkt als Eigenschaft des SharedMemoryNotificationEventArgs-Arguments empfangen werden.

static void DependencyOnChanged(object sender, SharedMemoryNotificationEventArgs e)
{
    Console.WriteLine("  lese: " + e.ChangedContent.ToString());
}

Soweit zur Verwendung der SharedMemoryDependency-Klasse in einer Client-Anwendung.

Wie bereits oben schon angesprochen, habe ich die SharedMemory-Klasse überarbeitet um das Zusammenspiel mit der SharedMemory-Klasse überhaupt zu ermöglichen. Um den aktuellen Status eines Objekts im Speicher abfragen zu können, wurde die interne Methode GetStatus() hinzugefügt. Diese gibt, ähnlich einem HRESULT, einen von drei möglichen Integer-Werten zurück. Wobei -1 für "Objekt nicht gefunden" steht. 0 wird zurückgegeben wenn keine Änderungen vorliegen und 1 wenn das Objekt im Speicher geändert wurde. Ich habe die Methode mit dem internal Schlüsselwort versehen, da diese Methode nur intern von der SharedMemoryDependency-Klasse verwendet werden soll. Diese Methode macht nichts anderes, als den Wert aus dem angesprochenen zweiten SharedMemory Segment zu lesen und zurück zugeben.

internal int GetStatus()
{
    // Rückgabewert mit -1, Objekt nicht gefunden, initialisieren
    int value = -1;

    // MemoryStream zum aufnehmen des Status erzeugen
    MemoryStream keyStream = new MemoryStream();

    // das Status-Objekt in den Stream schreiben
    CopySharedMemoryToStream(keyStream, true);

    // einen Formatter zum deserialisieren erzeugen
    BinaryFormatter formatter = new BinaryFormatter();

    try
    {
        // Den Rückgabewert in einen Int-Wert parsen
        value = int.Parse(
            formatter.Deserialize(keyStream).ToString(),
            CultureInfo.InvariantCulture);
    }
    catch (SerializationException)
    {
        // Wahrscheinlich kein Objekt im Stream.
        // Fehler bei der Deserialisierung abfangen
        // um -1 zurückgeben zu können.
    }

    return value;
}

In der SharedMemoryDependency-Klasse wird genau diese Methode in einem TimerCallback-Delegaten verwendet um auf Änderungen im Speicher zu reagieren. Wenn der TimerCallback-Delegate den Wert 0 liest erfolgt keine Aktion. Liest der Delegat den Wert 1 liest, wird das geänderte Objekt aus dem SharedMemory Segment gelesen. Anschließend wird  ein neuer EventHandler-Delegate erzeugt. Diesem Delegaten wird eine Instanz der SharedMemoryNotificationEventArgs-Klasse mit dem Objekt aus dem SharedMemory Segment erzeugt und das Ereignis OnChanged ausgelöst sowie die Eigenschaft HasChanged auf true gesetzt. Somit wird dem Abonnenten mitgeteilt, das eine Änderung im SharedMemory Segment vorliegt. Sollte der Delegat den Wert -1, also Objekt nicht gefunden, lesen wird der Aufrufende Timer zerstört sowie der Wert der Eigenschaft Enabled auf false gesetzt und somit die Überwachung als beendet gekennzeichnet. Hier ein Listing des Delegaten:

private void CheckSegment(object state)
{
    // den aktuellen Wert aus dem Schlüssel-Segment lesen
    this.segment.Lock();
    int i = this.segment.GetStatus();
    this.segment.Unlock();

    // wenn Rückgabewert 1, würde das Objekt im
    // SharedMemory Segment geändert
    if (i == 1)
    {
        // das Objekt aus dem SharedMemory-Segment holen
        this.segment.Lock();
        object data = this.segment.GetObject();
        this.segment.Unlock();

        // EventHandler initialisieren
        EventHandler<SharedMemoryNotificationEventArgs> handler = this.OnChanged;
        if (handler != null)
        {
            // Event auslösen
            handler(
				this,
				new SharedMemoryNotificationEventArgs(data));
        }

        // Änderung signalisieren
        this.hasChanged = true;
    }
    else if (i == -1)
    {
        // Objekt nicht im Speicher vorhanden oder es kann nicht
        // darauf zugegriffen werden.
        // Den aufrufenden Timer zerstören
        Timer t = (Timer)state;
        t.Dispose();

        // Überwachung als beendet signalisieren
        this.watching = false;

        // Ausnahme auslösen
        throw new SharedMemoryException(
            "Auf das Schlüssel-Segment im Speicher kann nicht zugegriffen werden.");
    }
}

Die Umsetzung der Dependency-Klasse ist somit erfolgreich abgeschlossen. Für die Zukunft währe eine dynamische Anpassung des Abfrage-Intervalls, basierend auf den Intervallen zwischen den erkannten Änderungen im SharedMemory Segment, eine nützliche Änderung. Somit bräuchte sich um die Einstellung des Abfrage-Intervalls nicht mehr gekümmert zu werden. Falls gewünscht könnte der Abfrage-Intervall mit 0 initialisiert werden und um den Rest kümmert sich die SharedMemoryDependency-Klasse. Vorschläge und Kritiken, positive als auch negative, sind ausdrücklich erwünscht.

IpcTests_dependency

Wer bereits das Demo-Projekt aus dem vorherigen Artikel verwendet, sollte die Klassen NativeMethods und SharedMemory mit den Klassen aus diesem Demo ersetzen. Sollte jemand dieses Projekt oder Teile daraus in einem eigenen Projekt verwenden wollen, sollte er unbedingt die Klassen aus dem aktuellen Projekt verwenden. Dieses Demo und alle zukünftigen stehen unter der MIT-Lizenz. Die Modifizierung des Original-Codes von Richard Blewett ist mit ihm abgeklärt und wurde von ihm abgesegnet.

Simples Text Captcha mit Classic ASP

12.07.2008 16:47:00 | Peter Bucher

Da es anscheinend immer noch solche gibt, die noch mit Classic ASP arbeiten, habe ich mein Captcha Beispel (Simples Text Captcha mit ASP.NET) mit Classic ASP nachgebaut.

Eines vorneweg, es war eine echte Qual, sich wieder die untypisierte, etc... Scriptsprache "VBScript" und "Classic ASP" anzutun, aber wieder mal ein wenig "Back to the roots" schadet auch nicht :-)
Da es bei Eingaben leicht zu Laufzeit-Fehlern in Classic ASP kommt, habe ich anstelle einer Prüfung - die auch fehlschlagen kann, da bspw. IsNumeric() auch Fehlerhaft ist - eine einfache Fehlerbehandlung benutzt.

Zusätzlich verwende ich jetzt ein HiddenField, damit der Label-Text nach einem PostBack wiederhergestellt wird. In ASP.NET geschieht dies automatisch mit Hilfe des ViewStates.
Und damit die Eingabe immer wieder ins Formular-Feld geschrieben wird, reicht ein einfaches (value = Request.Form(<Name>)).

Hier der Code zum studieren (Und zum Vergleich gibts hier das ASP.NET Pendant):



<% Option Explicit %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "<A href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</A>">
<html xmlns="<A href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</A>">
<head>
<title>Simples Text Captcha mit Classic ASP</title>
</head>
<body>
<%
Dim s_status, _
       s_captcha, _
       s_hiddenCaptchaText

'// Wenn das Formular abgeschickt wurde
If Request.Form.Count > 0 Then
    '// Validieren und Status anzeigen
    If ValidateCaptcha() Then
        s_status = "Eingaben korrekt"
    Else
        s_status = "Eingaben fehlerhaft"
    End If
   
    '// Wiederherstellung des aktuellen Captcha-Textes (ViewState kommt hier in ASP.NET zum Zuge)
    s_hiddenCaptchaText = Request.Form("hiddenCaptchaText")
    s_captcha                  = s_hiddenCaptchaText
Else
    Dim random
    Dim num1, _
           num2

    num1 = CreateRandomNumber(1, 15)
    num2 = CreateRandomNumber(1, 15)
   
    s_captcha = CStr(num1) & " + " & CStr(num2) & " ="
    s_hiddenCaptchaText = s_captcha
   
    Session("captcha") = num1 + num2
End If

'// Erzeugt eine Zufalls-Nummer zwischen "i_min" und "i_max"
Private Function CreateRandomNumber(i_min, i_max)
    Call Randomize()
    CreateRandomNumber = CInt((i_max * Rnd) + i_min)
End Function

'// Validiert das Captcha und gibt bei Erfolg "true" zurück
Private Function ValidateCaptcha()
    Dim i_sessionValue, _
           i_inputValue
   
    On Error Resume Next
   
    i_sessionValue = CInt(Session("captcha"))
    i_inputValue     = CInt(Request.Form("txtCaptcha"))
   
    If Err.Number <> 0 Then
        ValidateCaptcha = False
    End If
   
    On Error GoTo 0
   
    ValidateCaptcha = (i_sessionValue = i_inputValue)
End Function
%>

<h1>Simple Captcha (Classic ASP)</h1>
<form id="captchaForm" name="captchaForm" method="post" action="default.asp">
    <label for="txtCaptcha"><%= s_captcha%></label>
    <input type="text" name="txtCaptcha" id="txtCaptcha" value="<%= Request.Form("txtCaptcha") %>" /><br />
    <input type="hidden" name="hiddenCaptchaText" value="<%= s_hiddenCaptchaText %>" />
    <input type="submit" name="btnSubmit" value="Abschicken" />
    <span><%= s_status%></span>
</form>
</body>
</html>

Benutzung:
Kompletter Code in eine *.asp Datei speichern, das "action"-Attribut des Formulars auf den Dateinamen anpassen und fertig.

HowTo: Eigene .NET Events definieren und mit Unit-Tests testen

12.07.2008 01:30:15 | Robert Mühsig

In dem heutigen HowTo geht es um das Erstellen von eigenen .NET Events samt dem dazu gehörigen testen mit einem Unit-Test.

Was ist ein Event aus “Anfängersicht”?
Jeder der (wahrscheinlich) in einer X-beliebigen IDE für eine X-beliebige Sprache bereits irgendein Button auf ein Fenster gezogen hat, wird als Resultat dann so einen ähnlichen Code sehen:

        private void button1_Click(object sender, EventArgs e)
        {

        }

Hier kann man nun ganz genau definieren, was passiert wenn der “button1″ geklickt wird.
Eigentlich eine tolle Geschichte :)

Was passiert denn technisch im Grunde genommen dahinter?
Events sind von der Idee her bereits sehr alt. Der Grundgedanke ist einfach nach dem “Hollywood-Prinzip“: Ruf nicht uns an - wir rufen dich an.
Im Code brauchen wir nicht ständig prüfen ob der Button geklickt wird oder nicht - der Button sagt uns, wann er geklickt wird.
Ohne jetzt die .NET Implementierung (also was im Framework passiert) näher untersucht zu haben, würde ich meinen, dass das Grundkonzept aus dem Observer-Pattern abgeleitet ist.

Was ist ein Event: Beispiel aus der realen Welt
Um  mal ein Beispiel aus der realen Welt aufzugreifen - auch wenn dies manchmal arg abstrakt ist ;)
Wenn jemand ein “Bild-Zeitungs-Abo” hat, fragt der normal-deutsche-Leser auch nicht ständig den Herrn Springer ob es nun eine neue Ausgabe gibt oder nicht - er bekommt sein Exemplar automatisch sobald es gedruckt wurde.

Einsatzgebiet von Events
Sobald man irgendwelche “Prozesse” oder “Abläufe” modelliert, könnte man im Prinzip auf Events setzen - ich persönlich bin erst vor kurzem auf das Thema gekommen. Das liegt vor allem daran, dass ich bisher meistens mich mit Web-Projekten beschäftigt habe. In der ASP.NET Welt sind meiner Meinung nach Events nicht so unglaublich nützlich. Der Grund liegt auf dem, wie HTTP funktioniert:

image 
Der Client macht eine Anfrage und der Server antwortet entsprechend. Rein theoretisch geht nun ein Ablauf los - der irgendwann beeendet ist - das “Ich-bin-fertig-mit-meiner-Aufgabe” könnte über ein Event mitgeteilt werden.
An dieser Stelle sollte der Server an den Client zurückschicken: Bin fertig. Allerdings geht dies nicht:
image
Im HTTP Umfeld kann der Server nicht einfach Daten zum Client schicken - der Client  (Browser) muss immer erst die Anfrage stellen. Dadurch muss man auf der Clientseite (über AJAX z.B.) ein Polling durchführen. Das führt natürlich dazu, dass Events leicht nutzlos werden.
Allerdings sind sie in sämtlichen Client-Anwendungen die nicht auf HTTP beruhen äußerst nützlich :)

Eigene Events definieren
Dan Wahlin hat ein sehr schönes Video erstellt, in dem die Grundgedanken sehr gut vermittelt werden.

Mein Beispiel:
Wir haben eine bestimmte Anwendung, welche jeweils einen Verbindungsstatus haben kann:

image

Diese “ConnectionStates” in der Anwendung werden von einem “ConnectionManager” verwaltet.
Jetzt wäre es ja schön, wenn uns unser “ConnectionManager” sofort informiert, wenn sich der Status ändert.
Dazu erstmal das grobe Konstrukt unserer Klasse:

public class ConnectionManager
    {
        private ConnectionStates _state;

        public ConnectionStates State
        {
            get
            {
                return _state;

            }
            set
            {
                _state = value;
            }
        }

        public ConnectionManager()
        {
            this.State = ConnectionStates.Disconnected;
        }
    }

Der Anfangsstatus ist erstmal auf “Disconnected” gestellt. Der Rest sollte soweit klar sein.
Jetzt kommen wir zur eigentlichen Eventdeklaration.

Schritt 1: Delegat definieren
Als ersten Schritt schreiben wir uns ein Delegat:

public delegate void StateChangedEventHandler(object sender, StateChangedEventArgs e);

Ein Delegat darf man als eine Art “Funktionszeiger” (im Video von Dan Wahlin wird es auch als Pipe zwischen Objekten verglichen) verstehen.

Schritt 2: StateChangeEventArgs definieren
In unserem Delegat definieren wir eine Methodendeklaration die so später auch der Clientcode sieht - als EventArgs definieren wir ebenfalls unsere eigene Klasse:

    public class StateChangedEventArgs : EventArgs
    {
        public ConnectionStates NewConnectionStates { get; set; }
    }

Hier definieren wir einfach, was wir in unseren EventArgs später haben wollen - uns interessiert natürlich am meisten, was nun der neue Status ist.

Schritt 3: Event definieren
Jetzt definieren wir unser Event - an diesem können sich später die entsprechenden Clients melden:

public event StateChangedEventHandler StateChanged;

Dieses Event ist vom Typ “StateChangedEventHandler” - welches unser vorher definiertes Delegat ist.

Zwischenschritt: Der Client
Allein durch diese Definition des Events und des Delegates ist es möglich, das sich “Clientcode” an das Event dran hängt:

class Program
    {
        static void Main(string[] args)
        {
            ConnectionManager man = new ConnectionManager();
            Console.WriteLine("Start state: " + man.State.ToString());
            man.StateChanged += new ConnectionManager.StateChangedEventHandler(man_OnStateChanged);
            man.State = ConnectionStates.Connecting;
            man.State = ConnectionStates.Connected;
            man.State = ConnectionStates.Disconnected;
            Console.ReadLine();
        }

        static void man_OnStateChanged(object sender, StateChangedEventArgs e)
        {
            Console.WriteLine("State changed...");
            Console.WriteLine("New state is: " + e.NewConnectionStates.ToString());
        }
    }

Die “man_OnStateChanged” Methode ist zwar definiert - allerdings rufen wir in unserem “ConnectionManager” nie das Event auf.

Schritt 4: Event in der Klasse aufrufen
In unserem Setter müssen wir natürlich das Event werfen - dies geschieht über eine weitere Methode in der Klasse. Hier mal der komplette Source Code:

public class ConnectionManager
    {
        private ConnectionStates _state;

        public ConnectionStates State
        {
            get
            {
                return _state;

            }
            set
            {
                _state = value;
                OnStateChanged();
            }
        }

        public delegate void StateChangedEventHandler(object sender, StateChangedEventArgs e);

        public event StateChangedEventHandler StateChanged;

        protected void OnStateChanged()
        {
            if (StateChanged != null)
            {
                StateChangedEventArgs args = new StateChangedEventArgs();
                args.NewConnectionStates = this.State;
                StateChanged(this, args);
            }
        }
        public ConnectionManager()
        {
            this.State = ConnectionStates.Disconnected;
        }
    }

Bei jedem setzen eines ConnectionStates wird die “OnStateChanged” Methode aufgerufen - diese ist nur intern erreichbar (”protected”) bzw. von vererbten Klassen.
Diese Methode prüft, ob das “StateChanged” Event irgendwelche Beobachter hat - if(StateChanged != null).
Falls irgendwer im Clientcode sich an das Event angehangen hat, wird das Event mit unseren EventArgs geworfen.

Es klingt komplizierter als es ist
Da ich ebenfalls “neu” darin bin, musste ich mich ebenfalls erst mal in das Einarbeiten. Um es mal in kurzen Worten zu formulieren (soweit mein Verständnis richtig ist):

  • Am “event” StateChanged können sich beliebige Clienten anmelden. Der Clientcode hat die selbe Methodensignatur (object Sender, EventArgsXYZ args) wie in dem “delegat” definiert.
  • Das “delegat” ist nur eine definierte Schnittstelle zwischen den Objekten. Hier wird die Methodensignatur von dem Clientcode bestimmt.
  • Die “EventArgs” sind eigene Datenklassen um entsprechende sinnvolle Daten zu übermitteln wenn das Event geworfen wurde
  • Die interne “OnStateChanged” Methode prüft ob irgendwas am “event” hängt - wenn ja, dann löse es aus und leite es (über das delegat) an die richtige Stelle im Clientcode.

Resultat
In der Clientanwendung (die Consolen-Applikation) wird jedesmal die Ausgabe gemacht, sobald sich der Status ändert. Ohne jedes mal eine extra Methode aufzurufen oder die Ausgabe an den Manager zu ketten:

image

Unit-Tests: Wie teste ich Events?
Events kann man über eine nette C# 2.0 Sache testen: ein anonyme delegate. Den Trick habe ich bei Phil Haack gefunden.

[TestMethod]
        public void ConnectionManager_Raise_StateChanged_Event()
        {
            ConnectionManager man = new ConnectionManager();
            Assert.AreEqual(ConnectionStates.Disconnected, man.State);

            bool eventRaised = false;

            man.StateChanged += delegate(object sender, StateChangedEventArgs args)
            {
                eventRaised = true;
            };
            man.State = ConnectionStates.Connecting;

            Assert.IsTrue(eventRaised);
        }

In diesm Test lege ich einen bool “eventRaised” an - sobald das Event geworfen wird, wird ein anonymes delegat aufgerufen (man spart sich hier die zweite Methode) und ich setzt einfach diesen boolean auf “true”.
Sehr einfach und genial um zu testen, ob das Event wie gehofft auch geworfen wird :)

[Download Source Code]

ShareThis

DDD – Allgegenwärtige / Universelle Sprache

11.07.2008 19:49:00 | Sebastian Jancke

Die zentrale Technik im Domain Driven Design ist die “Allgegenwärtige / Universelle Sprache”, engl. ubiquitous language. Um zu erklären, wobei es darum geht, möchte ich zunächst weg von der technischen Sprache. Wir alle kennen das Phänomen, das manche Filme und Bücher in der originalen Sprache einfach aussagekräftiger und präziser sind. Übersetzungen gehen oft soweit, das Film- und Buch-Titel einfach die Aussage des originalen Titels kaum noch widerspiegeln. Übersetzungen erzeugen schlicht kleine Fehler und Abweichungen, die in der Summe (bei schlechten Übersetzungen) einfach nicht mehr die selbe Aussage treffen.

Image is subject to creative commons license. Created by: http://flickr.com/photos/nofrills/ Übertragen wir diese Film-Metapher auf die Software Entwicklung. In manchen starren Prozessen gibt es Entwickler, Architekten und Analysten. Letztere kommunizieren direkt mit dem Kunden, übersetzten die Anforderungen des Kunden in die Sprache der Architekten und Entwickler. Meist haben Entwickler und Architekten kaum noch die selbe Sicht und Sprache auf die Domäne wie der Kunde. Wie wir bereits gesehen haben, sind solche Übersetzungen nicht trivial und fehlerfrei. Im Extremfall kommunizieren die Team-Mitglieder in einer anderen Sprache und damit mit einem anderen Modell, als im Quelltext des Systems abgebildet ist. Sicherlich kennt fast jeder solche Projekte und Situationen.

Evans schlägt hier einen Ansatz vor, der zunächst manchem radikal erscheinen mag. Aber wie bei allen neuen Techniken ist ein wenig Dogmatik der Verbreitung sicherlich dienlich (siehe zum Beispiel TDD). Evans schlägt eine vereinheitlichte Sprache zwischen Kunden und Entwicklern vor. Diese allgegenwärtige, universelle Sprache bildet das Modell der Domäne ab. Diese Sprache manifestiert sich in der Kommunikation zwischen Team und Kunden, in Dokumenten und natürlich auch im Quelltext. Sie ist das Kernstück, das Rückgrat des Modells und muss unbedingt von allen Mitgliedern des Team kommuniziert werden.

Interessant zu diskutieren wäre hierbei das gebräuchliche Vorgehen, Quelltext in englischer Sprache zu halten während wir fast alle auf Deutsch kommunizieren. Denn dies ist bei deutschsprachigen Kunden schließlich auch eine Übersetzung. Auf der anderen Seite gehört es zum “guten Ton”, Quelltext auf Englisch zu schreiben – wohl auch weil die deutsche Sprache teilweise einfach mehr Wörter zum Ausdruck braucht.

Image is subject to creative commons license. Created by: http://flickr.com/photos/cleverclaire1983/ Die Wahl der Dokumentation ist unbedingt dem Kontext anzupassen, denn die allgegenwärtige Sprache wird sich über einige Zeit weiterentwickeln und ständig Verändern, bis sie sich in ihrem Kern stabilisiert hat. Für manche Projekte machen hier sicher starre UML-Diagramme und Anforderungsdokumente Sinn, wenn das Projekt nicht mit dauernden Änderungen in der Domäne umgehen muss. Für Domänen mit häufigen, ständigen Änderungen empfehlen sich dann wohl eher temporäre Diagramme (Skizzen). Evans gibt hierbei zu bedenken, das gerade Diagramme eher nur Ausschnitte des Modells reflektieren sollten. Gerade UML-Diagramme tendieren dazu, sich extrem zu vermehren oder aber allumfassend zu werden. Solche Diagramme sind sicherlich kaum zur Kommunikation geeignet, weil es viel zu lange dauert sie zu lesen und zu ändern. Gerade in der Anfangsphase der Modellierung ist das Modell in der Regel noch nicht Stabil genug. Hier sind Skizzen und Ausschnitte unter Umständen viel schneller von Hand zu erstellen als jedes andere Diagramm. Zu bedenken ist auch, dass je mehr komplexe Dokumentation des Modells es gibt, desto mehr Artefakte müssen auch synchron mit der Entwicklung der Sprache gehalten werden. Aus der eigenen Erfahrung kann ich berichten, dass in manchen Projekten einfache digitale Mindmaps ausreichen, um Änderungen an der Sprache (und damit auch an der Domäne) zu dokumentieren. Dies hat unter Umständen aber den Nachteil, dass alle Dokumente fast nur inkrementelle Ausschnitte sind. Auf der anderen Seite sind solche Mindmaps schnell geschrieben, strukturiert und auch schnell verstanden.

Egal welcher Ansatz gewählt wurde: Die Sprache und damit auch das Modell der Domäne manifestiert sich vor allem in der Kommunikation zwischen Team und Kunde und vor allem auch innerhalb des Teams. Dies bedeutet automatisch, dass jedes Konzept in der Sprache seine Entsprechung im Quelltext finden muss. Alle Änderungen in der Sprache sind somit auch eine Änderung am Modell. Somit muss dann auch der Quelltext die Änderungen mittragen. Andersherum kann es auch Konzepte geben, die bisher nur implizit vorhanden – und vielleicht bisher kaum verstanden - sind. Dies manifestiert sich vielleicht erst in der Entwicklung des Systems. Solche impliziten Konzepte müssen dann explizit gemacht werden und auch Eingang in die Sprache finden. Erst dann sind sie schließlich mit den Experten in der Domäne diskutierbar.

Unsere allgegenwärtige, universelle Sprache ist während der Modellierung natürlich Änderungen unterworfen - auch wenn wir Big-Design-Up-Front arbeiten. Dann gibt es diese Änderungen hoffentlich nur während der Modellierungs-Phase (was ich aus meiner bisherigen Erfahrung eher selten glaube). Dies kann dazu führen, dass dass Modell der Domäne nicht mehr mit den neuen Anforderungen skaliert. Dann ist es Zeit, Teile des Modells zu entfernen und neu zu beginnen. Dies erreicht man im Prinzip nur durch Experimenten mit dem Modell und Diskussion mit den Experten der Domäne. Beispiele für solche Vorgänge sind in den Büchern von Eric Evans und Jimmy Nilsson in vielen Beispielen zu finden.

Einen Vergleich, den ich sehr interessant finde, gibt es zwischen der allgegenwärtigen Sprache des DDD und der System-Metapher des eXtreme Programming. Die Metapher wurde in den ursprünglichen XP-Büchern kaum behandelt (nur auf wenigen Seiten) und ist daher vielleicht eine der kaum praktizierten Methoden (jedenfalls hört  und liest man eher selten davon). Man kann die allgegenwärtige Sprache im Domain Driven Design als ein Schlüssel zur Erstellung einer solchen System-Metapher auffassen.

Abschließend ein Ausblick:

In den folgenden Teilen der Serie stelle ich den von Evans eingeführten Sprach-Elemente vor, um aus der Kommunikation mit Experten der Domäne dann ein Modell zu strukturieren. Weitere Teile werden Techniken enthalten um Konzepte im Quelltext expliziter zu machen, das Modell reichhaltiger zu gestalten, seine Anstrengungen in der Modellierung zu fokussieren und ein Modell gegen unerwünschte Einflüsse zu schützen sowie mit anderen Modellen zu integrieren.

Zum Schluss: ein gutes Beispiel für schwierige Übersetzungen ist schon der Titel dieses Beitrags: “ubiquitous langauge” lässt sich übersetzten, verliert dann aber schnell an Bedeutung – jedenfalls in meinem sprachlichem Empfinden.

DDD Serie:

DDD – Eine Einführung

11.07.2008 19:18:00 | Sebastian Jancke

Dies ist der Beginn einer Serie zum Thema Domain Driven Design (DDD). Ich werde versuchen, die Serie mit weiteren Beiträgen zu füllen, während ich an einem Vortrag zum Thema arbeite.

Domain Driven Design ist der Titel eines Buches von Eric Evans. Das Buch selbst ist nicht mehr “ganz neu” – trotzdem gab es (gerade auch in den Java und ALT.NET Gemeinschaften) einigen “Hype” darum. Dabei gibt es eigentlich nicht genau das DDD. Zunächst ist DDD eine Kombination aus “Model Driven Design” (nicht zu verwechseln mit MDA – Model Driven Architecture) und Prozessen rund um Kunden-Kommunikation, Integration und Modularisierung verschiedener Domänen, etc..

Nun stellt sich natürlich die Frage, wann DDD eingesetzt werden sollte. Zunächst ist die Ansatz der allgegenwärtigen Sprache sicherlich universell nutzbar. Um den vollen Nutzen zu erfahren ist aber sicherlich die Kopplung mit Model Driven Design empfehlenswert. Ausgangspunkt ist das Domain Model Pattern von Martin Fowler. Hierbei sollen die kompletten Fähigkeiten unserer OOP-Sprachen genutzt werden, um die Komplexität der Domäne in ein reichhaltiges Objektmodell zu übersetzten.

Damit dürfte klar sein, dass Model Driven Design, also OO-Modelle der Domäne, sich vor allem in OLTP-Szenarios einsetzten lassen. Derzeit ist Objekt-Orientierung einer der stärksten Ansätze, die wir kennen, um hohe Modularisierung und Reduzierung der Komplexität zu erreichen.

Dies bedeutet aber auch, dass OLAP-Szenarios ungeeignet für den Einsatz eines reichhaltigen Objektmodells sind – denn hierbei geht es primär um die Analyse von Daten und weniger um komplexe Prozesse und Regeln innerhalb von Transaktionen. Die Frage nach Ad-hoc Reporting wurde auf der DDD-Mailingliste schon mehrfach gestellt und auch beantwortet – mittlerweile gibt es dazu auch einen Artikel und eine Diskussions-Zusammenfassung auf der Webseite zu DDD. Ich möchte hier nicht direkt im Detail darauf eingehen - dies werde ich zu einem späteren Zeitpunkt noch tun. Zusammenfassend möchte ich aber sagen, das solche Objektmodelle und damit verbundene Datenbank-Schemata eigentlich nicht zum performanten Ad-hoc Reporting geeignet sind. Alternativen wären flache, spezielle Datenbank-Sichten oder eine Art Data-Warehouse-Lösung.

Ziel des Model Driven Design ist also die Erstellung eines reichhaltigen Objektmodells, das wir mit völliger Freiheit unter Ausnutzung aller Sprach-Features erstellen. Zu starke Kopplung an die Infrastruktur (etwa Basisklassen mit Transaktions- und Datenbank-Logik) sind hier hinderlich. Deshalb ist der Begriff “Persistence Ignorance” eng mit der Erstellung solcher Objektmodelle verbunden. Objekte ohne Einschränkung durch Infrastruktur oder Runtime-Container (wie zB EJB 2.0 - Java , CSLA - dotnet) werden auch POCOs genannt – Plain Old CLR Objects. Ich würde sogar noch ein Stück weiter gehen und von Infrastruktur-Ignoranz sprechen, da Persistenz nur ein Aspekt der Infrastruktur ist.

Natürlich bedeutet dies auch, dass wir gewisse Anforderungen an die Architektur solcher Systeme haben, auf der anderen Seite aber auch eine Menge gewinnen. Zunächst möchte ich auf die Gewinne eingehen. Solche Objektmodelle eigenen sich perfekt um Komplexität zu verstecken und Änderung zu handhaben. Die Arbeit mit solchen Modellen führt zu ausdrucksstarken Kombinationen aus bereits bestehenden ausdrucksstarken Teilen. Damit gewinnen wir als Lesbarkeit und auch Wartbarkeit. Objektmodelle ohne Abhängigkeit von der Infrastruktur bedeuten natürlich auch einfache Testbarkeit. Somit ist es möglich, die gesamte komplexe Logik in Objektmodelle mit automatisierten Tests ständig zu überprüfen. Unsere Architektur wird hier also testbar und wir werden nicht gehindert Tests zu entwickeln oder gar Test-first (TDD) zu arbeiten. Solche Objektmodelle erleichtern die Arbeit mit automatisierten Tests geradezu.

Es gibt aber auch einige Anforderungen an unsere Architekturen, um solche reichhaltigen Objektmodelle im Sinne von DDD zu entwickeln. Diese sind nicht unbedingt negativ (haben sogar positiven Einfluss auf das gesamte System), sind teilweise aber fast Voraussetzung. Zunächst einmal lassen sich Infrastruktur-Ignorante Objektmodelle im Prinzip nur entwickeln, wenn wir das Dependency Inversion Principle anwenden. Dies führt sofort zur Notwendigkeit eines Dependency-Injection-Containers zur Konfiguration der Abhängigkeiten. Natürlich ließe sich das DIP auch anders realisieren, pragmatisch gesehen nehmen uns solche Container (Castle Windsor, Spring.NET, StructureMap, Ninject) aber sehr viel Arbeit ab. Der Einsatz eines solchen IoC/DI-Containers hat darüber hinaus einen positiven Effekt auf die gesamte Anwendung: lose Kopplung ist schließlich eine Eigenschaft, die man sich generell (neben hoher Kohäsion) für seine Module wünscht.

Soll unser Objektmodell persistiert werden, brauchen wir auch einen starken, flexiblen O/R-Mapper. In letzter Zeit gab es viel Rumoren um das Entity Framework – um es kurz zu machen: derzeit sieht sowohl das EF-Team als auch die ALT.NET Community das EF nicht als eine Option an, wenn man DDD nutzt. Derzeit stärkstes Framework auf dem Markt ist damit NHibernate, eine ehemalige Portierung des Java-Frameworks Hibernate. NHibernate ist komplett in das dotnet-Ökosystem integriert und weit verbreitet.

Die Entwicklung reichhaltiger Objektmodelle erfordert zudem generell eine Leistungsfähige Entwickler-Struktur, denn ohne gute, OOP-erfahrene Entwickler ist es schwierig ein reichhaltiges Objektmodell zu erstellen. Viele Entwickler sehen sich zwar als OOP-erfahren, doch ist es noch ein großer Unterschied eine OOP-Sprache wie C# einzusetzen oder aber gute, leistungsfähige Objektmodelle zu entwickeln. Dies merkt eigentlich jeder, der bisher noch nie mit aller Kraft versucht hat, ein Modell noch reichhaltiger zu machen. Die ersten Versuche scheitern wohl in der Regel (und sind es auch bei mir). Das eingestehen und sehen der Fehler ist hier der Schlüssel um sich weiterzuentwickeln. Viele open-source Projekte nutzen reichhaltige Objektmodelle und sind optimal zum Lernen.

Zum Schluss noch einige Worte zum Aufwand. Ein Objektmodell zur Repräsentation der Domäne zu erstellen ist harte Arbeit und aufwendig – dem gegenüber steht aber die Leichtigkeit mit der später Komplexität gehandhabt werden kann (wenn das Modell gut genug ist). Als Alternativen gäbe es noch die Arbeit mit Transaction Scripts (Fowler) oder dem Table Module Pattern (Fowler). Letzteres erfreut sich gerade bei .NET-Entwicklern großer Beliebtheit wegen der guten IDE-Unterstützung in Visual Studio, dort bekannt als DataSets. Für einen genauen Vergleich möchte ich auf Martin Fowler’s Buch “Patterns of Enterprise Application Architecture” verweisen. Es sei aber gesagt, dass gerade das Table Module Pattern wahrscheinlich am wenigsten mit steigender Komplexität der Domäne skaliert, gefolgt von Transaction Scripts. Beide haben vor allem das Problem von Copy&Paste-Smells und Dopplung von Logik. Nach derzeitigem Stand ist ein objekt-orientieres Modell das einzige, das wirklich besser mit wachsender Komplexität skaliert. Problematisch ist allerdings, dass man Komplexität schlecht messen kann und keiner genau weiß, wo die Schwellwerte liegen. Letzten Endes ist hier die Erfahrung und Experimente / Prototypen gefragt.

Das Eis taut für Open XML

10.07.2008 13:53:58 | Jens Häupel

Vor einiger Zeit gab es wieder etwas Wirbel um die Standardisierung von Open XML, legte doch die ISO den Prozeß nach Einsprüchen einiger Miglieder vorerst auf Eis.

Die Gründe für die Einsprüche von Indien, Südafrika, Venezuela und Brasilien gegen die Zertifizierung von Open XML konnten von den Generalsekretären der ISO bzw. IEC allerdings nicht bestätigt werden. Sie haben keine Regelverstöße im Zertifizierungsprozeß ausmachen können. Somit wird der Standard DIS 29500 voraussichtlich ratifiziert und dann natürlich auch veröffentlicht.

Ein weiterer Aufschrei machte vor kurzem ebenfalls die Runde: Microsoft's amerikanischer National Technology Officer, Stuart McKee, wurde mit der Aussage zitiert "ODF habe den Wettkampf der Standards klar gewonnen..."

O-Text:

"ODF has clearly won," said Stuart McKee, referring to Microsoft's recent announcement that it would begin natively supporting ODF in Office next year and join the technical committee overseeing the next version of the format.

Interessanterweise wurde bei der Zitierung einer Nachricht von der anderen die eigentliche Aussage immer ein bißchen weiter verändert. Stuart McKee's Aussage zielte darauf, daß ODF nun klar an Verbreitung gewinnt, da mit der Bereitstellung entsprechender Konverter im nächsten Office 2007 Service Pack Anfang 2009 dann Millionen Microsoft Office Nutzer auch ODF zur Verfügung steht.

Microsoft wird natürlich nach wie vor Open XML als das native Format von Office 2007 einsetzen und ODF gleichzeitig als Alternative anbieten auch für Fälle, wo das Format der Schlüsselfaktor für den Einsatz eines bestimmten Office Paketes wird. ODF ist allerdings nach wie vor nicht in der Lage, alle Funktionalität eines Microsoft Office Dokuments abzubilden. Das ist aber eine Begrenzung der derzeitigen Implementation und mag in einer der nächsten Versionen anders sein. Auch Standards unterliegen Änderungen, durch die sie gewanchsenen Anforderungen angepaßt werden.

HowTo: 3-Tier / 3-Schichten Architektur

09.07.2008 22:38:20 | Robert Mühsig

Eine 3-schichtige Architektur ist eigentlich ein “Klassiker” in der Softwareentwicklung. Da allerdings das Thema sehr weitläufig ist und Anfänger (und unbelehrbare Entwickler) aus Mangel an Zeit, Lust oder Erfahrung zurückschrecken gibt es genügend Beispiele wo einfach darauf verzichtet wurde.

Um den Grundgedanken zu vermitteln und um zu zeigen, dass es eigentlich sehr einfach ist, sowas am Projektanfang zu implementieren, schreibe ich diesen Artikel

Was für “Schichten” und warum 3?
Fast jede Software greift auf Daten zurück - sei es XML, ein Webservice, eine Datenbank, eine Textdatei oder ein X-beliebiges anderes System.
Diese Daten werden irgendwie verarbeitet - sei es eine mathematische Funktion, eine Validierung oder eine bestimmte Filter und Suchfunktion.
Damit das Ergebnis auch irgendwo angezeigt wird (bzw. die Eingaben entgegen genommen werden) gibt es in den meisten Fällen auch ein Frontend, sei es eine Consolen-Applikation, eine Website oder irgend etwas anderes.

Jetzt wären wir bei den 3-Schichten angekommen:
 image

“Nur 3? Ich hab mehr!”
Natürlich kann man unzählige Schichten noch dazwischen schieben. Ein Beispiel ist z.B. die Software Factory von Microsoft. Da gibt es noch etliche Mappings zwischen den Data-Access-Schichten bis hin zu den Service-Schichten. Siehe auch den Wiki-Artikel zu den Schichtenmodellen.

“Ich frage nur Daten ab - ich brauch nur 2 Schichten.”
Über SQL etc. kann man natürlich auch Filtern, Sortieren etc. - da könnte man auch die “Business” Schicht in Frage stellen. Aus meiner Erfahrung sollte man das allerdings lieber nicht machen - später können noch irgendwelche Anforderung dazukommen, die nix im Data Access Layer zu suchen haben. Bis es soweit ist, könnte die Business-Schicht die Daten einfach nur “durchreichen”.

Beispielapplikation:
image

Data-Access Layer: “ThreeTier.Data”
Business Layer: “ThreeTier.Service”
Presentation Layer: “ThreeTier.ConsoleApp”
+ Unit-Tests: “ThreeTier.Tests”

Hier haben wir eine recht einfache Beispielapplikation - das ganze noch mit ein paar kleinen Unit-Tests bestückt (Einführung in Unit-Tests hier).

Die Architektur sieht man z.B. auch recht gut in Rob Conerys Storefront Projekt.

Schichten im Detail: ThreeTier.Data
image 
Hier definiere ich erst mal meine Objekte, welche ich im System nutze - simple POCOs.
Zugegeben, man kann sich darüber streiten ob man sein “Model” tatsächlich mit in dem Data-Projekt haben möchte. Da allerdings alles meistens mit irgendwelchen Daten zusammenhängt, passt das schon.
Wir haben hier nur die User Klasse:

image 
Im Ordern “DataAccess” liegt unsere Schnittstellen (Einführung zu Schnittstellen) zu den Datenquellen.
In diesem Fall haben wir nur die Schnittstelle “IUserRepository” (Repository zu dt. sowas wie Lager, Speicherort etc.) - dort definieren wir, welche Operationen ich generell auf eine X-beliebige Datenquelle ich ausführen möchte:
image
Das “DemoUserRepository” ist die konkrete Implementierung dieser Schnittstelle. Da ich keine DB oder ähnliches wollte, werden hier statische Daten zurückgegeben.

Welchen Vorteil bringt mir jetzt das Interface?
Das Interface könnte man hier in Frage stellen, allerdings erlaubt es später recht einfach die Datenquelle zu wechseln - weil alles auf der Schnittstelle beruht.
Da man im Regelfall mit einer DB etc. arbeitet möchte man z.B. in Unit-Tests nicht unbedingt die Datenbank fluten, sondern kann sich hier statische Testdaten zurückgegeben lassen. Einfach durch die Schnittstelle.
So könnte man auch leichter von einem “Showcase” zu einer echten Implementierung umschwenken.

Da ich in meinem Beispiel aber nur statische Daten zurückgebe, habe ich im Unit-Tests genau diese getestet.

Schichten im Detail: ThreeTier.Service

image
Im Service haben wir nach dem gleichen Prinzip auch eine Schnittstelle für unseren “UserService” erstellt.

image

In unserem UserService gibt es einmal eine Login-Methode und eine Methode, welche (ganz im Sinne von Social Networking) die Freunde von einem User zurück gibt. Hierbei habe ich zudem auch nur statische Daten genommen. Das ganze basiert allerdings auf dem “UserRepository”.

Schichten im Detail: ThreeTier.ConsoleApp
image

Mal wieder ein Konsolenprogramm - zwar ist das keine schöne Oberfläche, aber für das Beispiel soll es genügen:

        static void Main(string[] args)
        {
            Console.WriteLine("Great Social Community System - Please Login...");
            Console.Write("Name: ");
            string loginname = Console.ReadLine();
            Console.Write("PW: ");
            string password = Console.ReadLine();

            IUserService srv = new DemoUserService();

            if (srv.Login(loginname, password))
            {
                Console.WriteLine("Hello: " + loginname);
                Console.WriteLine("Your demo friend collection in the system: ");
                List<User> friends = srv.GetFriendsFromUser(loginname).ToList();

                foreach (User friend in friends)
                {
                    Console.WriteLine(" + " + friend.Login + " - Id: " + friend.Id);
                }
            }
            else
            {
                Console.WriteLine("Login failed");
            }

            Console.ReadLine();
        }

Ausgabe:

image 

Extras: Unit-Tests

Um ein gutes Beispiel zu geben, habe ich sogar 6 Unit-Tests geschrieben. Das Frontend hab ich allerdings nicht getestet ;)
image

Code-Coverage: 97% (Data + Service)
image

Resultat:

Durch die 3-Schichtige Architektur ist es später leichter Möglich neue Features einzubauen und die Applikation zu Warten. Im Team macht sich das auch recht gut, da man dadurch eine bessere Teamaufteilung machen kann.

[ Download Source Code ]

ShareThis

Things to note when upgrading to BlogEngine.NET 1.4

09.07.2008 12:07:08 | Andre Loker

Several days ago I upgraded the blog software (BlogEngine.NET) from version 1.3.1 to 1.4. Here are some changes that sneaked in to the new version:

  • The RSS feed generator creates a different value for the <author> element. In version 1.3.1 it was simply the blog owner's name (here: Andre Loker), in version 1.4 the name is composed of a "anti-spammified" version of the blog owner's email address and his name (here:  mail.nospam@nospam.andreloker.de <Andre Loker>). I don't like it, so I reverted it to the original behaviour:
    • Locate BlogEngine.Core\SyndicationGenerator.cs (in the source package)
    • Comment out line 554 (prefixed by "Was" in the picture)
    • Add the line prefixed by "Now" in the picture
      image
  • When you receive a mail that was sent via the contact form, the "From" field is filled with the e-mail address and name of the blog owner, a "Sender" field is added that contains the mail author's name and e-mail address. In my mail client, all mails coming from the contact form now have "Andre Loker" in the sender column. Again, I reverted this to the original behaviour:
    • Locate contact.aspx.cs
    • Comment out the line prefixed by "Was" in the picture
    • Add the line prefixed by "Became" in the picture
       image
  • To be continued...

Note: Remember to apply the Memory leak fix for version 1.3, 1.3.1 and 1.4

Techno trouble

09.07.2008 10:43:19 | Andre Loker

I encountered some technical problems yesterday and today. Sorry for that!

  • The SMTP server has been unavailable since today 3 o'clock
  • The RSS feed has been unavailable since I updated to version 1.4 of BlogEngine.NET (I forgot to upload a file...)

So if you tried to send me an email in the last 24 hours, especially regarding ReSharper licenses, I ask you to send it again if I haven't answered yet.

By the way: if you are sending me a mail and expecting a reply, please make sure you provide a working(!) e-mail address. I have several requests for ReSharper evaluation license that I can't answer, simply because my response won't make it back to the sender.

Registrierungsformulare optimal gestalten

09.07.2008 08:34:24 | Jürgen Gutsch

Wer sich dafür interessiert, wie Registrierungsformulare benutzergerecht gestaltet werden können. Sollte sich zwei Artikel, mit dem Titel "Web Form Design Patterns: Sign-Up Forms" auf Smashing Magazine anschauen:

http://www.smashingmagazine.com/2008/07/04/web-form-design-patterns-sign-up-forms/
http://www.smashingmagazine.com/2008/07/08/web-form-design-patterns-sign-up-forms-part-2/

Anhand von Beispielen und Grafiken wird erklärt, wie ein Registrierungsformular optimal aufgebaut sein kann.

Registrierungsformulare optimal gestalten

09.07.2008 08:34:24 | Jürgen Gutsch

Wer sich dafür interessiert, wie Registrierungsformulare benutzergerecht gestaltet werden können. Sollte sich zwei Artikel, mit dem Titel "Web Form Design Patterns: Sign-Up Forms" auf Smashing Magazine anschauen:

http://www.smashingmagazine.com/2008/07/04/web-form-design-patterns-sign-up-forms/
http://www.smashingmagazine.com/2008/07/08/web-form-design-patterns-sign-up-forms-part-2/

Anhand von Beispielen und Grafiken wird erklärt, wie ein Registrierungsformular optimal aufgebaut sein kann.

The Web Standards Curriculum

09.07.2008 08:26:16 | Jürgen Gutsch

Opera hat, unter der CC Lizenz, unter dem Namen "Web Standards Curriculum", Anleitungen zur standardkonformen Webentwicklung mit HTML und CSS veröffentlicht. 23 Artikel sind bereits Online und etwa 30 weitere sollen bis ende September folgen.

Hier geht es es zur Einführung und dem Inhaltsverzeichnis:
http://dev.opera.com/articles/view/1-introduction-to-the-web-standards-cur/

Ich bin also die nächsten zwei Wochen erst mal mit Tutorials versorgt :-) Hoffen wir mal das die Artikel nicht zu "Opera"-lastig sind...

Via: http://www.webkrauts.de/.../opera-unterrichtet-webstandards/

The Web Standards Curriculum

09.07.2008 08:26:16 | Jürgen Gutsch

Opera hat, unter der CC Lizenz, unter dem Namen "Web Standards Curriculum", Anleitungen zur standardkonformen Webentwicklung mit HTML und CSS veröffentlicht. 23 Artikel sind bereits Online und etwa 30 weitere sollen bis ende September folgen.

Hier geht es es zur Einführung und dem Inhaltsverzeichnis:
http://dev.opera.com/articles/view/1-introduction-to-the-web-standards-cur/

Ich bin also die nächsten zwei Wochen erst mal mit Tutorials versorgt :-) Hoffen wir mal das die Artikel nicht zu "Opera"-lastig sind...

Via: http://www.webkrauts.de/.../opera-unterrichtet-webstandards/

Sandcastle Source released

09.07.2008 03:27:27 | Rainer Schuster

Da ist mir doch letzte Woche glatt eine wichtige News durch die Lappen gegangen. Nach einer hitzigen Diskussion und dem Aufschrei, Microsoft würde auf seiner eigenen OpenSource-Plattform ein ClosedSource Projekt hosten wurde Sandcastle erst einmal entfernt, wie ich bereits berichtet habe.

Es wurde daraufhin diskutiert, was geschehen sollte.

  1. eine Relaunch auf Codeplex mit Sourcecode
  2. ab damit in die Codegallery (http://code.msdn.microsoft.com)

 

Da sich Microsoft ja mit diversen OpenSource Aktivitäten und milliardemschwerem Aufwand bemüht, das nachzuholen, was Jahre lang verschlafen wurde, war hier nur eine Option denkbar. das Projekt ist wieder unter der alten Adresse mit Sourcecode beziehbar.

Vielen Dank Microsoft und dem Manager Anand Raman. Wer sich darüber hinaus was Sandcastle angeht immer auf dem laufenden halten will, kann direkt auf der Forumseite vorbei schauen.

HowTo: Generische Extensions

08.07.2008 21:07:37 | Robert Mühsig

C# 3.0 bringt ein nettes Feature mit: Extensions.

Generell sind die recht einfach, allerdings sind die meisten Beispiele ohne Generics gemacht.

Meine Problemsituation:
In einem Projekt waren einige Klasse von List<…> abgeleitet:

    public class MyList : List<MyObject>
    {
        ...
    }

Jede dieser “Listenklassen” hatte eine kleine Methode, welche diese Liste durchgeht und eine Aktion auslöst.

Ganz nach dem Prinzip: Keep it DRY
Don´t repeat yourself - daher müssen diese eigentlich gleichen Methoden weg und in eine Extension Methode. Da der Syntax von den generischen Extensions mir etwas Zeit geraubt hat, stell ich ihn mal der Allgemeinheit offen.

In unserem Beispiel:
Wir wollen eine kleine Extension schreiben, welche mehrere Elemente an eine ICollection anfügt - kann man auch über AddRange lösen, aber z.B. hat die ObservableCollection das nicht - daher nehmen wir einfach mal dieses Beispiel.

Source Code:
Main:

class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int>();
            intList.Add(1);
            intList.Add(2);
            intList.Add(3);

            List<int> newIntList = new List<int>();
            newIntList.Add(4);
            newIntList.Add(5);

            intList.Add(newIntList);

            foreach (int myInt in intList)
            {
                Console.WriteLine(myInt); // Should be 1,2,3,4,5
            }

            Console.ReadLine();
        }
    }

Extension:

    public static class Extensions
    {
        public static ICollection<T> Add<T>(this ICollection<T> src, ICollection<T> addingElements)
        {
            foreach (T element in addingElements)
            {
                src.Add(element);
            }
            return src;
        }
    }

Resultat: Viele Ts und eine kleine generische Extension.

[ Download Source Code ]

ShareThis

Did you know that...

08.07.2008 19:24:57 | Andre Loker

... you can annotate an attribute class with an attribute of that very type?

 [ThisIsCool]
 public class ThisIsCoolAttribute : System.Attribute {
 }

Admittedly I did not.

Objekt-Modellierung: Nomen? Verben? Beides? Keins?

08.07.2008 17:33:00 | Sebastian Jancke

Die allermeisten lernen wohl OOP – bis heute – in seiner einfachsten Form zunächst als “Finding the nouns”. Die Nomen finden – und dann daraus Objekte machen. Fehlen noch Aktionen, um daraus Methoden für ein Objekt zu erstellen – wie gut das es ja noch Verben gibt.

Vielen Entwicklern dürfte klar sein, dass diese Schema zu simpel ist und höchstens für die ersten OOP-Beispiele ausreicht. Fortgeschrittenere “Richtlinien” und Möglichkeiten zur Strukturierung bieten unter anderem der DDD-Prozess, und das Entity-Controller-Boundary Modell.

Trotzdem habe ich schon zu oft Entwickler gesehen, die krampfhaft versuchen, ein Objekt nach Nomen zu benennen, obwohl ein Verb viel aussagekräftiger wäre. Das Resultat sind die allerseits beliebten Konstrukte:

AnotherCarFinder.FindCar(myCarId);

Neben der Dopplung – die sich sehr holprig liest – ist das ganze auch kaum aussagekräftig. Deshalb wäre mein Ratschlag: Weg vom OO-Denken in Nomen, hin zu mehr Flexibilität und Kreativität.


Das obige Beispiel sieht so sicherlich viel lesbarer aus:

FindAnotherCar.By(myCarId)

Dies ist sicherlich noch nicht das non-plus-ultra und auch nicht das Ende der Möglichkeiten. Deshalb möchte ich alle Leser auffordern, mir ihre Beispiele und Ideen doch vielleicht zukommen zu lassen. Mich interessiert brennend, welche Techniken andere (unter anderem) nutzen, um die Lesbarkeit zu erhöhen.


DocProject - MAML Editor kurz vor dem Release

08.07.2008 14:14:37 | Rainer Schuster

In einer E-Mail von Dave Saxton habe ich erfahren, das die Preview des Editors kurz vor dem Release steht. Er hat die Zero-Bug Grenze erreicht. Die Konvertierung von MAML nach XAML und umgekehrt ist fertig. Die Commandbuttons müssen noch mit den entsprechenden Funktionen verknüpft und einige Styles angepasst werden. Danach steht einem Preview-Release nichts mehr im Wege. Die E-Mail hat mich schon vergangene Woche erreicht. Er hatte mir mitgeteilt über das Wochenende daran zu arbeiten und vielleicht heute die Version frei zu geben. Wenn wir Glück haben bekomme ich gegen heute Nachmittag/Abend dann die Preview unter meine Finger um sie zu testen.

Wahlkampf mit Silverlight mal etwas anders

08.07.2008 10:40:42 | Oliver Scheer

Ein sehr cooles Beispiel für die Verwendung von Silverlight Deep Zoom.

image image

Mehr unter: http://www.deepzoomobama.com/

NDepend: code metrics at your service

08.07.2008 10:07:14 | Andre Loker

If you ever wrote code for a non-trivial project chances are that from time to time you stop an think: "I don't know, but I have the feeling that the code is not really clean/too complex/[insert adjective here that makes you feel bad about your code]". Chances are even that you did not had these thoughts - but your source code indeed was not really clean, too complex or what not. While the latter situation is certainly the worse of the two, both situation make clear that we need means to quantify the quality of our code. And how do we quantify things? By attaching numbers to it, of course. While a statement such as "80% of my code is crap, I think" is certainly a quantification (one which is not applicable in practice, I hope though), we are looking for a tool that can do the math for us and tell us everything we want to know about our code...

... and here comes NDepend!

NDepend is an incredibly versatile tool that can help us improving our code base. The tool analyses project assemblies and source code regarding a multitude of metrics. NDepend can create static reports containing the results in tabular and graphical form, but it also provides an interactive tool (Visual NDepend) which allows us to drill down into assemblies, namespaces and types in virtually every possible way.

First of all, let's realize why it is so useful to have a tool like NDepend at hand:

Improve Communication

Communication is extremely important if you are developing software in a team. One reason why there are catalogues of design patterns is the fact that they introduce a vocabulary that developers get used to. If I talk about abstract factories, commands and strategies, my colleagues know what I mean.

Using NDepend extends the developers' vocabulary and enriches the way in which developers can communicate. This can be a dialogue between two developers: A: "Hey, this type has high efferent coupling, we need to have a look at it" - B: "You're right, it also has a high lack of cohesion value" - A: "Looks like we should concentrate our next refactoring session on this type..." - B: "Absolutely!" - A: "... but first have a cup of coffee :-)" [note: the last statement is independent of any third party tools]

Track progression and evolution

NDepend is capable of comparing two builds of the same project. This allows us to quantify how the quality of a project evolves. For example, code refactoring should generaly lead to code that is less complex (for example in terms of "number of lines per method" or cyclomatic complexity). By comparing a build before refactoring with one after refactoring you can track how effective your refactoring session was.

Verify development guidelines

NDepend can help us enforcing guidelines that have been agreed upon. For example, you might define that method should not have more than X lines (or Y IL instructions) or that methods with more than 5 lines of code should have at least 20% comment. Checking those guidelines is easily done with NDepend.

Improve code quality

This is, of course, the ultimate goal of all of us - at least I hope :-) Having the numbers (ie. metrics) is one thing, taking consequences from those numbers is the other thing. The numbers (and graphs) NDepend gives us can help us spot places in the code that can be improved. Places which we might have overlooked otherwise. This gives us very concrete chances to improve our source code.

Some basic metrics

Before we go into detail on NDepend, here are some of the more advanced metrics that we will deal with:

  • Afferent coupling (Ca)
    This metric desribes the number of types or methods from outside of the current assembly that use a given type or method. The higher this value, the more important the given type or method is to users of the assembly.
  • Efferent coupling (Ce)
    This is the counterpart of Ca: it describes the number of assembly external types/methods that a specific type/method uses. A high value indicates that the specific type/method is very dependent on the external assembly.
  • Relational cohesion (H)
    A metric that describes how strong the types within a single assembly are related to each other. Generally, types within an assembly should be strongly related, but not too strong.
  • Instability (I)
    This describes how sensitive an assembly is regarding changes ion assemblies it depends on. It is measured as the quotient of efferent coupling (Ce) and total coupling (Ca+Ce).
  • Abstractness (A)
    Describes the ratio of abstract types in an assembly.
  • Distance from main sequence (D)
    Instability and abstractness should be in a certain balance. With my own words, I would describe this balance like that: an assembly with high abstractness should be stable as it is most likely used as an input assembly for other assemblies. If it were instable, it would be likely that it has to change sooner or later and this change would ripple through all assembly that depend on this assembly. On the other hand, a very concrete assembly (low abstractness) is likely to be at the end of a dependency graph, that is, almost no assemblies depend on it. It can and will therefore be quite instable.
  • Lack of cohesion (LCOM)
    In a coherent class, most of the methods will deal with most of the fields of that class. If you find that many methods in the class deal only with a subset of the fields it might be an indicator that the responsibility of the class is too broad and the class should be split.
  • Cyclomatic complexity (CC)
    This metric describes how many pathes a method has. The control flow in a method branches at every conditional statement, loop and other statements. A method with a high CC is hard to maintain.

Visual NDepend

Now that we are convinced that metrics are a Good Thing™  let us have a look at what NDepend brings along.

The NDepend package comes with two programs: the console runner (NDepend.Console.exe) and the graphical user interface (Visual NDepend). The former will be mostly used in automatic builds. To get in touch with NDepend let's stick to the GUI.

User interface styles

Visual NDepend supports to styles:

  1. the "Menu & Toolbar" style -  this is a look and feel comparable to MS Office 2003
  2. the "ribbon" style - this style uses the tabs & ribbons look and feel that you know from MS Office 2007

Here's what you get after you fire up VisualNDepend.exe. To the left, the "Menu & Toolbar" style, to the right the "ribbon" style:

imageimage  

As you see, both versions look very pleasing. The UI of Visual NDepend is extremely polished, certainly among the most polished UIs of any of the tools I use. Personally I prefer the ribbon style - it's well arranged and I can find everything quickly.

You can change between the two styles in the options:

image

Hint: to reduce the amount of place the ribbons take, double click on the tab header. The ribbons will then disappear:

image

A single click on a tab header will show the ribbon temporarily, a double click restores the view back to normal. This is useful if you need as much space as possible, e.g. when you're analysing a solution.

Creating a project - a simple example

imageVisual NDepend supports two operation modes which only differ in the fact whether you explicitly create a project file or not: if you just want to do a quick analysis, simply select the menu point "Select .NET assemblies". This will allow you to perform the analysis without creating an explicit project file.The other option is to create an explicit project. This is of course recommended if you need to perform the analysis more than one time (eg. in continuous builds). image

Let's just create a new project. You only need to name the project and enter a location for the project file (an xml file). I really appreciate the simplicity here. I don't like it if a program requires you to make a lot of decisions when creating a project.

image

After the new project is created you need to add assemblies that NDepend can analyse. To the left you have a list of "Application assemblies". Those assemblies are the ones that are compiled from the source code in the project. To the right there's a list of "Tier assemblies". These are the assemblies that your application assemblies reference, for example mscorlib, the System.* assemblies or other third party libraries. The separation between application assemblies and tier assemblies is extremely useful. Most likely you'll only want to analyse your own assemblies and their dependencies to the tier assemblies - there's no need to analyse the cohesion of classes in System.Core.dll.

To add application assemblies either drag and drop them from the Explorer to the application assemblies list or use "Add Assemblies of a Visual Studio solution" to use a .sln file to look up the project assemblies. The "View folders" button allows you to inspect and add folders from which application and tier assemblies should be loaded. After adding some application assemblies, my screen looks like this:

image

You can use the tabs on the left side to edit additional properties of your project, for example if and how to compare your project to an earlier build, where to put the report files and what to show in your report.

image image

imageAfter we have set up the project, we're ready to go: it's time to run the analysis! NDepend will start analysing your assemblies and generating the report files. The generation process does not take to long, about 20 seconds for a medium sized project on a decent machine.

Result windows

This what you will see after running the analysis;

image

Class browser

On the left side you have a class browser which shows the assemblies, namespaces, types and members of your project. Application assemblies are black, tier assemblies are blue. If you hover over a type or member it is selected in the metrics window and the Info window displays the metrics of the selected element (see the description of the metrics window).

Metrics

The metrics window visualizes the relative and absolute size of assemblies, namespaces, types and methods in terms of lines of code, number of IL instructions etc. This allows us to easily pinpoint the most important types etc. at a glance. If you hover with the mouse over one of the squares it is highlighted, the metric value is shown and the Info window in the bottom left displays all metrics for the selected square:

image

There is a little issue with the Metrics window, though. While hovering or clicking a square updates the Info window, the selection is not fixed. This means that as soon as the mouse leaves the selected square, the Info window will be either empty (if the mouse does not hover a square) or reflects the element that is currently under the mouse. It would be better if a single click on an element in the Metrics window would fix the selection. Clicking on an element in the Class Browser by the way does fix the selection.

Double clicking a member will launch Visual Studio and open the appropriate file. Cool!

Info

The Info window shows metrics for the selected element in the Metrics window or in the Class Browser: number of IL instructions, number of lines, number of lines with comment, percentage of comments, cyclomatic complexity etc.

Dependencies

This window displays the dependencies between the assemblies and types in our project. Starting on the Assembly level this tool allows us to drill down to deeper levels (namespaces, types, members) to detect dependencies between on these levels. Again, NDepend displays application assemblies and tier assemblies differently.

Application assemblies are shown in a triangulated matrix: all app assemblies can potentially be used by other assemblies and use other assemblies at the same time. For example: in this test project, 6 methods in the assembly Vanilla.Web.Monorail together use 17 members of the assembly Vanilla.Web.

image image

 

For tier assemblies only one direction is displayed, that is how they are used by app assemblies. For example, we can see that Vanilla.Web.MonoRail uses 68 types of the Castle.MonoRail.Framework assembly:

image

A single click on any of the squares is meant to show you a dependency graph. As of now this does not work on 64bit platforms. This issue comes from an incompatibility of the used graph rendering library with 64 bit systems. Patrick Smacchia promised that this problem will be taken care of in one of the next versions.

image

Graph creation works nicely on 32 bit platforms, though. I will show an example of this when I come to automatic builds.

By clicking on one of the "+" buttons on the left side or the top side of the matrix you can dig down to lower levels: namespaces, types, members. This gives you endless possibilities of determining dependencies.

image

If you want to focus on a specific dependency, double click the corresponding square and Visual NDepend will "zoom in" into this dependency:

image

Let's leave the Dependencies window alone for now - it's possibilities are countless, just play with it!

CQL Queries

Let me put it straight: this feature is just awesome! NDepend spits out a lot of metrics on its own, but it also gives you a powerful query language that you can use to gather almost any information about your source code that you like.

CQL (Code Query Language) is a query language similar to SQL - which is the first cool thing as most of us are used to SQL. Using CQL you can query against a large set of metrics. Have a look at the CQL specifications to see how complex the query language is.

To give you an example of a simple CQL query:

 SELECT TYPES WHERE NbFields > 6

This query returns all types with more than 6 fields. Easy, hm? Another example: methods that are potentially unused:

 SELECT TOP 10 METHODS WHERE 
  MethodCa == 0 AND            // Ca=0 -> No Afferent Coupling -> The method is not used in the context of this application.
  !IsPublic AND                // Public methods might be used by client applications of your assemblies.
  !IsEntryPoint AND            // Main() method is not used by-design.
  !IsExplicitInterfaceImpl AND // The IL code never explicitely calls explicit interface methods implementation.
  !IsClassConstructor AND      // The IL code never explicitely calls class constructors.
  !IsFinalizer                 // The IL code never explicitely calls finalizers.

Taking the queries a step further, you can define constraints using CQL which can be used to express design guidelines or rules. For example if your design rule is to not have methods with more than 20 lines of code, you can express this constraint like this:

 WARN IF Count > 0 IN SELECT METHODS WHERE NbLinesOfCode > 20

When you analyse your project NDepend will generate a warning for all methods that have more than 20 lines of code (you might want to refactor those methods).

In the CQL window you can group the queries. NDepend comes with a standard set of useful queries so you don't have to write everything from scratch.

image

The CQL query editor is - like the rest of the application - well polished. It provides syntax highlighting and code completion:

image

By the way, the CQL is constantly extended. New metrics are added in almost every new version of NDepend.

Here's a screen shot of the query result window showing types with more than 20 methods:

image

While CQL is already very powerful, I've been missing some features:

  • aggregation - for example I'd like to calculate the max, min and average number of lines of code per method (update: while aggregates are not queryable, the query result window shows some aggregated values, see the screen shot above)
  • comparing metrics - for example I cannot select methods with too many IL instructions per line (like "SELECT METHODS WHERE NbILInstructions  > (NbLinesOfCode * 10)"); CQL won't allow me to compare NbILInstructions with anything other than integer numbers.

But all in all, CQL is a great idea and a powerful language. It is what makes NDepend such a versatile tool.

With this I'll conclude this short overview of Visual NDepend. The programs contains heaps of other features which you should discover for yourself.

The HTML report

Where Visual NDepend is used to setup a project and analyse it interactively, the HTML is meant to represent the state of a project in a static and concise way. The analysis data that NDepend generates is stored in an XML files. This has the advantage that you can simply use XSLT to transform the result into HTML. This is exactly what NDepend does to generate the HTML report. In Visual NDepend you can select to either provide your own xsl file or use the default transformation that NDepend comes with. The latter is certainly useful in most cases. If you need more control, go ahead and build your own xsl transformation file. This fits perfectly into NDepends philosophy: provide a useful default set of functionality, but be open for extensions!

So, what does the default report show:

  • General application metrics
    • lines of code
    • number of IL instructions
    • number of lines with comment, percentage comments
    • number of assemblies, types, classes, interfaces, structs,  etc.
    • Percentage of public types and methods etc.
    • Average number of fields per type, method per type etc.
    • ...
      image
  • Metrics per assembly
    • LOC, number of IL instructions, ...
    • coupling metrics (Ca, Ce, relational cohesion, instability, abstractness, instability-abstractness-balance)
  • Assembly dependencies
    image
  • CQL query & constraints results
    • Warnings for constraints that have failed
       image
  • Type metrics
    • LOC, number of IL instructions, ...
    • coupling metrics (Ca, Ce, lack of cohesion ...)
    • cyclomatic complexity
    • Number of directly and indirectly derived classes, depth in inheritance tree
  • Type dependencies (initially not enabled)
    • Defines which types depend on which types.

The report also contains a dependency view (as in the Metrics window in Visual NDepend), a dependency graph (again, no 64bit support) and graph that show the balance between abstractness and stability.

image

(In the example project, the assemblies seem to be quite instable)

NDepend in automatic builds

You will most likely want to have NDepend generate a report during automatic builds; it's an invaluable tool to define metrics for code quality and to enforce design guidelines. NDepend comes with a command line tool (NDepend.Console.exe) that can be integrated into the build process. The command line tool is held simple: it simply uses a project file that you generated with Visual NDepend beforehand. While this makes it easy to configure an NDepend project at a central place, it has some drawbacks. NDepend stores only absolute paths, for example to folders that contain tested assemblies or to a previous build you want to compare the current build to. Update: the previous sentence is not true, NDepend supports a relative path mode. I simply overlooked the option the whole time. It can be found under Project properties => Code to analyze => Relative path mode:

image 

While you can override the input folders and output folders with command line flags (/InDirs and /OutDir), other options in the project file cannot be overridden. This could cause trouble if you have a dedicated build server.

NDepend ships with an xslt for CruiseControl.NET and build tasks for nant and MsBuild. I haven't used any of the build tasks. I simply used the <exec> task in nant. Here's an example from one of my build files:

 <property name="ndepend.project" value='"${root.dir}\NDependProject.xml"'/>
 <property name="ndepend.outdir" value='${reports.dir}\ndepend'/>
 <property name="ndepend.indirs" value='"${build.dir}"'/>
 <property name="ndepend.indirs" value='${ndepend.indirs} "C:\Windows\Microsoft.NET\Framework\v2.0.50727"'/>
 <property name="ndepend.indirs" value='${ndepend.indirs} "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"'/>
 <property name="ndepend.indirs" value='${ndepend.indirs} "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5"'/>
  
 <exec program="NDepend.Console.exe"
     commandline='${ndepend.project} /InDirs ${ndepend.indirs} /OutDir "${ndepend.outdir}"'/>

Not too hard, if you ask me. The xsl file provided for CruiseControl creates a report that is similar to the HTML report you get when analysing using Visual NDepend, so I won't go into detail here. However, I promised you to show an example of a depency graph, so here we go:

image

Based on a blog post of Robin Curry I further improved ccnet integration, it now looks like this:

image

I needed to update Robin's XSL file to match the current version of NDepend. I plan to write a separate article on this "advanced" ccnet integration, so stay tuned!

Documentation, help, support

One thing I absolutely have to mention positively is the amount of help you get from NDepend. NDepend offers a plethora of tutorials (in video and text form), definition of all metrics, an in-depth specification of CQL, and a massive amount of Tips and Tricks. It is not often that you get that much of support!

Pricing

NDepend licenses are available starting from 299€ (excl VAT), with a massive discount depending on the number of licenses (down to 179€/license if you order more than 20 licenses). Furthermore Enterprise licenses are available on demand. See the purchase page for details.

Conclusion

Wow, that was a long article, wasn't it. Still I could only show you a fraction of the functionality NDepend has to offer. The cool thing is that you can do whatever fits your needs thanks to the extremely flexible and extensible design using CQL. Visual NDepend is a great user interface which makes analysing a project interactively easy fun and interesting. Integrate NDepend into your build process and you have heaps of metrics that you can use to quantify the quality of your code. The price is absolutely adequate.

Pros

  • Extremely versatile and extensible, thanks to CQL
  • Pinpoints problematic areas in your code
  • Quantifies code quality - get rid of "I have the feeling that this and that piece of code is not optimal"
  • Introduces a whole new language to the communication between developers
  • Visual NDepend as a great GUI
  • Large amount of tutorials
  • Useful set of metrics to start with, extensible if needed
  • Very convincing value for the money!

Cons and issues

  • Dependency graphs not supported on x64 machines as of now
  • CQL lacks some possibly interesting features (aggregates, comparison of metrics)
  • NDepend.Console.exe has a limited set of parameters. It would be nice to be able to provide more options instead of relying on project files
  • Project files stores mostly absolute paths  Update: not true, NDepend supports a relative path mode.
  • An HTML report is always created, even in CI scenarios, where the XML files would have been enough.

Granted, none of the issues stated above are show stoppers. All in all there's no doubt that NDepend is an excellent tool. I can wholeheartedly recommend it to any developer who wants to improve the quality of his/her code.

Update 07/08/2008:

Patrick has just published a post in his blog in which he compares NDepend to other tools. I especially like the comparison to tools like Resharper or CodeRush:

I like to think that what tools such as ReSharper or CodeRush are doing to your code at micro level (i.e methods' body structuring), NDepend does it at macro level (i.e class, namespace, assembly structuring). Hence, as a developer I personally use both kind of tools to automatically control every aspects of the code base I am working on.

@Patrick: thanks for mentioning this post!

Things I updated:

  • Rectified statement regarding absolute paths. NDepends does support a relative path mode
  • Added screen shot of query result window and mentioned that aggregates are shown in that window
  • Added link to Patrick's blog

Einfache Authentifizierung mit ASP.NET

07.07.2008 21:30:00 | Jürgen Gutsch

Ich möchte heute mal eine einfache Authentifizierung mit ASP.NET vorstellen, welche ohne die herkömmlichen ASP.NET Login Controls und deren Provider auskommt. Der Vorteil ist, dass es für kleine Anwendungen wesentlich schneller und einfacher umzusetzen ist. Möchte man dagegen noch Dinge nutzen wie Rollen und Profile sind eher die herkömmlichen Controls und Membership-, Role- und ProfileProvider schneller zu implementieren. Der größte Nachteil bei der herkömmlichen Methode ist der, dass es recht aufwendig wird, sobald die Benutzer und Rollen aus einer Benutzerdefinierten und/oder bereits vorhandenen Datenbank kommen sollen, denn dann müssen in der Regel eigene Provider geschrieben werden.

Was wird benötigt?

1) Eine Datenquelle welche die Benutzerinformationen enthält
2) Eine öffentliche Loginseite oder eine öffentliche Seite mit einer Loginmöglichkeit
3) Mehrere geschützte Seiten

Die Datenquelle kann jede beliebige sein (XML, CSV, Access, SQL Server, etc...). In den Code-Beispielen gehe ich nicht auf diese ein, sondern verwende eine fiktive Klasse mit dem Namen "UserLib", welche mir dem Zugriff auf die DatenQuelle abnimmt.

Das Loginformular enthält ein Feld für die Benutzernamen, ein Feld für das Passwort, eine Checkbox um anzugeben, ob ein Cookie gesetzt werden soll und natürlich einen Button:

<asp:Panel ID="pnlLogin" runat="server"
        GroupingText="Login" Width="300" BackColor="#FFFFFF">
    <asp:Label AssociatedControlID="txtUsername" ID="lblUsername"  
        runat="server" Text="Benutzername" />
    <br ID="txtUsername" runat="server" CssClass="text" />
    <asp:Label AssociatedControlID="txtPassword" ID="lblPassword"  
        runat="server" Text="Passwort" />
    <br ID="txtPassword" runat="server" CssClass="text" />
    <br ID="chbRememberLogin" runat="server"  
        Text="Login Merken" />
    <asp:Button ID="btnLogin" runat="server" Text="Anmelden"  
        OnClick="btnLogin_Click" />
    <p>(Benutzername und Passwort: maxm)</p>
</asp:Panel>

Was passiert bei Klick auf den Button?

Wenn beide Felder ausgefüllt sind (zur Prüfung können selbstverständlich die Validator Controls verwendet werden) werden die Daten mit der Datenquelle gegen geprüft. Im Optimalfall ist das Passwort in der Datenquelle verschlüsselt. Das eingegebene Passwort wird als ebenfalls verschlüsselt und dem in der Datenquelle verglichen.

Sind die eingegebenen Daten falsch wird eine erneute Eingabe gefordert. Im anderen Fall wird jetzt erst mal die BenutzerID in einer Sessionvariablen gespeichert. Anschließend wird geprüft, ob die CheckBox für das Cookie angehakt ist. Wenn ja, wird ein Cookie mit der BenutzerID gesetzt. Das Cookie könnte eine Gültigkeit von ca. 14 Tagen haben.

Das war schon alles für den Loginvorgang. Man könnte jetzt auch schon auf eine geschützte Seite weiterleiten, je nach dem wie und wo das Loginformular eingebaut ist.

protected void btnLogin_Click(object sender, EventArgs e)
{
    Guid UserID = UserLib.CheckUser(this.txtUsername.Text,
          this.txtPassword.Text);
    if (UserID == Guid.Empty)
    {
        // Fehlermeldung: Daten stimmen nicht
    }
    else
    {
        Session["UserID"] = UserID;
        if (this.chbRememberLogin.Checked)
        {
            HttpCookie MyCookie = new HttpCookie("UserID");
            MyCookie.Value = UserID.ToString();
            MyCookie.Expires = DateTime.Now.AddDays(14);
            Response.Cookies.Add(MyCookie);
        }
        Response.Redirect("Secure.aspx");
    }
}

Wie wird der User erkannt?

Um mir die Arbeit nicht auf allen Seiten machen zu müsse, schreibe ich mir eine neue Basisklasse für diese Seiten. In meinem Beitrag über die Basisklasse ist die Lösung bereits teilweise umgesetzt. Was fehlte, ist die Prüfung des Cookies, wenn keine Sessionvariable existiert:

// Aktuelle Benutzer ID
private Guid userId = Guid.Empty;
public Guid UserId
{
    get
    {
        if (userId == Guid.Empty)
        {
            object obj = Session["UserID"];
            if (obj != null)
            {
                userId = new Guid(obj.ToString());
            }
            else
            {
                HttpCookie cookie = Request.Cookies["UserID"];
                if (cookie != null)
                {
                    string value = cookie.Value;
                    if (!string.IsNullOrEmpty(value))
                    {
                        userId = new Guid(value);
                        Session["UserID"] = userId;
                    }
                }
            }
        }
        return userId;
    }
}

Theoretisch kann ich mir die Arbeit noch mehr vereinfachen, wenn ich mir die Ermittlung des aktuellen Users ebenfalls in diese Klasse holen (User ist hier eine einfache Klasse welche die Eigenschaften eines Benutzer enthält):

// Aktueller Benutzer
private User currenUser = null;
public User CurrentUser
{
    get
    {
        if (currenUser == null)
            currenUser = UserLib.LoadUser(this.UserId);
        return currenUser;
    }
}

In der Page_Init muss ich nur noch prüfen ob der aktuelle Benutzer nicht existiert und entsprechend z. B. auf eine Loginseite umleiten:

// Muss von öffentlichen Seiten überschrieben werden
protected virtual void Page_Init(object sender, EventArgs e)
{
    if (this.CurrentUser == null)
        Response.Redirect("Default.aspx", true);
}

Wenn der Benutzer als auf eine geschützte Seite trifft, wird erst geprüft, ob eine gültige Session existiert. Wenn nein, wird geprüft, ob ein Cookie existiert. Ist das auch nicht der Fall, kann die zweite Eigenschaft keinen Benutzer ermitteln, gibt Null zurück und es wird zum Login weitergeleitet. Existiert ein Cookie, wird die BenutzerID in die Session gespeichert und der Benutzer ist eingeloggt.

In öffentlichen Seiten muss die Page_Init der Basisklasse überschrieben werden, sonst entsteht eine Endlosschleife. Die Login Seite wird sich immer wieder selbst aufrufen, wenn "CurrentUser" null ist.
Die Loginseite könnte z. B: auf die erste geschützte Seite umleiten, wenn der Benutzer erkannt wurde:

protected override void Page_Init(object sender, EventArgs e)
{
    if (this.CurrentUser != null)
        Response.Redirect("Secure.aspx", true);
}

Anwendung

Von jetzt an kann ich in allen geschützten Seiten, welche von dieser Basisklasse ableiten, auf die Eigenschaften des aktuellen Benutzers zugreifen:

this.lblFullName.Text = String.Format("{0} {1}",
        this.CurrenUser.Name, this.CurrenUser.LastName);

Demoprojekt

Ein kleines Demoprojekt kann hier heruntergeladen werden:
http://www.aspnetzone.de/files/folders/198986/download.aspx
(Es wird das .NET Framework 3.5 verwendet)

Einfache Authentifizierung mit ASP.NET

07.07.2008 21:30:00 | Jürgen Gutsch

Ich möchte heute mal eine einfache Authentifizierung mit ASP.NET vorstellen, welche ohne die herkömmlichen ASP.NET Login Controls und deren Provider auskommt. Der Vorteil ist, dass es für kleine Anwendungen wesentlich schneller und einfacher umzusetzen ist. Möchte man dagegen noch Dinge nutzen wie Rollen und Profile sind eher die herkömmlichen Controls und Membership-, Role- und ProfileProvider schneller zu implementieren. Der größte Nachteil bei der herkömmlichen Methode ist der, dass es recht aufwendig wird, sobald die Benutzer und Rollen aus einer Benutzerdefinierten und/oder bereits vorhandenen Datenbank kommen sollen, denn dann müssen in der Regel eigene Provider geschrieben werden.

Was wird benötigt?

1) Eine Datenquelle welche die Benutzerinformationen enthält
2) Eine öffentliche Loginseite oder eine öffentliche Seite mit einer Loginmöglichkeit
3) Mehrere geschützte Seiten

Die Datenquelle kann jede beliebige sein (XML, CSV, Access, SQL Server, etc...). In den Code-Beispielen gehe ich nicht auf diese ein, sondern verwende eine fiktive Klasse mit dem Namen "UserLib", welche mir dem Zugriff auf die DatenQuelle abnimmt.

Das Loginformular enthält ein Feld für die Benutzernamen, ein Feld für das Passwort, eine Checkbox um anzugeben, ob ein Cookie gesetzt werden soll und natürlich einen Button:

<asp:Panel ID="pnlLogin" runat="server"
        GroupingText="Login" Width="300" BackColor="#FFFFFF">
    <asp:Label AssociatedControlID="txtUsername" ID="lblUsername"  
        runat="server" Text="Benutzername" />
    <br ID="txtUsername" runat="server" CssClass="text" />
    <asp:Label AssociatedControlID="txtPassword" ID="lblPassword"  
        runat="server" Text="Passwort" />
    <br ID="txtPassword" runat="server" CssClass="text" />
    <br ID="chbRememberLogin" runat="server"  
        Text="Login Merken" />
    <asp:Button ID="btnLogin" runat="server" Text="Anmelden"  
        OnClick="btnLogin_Click" />
    <p>(Benutzername und Passwort: maxm)</p>
</asp:Panel>

Was passiert bei Klick auf den Button?

Wenn beide Felder ausgefüllt sind (zur Prüfung können selbstverständlich die Validator Controls verwendet werden) werden die Daten mit der Datenquelle gegen geprüft. Im Optimalfall ist das Passwort in der Datenquelle verschlüsselt. Das eingegebene Passwort wird als ebenfalls verschlüsselt und dem in der Datenquelle verglichen.

Sind die eingegebenen Daten falsch wird eine erneute Eingabe gefordert. Im anderen Fall wird jetzt erst mal die BenutzerID in einer Sessionvariablen gespeichert. Anschließend wird geprüft, ob die CheckBox für das Cookie angehakt ist. Wenn ja, wird ein Cookie mit der BenutzerID gesetzt. Das Cookie könnte eine Gültigkeit von ca. 14 Tagen haben.

Das war schon alles für den Loginvorgang. Man könnte jetzt auch schon auf eine geschützte Seite weiterleiten, je nach dem wie und wo das Loginformular eingebaut ist.

protected void btnLogin_Click(object sender, EventArgs e)
{
    Guid UserID = UserLib.CheckUser(this.txtUsername.Text,
          this.txtPassword.Text);
    if (UserID == Guid.Empty)
    {
        // Fehlermeldung: Daten stimmen nicht
    }
    else
    {
        Session["UserID"] = UserID;
        if (this.chbRememberLogin.Checked)
        {
            HttpCookie MyCookie = new HttpCookie("UserID");
            MyCookie.Value = UserID.ToString();
            MyCookie.Expires = DateTime.Now.AddDays(14);
            Response.Cookies.Add(MyCookie);
        }
        Response.Redirect("Secure.aspx");
    }
}

Wie wird der User erkannt?

Um mir die Arbeit nicht auf allen Seiten machen zu müsse, schreibe ich mir eine neue Basisklasse für diese Seiten. In meinem Beitrag über die Basisklasse ist die Lösung bereits teilweise umgesetzt. Was fehlte, ist die Prüfung des Cookies, wenn keine Sessionvariable existiert:

// Aktuelle Benutzer ID
private Guid userId = Guid.Empty;
public Guid UserId
{
    get
    {
        if (userId == Guid.Empty)
        {
            object obj = Session["UserID"];
            if (obj != null)
            {
                userId = new Guid(obj.ToString());
            }
            else
            {
                HttpCookie cookie = Request.Cookies["UserID"];
                if (cookie != null)
                {
                    string value = cookie.Value;
                    if (!string.IsNullOrEmpty(value))
                    {
                        userId = new Guid(value);
                        Session["UserID"] = userId;
                    }
                }
            }
        }
        return userId;
    }
}

Theoretisch kann ich mir die Arbeit noch mehr vereinfachen, wenn ich mir die Ermittlung des aktuellen Users ebenfalls in diese Klasse holen (User ist hier eine einfache Klasse welche die Eigenschaften eines Benutzer enthält):

// Aktueller Benutzer
private User currenUser = null;
public User CurrentUser
{
    get
    {
        if (currenUser == null)
            currenUser = UserLib.LoadUser(this.UserId);
        return currenUser;
    }
}

In der Page_Init muss ich nur noch prüfen ob der aktuelle Benutzer nicht existiert und entsprechend z. B. auf eine Loginseite umleiten:

// Muss von öffentlichen Seiten überschrieben werden
protected virtual void Page_Init(object sender, EventArgs e)
{
    if (this.CurrentUser == null)
        Response.Redirect("Default.aspx", true);
}

Wenn der Benutzer als auf eine geschützte Seite trifft, wird erst geprüft, ob eine gültige Session existiert. Wenn nein, wird geprüft, ob ein Cookie existiert. Ist das auch nicht der Fall, kann die zweite Eigenschaft keinen Benutzer ermitteln, gibt Null zurück und es wird zum Login weitergeleitet. Existiert ein Cookie, wird die BenutzerID in die Session gespeichert und der Benutzer ist eingeloggt.

In öffentlichen Seiten muss die Page_Init der Basisklasse überschrieben werden, sonst entsteht eine Endlosschleife. Die Login Seite wird sich immer wieder selbst aufrufen, wenn "CurrentUser" null ist.
Die Loginseite könnte z. B: auf die erste geschützte Seite umleiten, wenn der Benutzer erkannt wurde:

protected override void Page_Init(object sender, EventArgs e)
{
    if (this.CurrentUser != null)
        Response.Redirect("Secure.aspx", true);
}

Anwendung

Von jetzt an kann ich in allen geschützten Seiten, welche von dieser Basisklasse ableiten, auf die Eigenschaften des aktuellen Benutzers zugreifen:

this.lblFullName.Text = String.Format("{0} {1}",
        this.CurrenUser.Name, this.CurrenUser.LastName);

Demoprojekt

Ein kleines Demoprojekt kann hier heruntergeladen werden:
http://www.aspnetzone.de/files/folders/198986/download.aspx
(Es wird das .NET Framework 3.5 verwendet)

Softwarezellen - oder: Wie baue ich eine Architektur?

07.07.2008 13:45:20 | Rainer Schuster

Ralf Westphal

... berichtet schon seit längerem über Architektur und seine Gedanken dazu. 2005 hat er mit einem neuen Model für sich angefangen, da ihm die bisher übliche Schichtenarchitektur nicht ausreichend war. In der dotnetpro finden wir auch immer wieder Artikel von ihm, die mit diesem System angereichert sind.

Und ich?

Persönlich habe ich auch schon Programmarchitekturen damit zerlegt und erstellt und muss sagen das dieses System - hat man es einmal verstanden - sehr eingängig ist und schon bei der ersten Anwendung einen großen "Benefit" bringt. In jedem Fall findet - so bisher meine Erfahrung - eine win-win Situation statt. Ich kann meinem Vorgesetzten oder dem Stakeholder meine reflektiven Fähigkeiten beweisen und mein Gegenüber bekommt im Gegenzug ein einfaches und selbsterklärendes Diagramm der jeweiligen Architektur.

Zurück zum Thema.

Des öfteren berichtet er in seinem deutschen, sowie englischen Blog über die Evolution seines Systems. Nun widmet er sich in einem neuen, dedizierten Blog (The Architect's Napkin) zu diesem Thema. Als eine Art Journal wird er es zum Sammeln seiner Ideen für ein Buch nutzen und uns seine Gedanken damit näher bringen. Grundgedanke und Medium für die Konstruktion der Ideen sind Servietten. Alles was nicht auf eine passt, ist nicht wertvoll und zu überladen und in der Endkonsequenz damit unbrauchbar. Wie bekomme ich dann eine Architektur auf eine Serviette? Das Abstraktionslevel wird von Serviette zu Serviette gesteigert. Aber lest selber nach. Es lohnt sich für alle Architekturinteressierten. Er listet in einem Beitrag die Fragen auf, die sich der Architekt stellen sollten, wenn es darum geht eine Softwarezelle zu erstellen. Nun finde ich, ist dieser kleine Fragenkatalog abgesehen davon auch für Softwareentwickler sinnvoll. Darum einmal hier die Auflistung von ihm:

  • How is the software cell started and stopped?
  • Can the software cell be paused or interrupted?
  • How are processing failures reported to the environment of the software cell?
  • How should data be passed into and out of the software cell?
  • How is shared data protected from inconsistencies through concurrent access by several software cells?
  • How can I be informed of certain states during a process executed by a software cell?
  • How can I know if a software cell is still alive and progressing?
  • How fast, reliable, secure etc. is a software cell and a connection to it?
  • Does it matter to a client of a software cell, where and if the software cell is running?

ASP.NET Hacks

07.07.2008 13:09:33 | Jürgen Gutsch

Auf lessthandot.com ist eine kleine Liste mit nützlichen ASP.NET Hacks (Tips und Tricks) zu folgenden Themen zu finden:

  1. Applications
  2. Caching
  3. Controls
  4. Datenbanken
  5. Daten
  6. Debugging
  7. Email
  8. Encryption
  9. Files
  10. Images
  11. JavaScript
  12. Objekte and klassen
  13. Pages
  14. Sessions
  15. Strings
  16. Validation
  17. Visual Studio
  18. Web

via: http://weblogs.asp.net/marksmith/.../asp-net-quot-hacks-quot-series-at-lessthandot.aspx

ASP.NET Hacks

07.07.2008 13:09:33 | Jürgen Gutsch

Auf lessthandot.com ist eine kleine Liste mit nützlichen ASP.NET Hacks (Tips und Tricks) zu folgenden Themen zu finden:

  1. Applications
  2. Caching
  3. Controls
  4. Datenbanken
  5. Daten
  6. Debugging
  7. Email
  8. Encryption
  9. Files
  10. Images
  11. JavaScript
  12. Objekte and klassen
  13. Pages
  14. Sessions
  15. Strings
  16. Validation
  17. Visual Studio
  18. Web

via: http://weblogs.asp.net/marksmith/.../asp-net-quot-hacks-quot-series-at-lessthandot.aspx

Dieses Blog steht unter einer Creative Commons-Lizenz

07.07.2008 10:41:25 | Albert Weinert

Schön etwas länger, als versteckte Information im RSS Feed, habe ich es nun etwas deutlicher kenntlich gemacht

Zusammenfassung http://creativecommons.org/licenses/by-sa/2.0/de/

Kleingedrucktes http://creativecommons.org/licenses/by-sa/2.0/de/legalcode

Somit erlaube ich explizit meine Beiträge unter bestimmten Bedingungen weiter zu verwenden.

Bei einer abweichende Verwendung ist weiterhin meine Zustimmung notwendig. Einfach fragen, wir werden uns sicherlich über die Bedingungen einig.

CC Logo

Für mehr zu Creative Commons empfehle ich folgende weiterführende Informationen

Die Diplomarbeit von Nicole Ebber
http://www.antischokke.de/meine-diplomarbeit/

Die Wikipedia
http://de.wikipedia.org/wiki/Creative_Commons 

Die Seite von Creative Commons:
http://de.creativecommons.org

Sowie einen kurzen Film der Creative Commons erklärt.
http://de.creativecommons.org/werbefilm-creative-commons-erklart/

Technorati-Tags: ,

Shared Memory in verwaltetem Code

06.07.2008 18:23:51 | Klaus Bock

Zum Austausch von Daten zwischen verschiedenen Prozessen bietet das .NET Framework diverse Möglichkeiten im Namensraum System.Runtime.Remoting und den untergeordneten Namensräumen. Jedoch habe alle diese Klassen eins gemein: mit Ausnahme des System.Runtime.Remoting.Channels.Ipc-Namensraum verwenden sie alle Channels der verschiedenen Netzwerk-Protokolle. Als ich begann mich mit Interprozesskommunikation in verwaltetem Code zu beschäftigen, gab es den Ipc-Namensraum im .NET Framework noch nicht. Doch wenn ich mir heute die Verwendung der IpcChannel Klasse anschaue, ist mir das viel zu umständlich. Da müssen URIs angegeben und Objekte für Remote-Aufrufe zur Verfügung gestellt und geparst werden. Ich will doch nur ein paar Daten zwischen zwei Prozessen austauschen! In nicht verwaltetem C++ gab und gibt es Shared Memory, einen gemeinsam genutzten Speicherbereich auf den von beiden Prozessen zugegriffen werden kann. Da die Win32-API diese Möglichkeit bietet sollte dieses Prinzip doch auch in verwaltetem Code zu realisieren sein. Wie in folgendem kleinem Video zu sehen ist, funktioniert es wunderbar. Zum Testen des Shared Memory habe ich ein Projekt mit zwei Konsolenanwendungen erstellt. Die eine Anwendung, welche als Client fungiert, startet die zweite Anwendung als Server und liest die Werte welche von der Server-Anwendung in den Shared Memory geschrieben werden.

Die Initialisierung der SharedMemory Klasse ist denkbar einfach. Wenn in das Shared Memory Segment geschrieben werden soll, wird der Konstruktor der Klasse mit dem Namen des Segments und dem einzufügenden Objekt aufgerufen.

SharedMemory sm = new SharedMemory("testing", obj);

Wenn aus dem Shared Memory Segment nur gelesen werden soll, wird ein Konstruktor lediglich mit dem Namen des Shared Memory Segment aufgerufen.

SharedMemory sm = new SharedMemory("testing");

Um das schreiben in und das lesen aus dem Shared Memory Segment threadsicher zu gestalten, stellt die Shared Memory Klasse die öffentlichen Methoden Lock() und Unlock() bereit. Diese sperren den verwendeten Mutex der Klasse bzw. geben ihn wieder frei. So wird ein Objekt wie folgt in das Shared Memory Segment geschrieben:

sm.Lock();
sm.AddObject(value);
sm.Unlock();

und so wird ein Objekt aus dem Shared Memory Segment gelesen:

sm.Lock();
object obj = sm.GetObject();
sm.Unlock();

So einfach kann IPC (inter-process communication) sein.

Soweit zur Verwendung der Shared Memory Klasse.
Um die Funktionsweise von Shared Memory unter Windows zu verstehen, muss man sich erst ein wenig mit der Speicherverwaltung von Windows auseinandersetzen. Unter Windows läuft die gesamte Speicherverwaltung über den System eigenen Memory Manager der für jeden Prozess einen eigenen virtuellen Adressraum zur Verfügung stellt. Dieser virtuelle Adressraum entspricht aber nicht der physikalischen Adresse im Speicher. Er muss sich gar nicht im Hauptspeicher befinden, sondern kann in die Auslagerungsdatei ausgelagert worden sein. Wenn jetzt ein Prozess auf einen Bereich seines virtuellen Adressraum's zugreift, weis die Speicherverwaltung von Windows wo sich der zugehörige physikalische Adressbereich befindet und gibt diesen and den Prozess zurück. Mit Hilfe der Win32-API kann nun ein gemeinsamer Speicherbereich für mehrere Prozesse geschaffen werden, der physikalisch nur einmal vorhanden ist. Unter Windows werden diese Speicherbereiche File Mapping Objekte genannt. In so einem Objekt wird ein Teil oder auch eine gesamte Datei im physikalischen Speicher abgebildet. Die Speicherverwaltung kann jetzt einen angegebenen oder auch den gesamten Speicherbereich im virtuellen Speicher eines oder mehrere Prozesse zur Verfügung stellen.
Wenn man diese Information bedenkt, wird vielleicht klar, warum in der Win32-API die Verwendung von Shared Memory und Memory-Mapped Files in einer API zusammengefasst sind. Es spielt also keine Rolle ob man ein Shared Memory Segment verwendet oder den Inhalt einer bestimmten Datei als File Mapping abbildet; es ist immer die gleiche API. Es braucht sich also niemand zu wundern wenn die Funktionen CreateFileMapping oder OpenFileMapping verwendet werden obwohl keine Datei vorhanden ist die im Speicher abgebildet werden soll.

Da ich mir sicher war, dass bestimmt schon jemand vor mir auf die Idee gekommen war Shared Memory in verwaltetem Code zu verwenden suchte ich Informationen zu einer möglichen Implementierung. Mehr durch Zufall fand ich ein paar Zeilen dazu, sowie ein Demo-Projekt zum Download, in Richads Blewett's altem Blog. Seine Segment Klasse gefiel mir auf den ersten Blick sehr gut. Das erzeugen bzw. öffnen eines File Mapping Objekts wird im Konstruktor erledigt. Der Zeiger auf das Shared Memory Segment wird im Konstruktor erzeugt und in einer Klassen Member Variablen vom Typ IntPtr gehalten. Das kopieren der Daten in und aus dem Segment wird mit einem Stream erledigt.
Was mir nicht gefiel, war die Verwendung von unsafe Pointern und die Verwendung der fixed-Anweisung. Hier die original Methoden welche Daten in und aus einem Shared Memory Segment kopieren:

/// <summary>
/// Copies stream to shared memory segment using unsafe pointers
/// </summary>
/// <param name="stream"> System.IO.Stream - data to be copied to shared memory</param>
private unsafe void CopyStreamToSharedMemory( Stream stream )
{
    // Read stream data into byte array
    BinaryReader reader = new BinaryReader(stream);

    Byte[] data = reader.ReadBytes((int)stream.Length);

    // Copy the byte array to shared memory
    fixed( byte* source = data )
    {
        void* temp = nativePointer.ToPointer();
        
        byte* dest = (byte*)temp;    

        Win32Native.CopyMemory((int) dest, (int) source, (int)stream.Length);
    }
}
/// <summary>
/// Copies shared memory data to passed stream using unsafe pointers
/// </summary>
/// <param name="stream">System.IO.Stream - stream to receive data</param>
private unsafe void CopySharedMemoryToStream( Streamstream )
{
    // Create a tempory byte array to store the length
   void* temp = nativePointer.ToPointer();

    byte* source = (byte*)temp;

    long len = *(long*)temp;
        
    // Set the source data pointer to start of serialized object graph
   source = (byte*)temp;
    source += 8;

    // Create a byte array to hold the serialized data
   Byte[] data = new Byte[len];

    // Copy the shared memory data to byte array
   fixed(byte* dest = data )
    {
        Win32Native.CopyMemory((int)dest, (int)source, (int)len);
    }

    // Write the byte array to the stream
   BinaryWriter writer = new BinaryWriter(stream);

    writer.Write(data);

    // Reset stream to start
   stream.Seek(0, SeekOrigin.Begin);
}

Dies ist eine gutes Beispiel um zu zeigen wie sich der Umgang mit Zeigern und das kopieren von Speicherbereichen durch Verwendung der Marshal Klasse stark vereinfachen lässt sowie auf unsafe und fixed komplett verzichtet werden kann. Vor allem die Methode Marshal.Copy ersetzt die Win32-API Funktion CopyMemory ohne von P/Invoke Gebrauch machen zu müssen.

private void copyStreamToSharedMemory(Stream stream)
{
    // die Stream-Daten in ein byte Array lesen
    BinaryReader reader = new BinaryReader(stream);
    byte[] data = reader.ReadBytes((int)stream.Length);

    // kopiere das byte Array in den SharedMemory
    Marshal.Copy(data, 0, this.nativePointer, (int)stream.Length);
}

Ähnlich verhält es sich auch mit der Verwendung der Zeiger in der zweiten Methode CopySharedMemoryToStream. In der Original-Implementierung wird ein Konstrukt aus Zeigern verwendet um die Länge der Daten im Shared Memory Segment zu ermitteln sowie einen Zeiger auf das Shared Memory Segment zu erhalten. Als erstes wird mit

void* temp = nativePointer.ToPointer();

ein Zeiger auf einen Speicherbereich erzeugt, von dem man nicht weis welchen Typs die enthaltenen Daten sind. Wenn jetzt die Speicheradresse bekannt ist, wird mit

long len = *(long*)temp;

ein Zeiger vom Typ long erzeugt um die Länge des Speicherbereichs zu lesen.

Nun wird noch der erzeugte Zeiger in einen Zeiger auf eine nicht verwaltete Speicheradresse geparst um die enthaltenen Daten aus dem Shared Memory Segment kopieren zu können:

byte* source = (byte*)temp;

Mit der Marshal Klasse lässt sich das sehr elegant auch ohne unsafe Zeiger erledigen:

// die Länge der Daten im SharedMemory Segment bestimmen.
long objLength = (long)Marshal.ReadIntPtr(this.nativePointer);

// Zeiger auf das SharedMemory Segment erzeugen.
IntPtr source = (IntPtr)((long)this.nativePointer + sizeof(long));

Wahrscheinlich fragt sich hier der ein oder andere warum so ein Aufwand mit der Länge der Daten im Shared Memory Segment betrieben wird. Nun ganz einfach: um die Daten aus dem Shared Memory Segment zu kopieren benötigt man ein byte-Array. Dieses Bayre-Array muss mit einer bestimmten Länge erzeugt werden um alle Daten aufnehmen zu können. Woher soll also die Information genommen werden, wie groß die Datenmenge im Shared Memory Segment ist? Um dies zu handhaben wird beim kopieren der Daten in das Shared Memory Segment an erster Stelle im Stream die Größe des serialisierten Objekts als long-Wert geschrieben und direkt im Anschluss das eigentliche Objekt in den Stream serialisiert.
Aus dem gleichen Grund wird auch ein Offset von der Länge des Typs long bei der Erzeugung des Zeigers auf das Shared Memory Segment verwendet. Da ja, wie oben erwähnt, die ersten acht byte der Daten im Speicher mit der Länge des Objekts belegt sind muss der Zeiger um acht byte, die Länge des Typs long, nach hinten verschoben werden.

Hier nun die komplette Methode zum kopieren der Daten des Shared Memory Segment in einen Stream:

private void copySharedMemoryToStream(Stream stream)
{
    // die Länge der Daten im SharedMemory Segment bestimmen.
    long objLength = (long)Marshal.ReadIntPtr(this.nativePointer);

    // Zeiger auf das SharedMemory Segment erzeugen.
    IntPtr source = (IntPtr)((long)this.nativePointer + sizeof(long));

    // ein byte Array mit der Länge des Speicherbereichs
    // erzeugen um die serialisierten Daten aufzunehmen.
    byte[] data = new byte[objLength];

    // die SharedMemory Daten in das byte Array kopieren
    Marshal.Copy(source, data, 0, (int)objLength);

    // neue BinaryWriter erzeugen
    BinaryWriter writer = new BinaryWriter(stream);

    // das byte Array in den Stream schreiben
    writer.Write(data);

    // den Stream auf seinen Startpunkt setzen
    stream.Seek(0, SeekOrigin.Begin);
}

Dies sind die beiden privaten Methoden, welche transparent das kopieren der Daten in und aus dem Shared Memory Segment erledigen. Auf die anderen Member der Klasse sowie auf weitere verwendete Methoden der Win32-API will ich hier nicht näher eingehen. Die Grundlage und die entscheidenden Methoden habe ich gezeigt. Für interessierte deren Neugier ich geweckt habe, steht ein Demo-Projekt zum Download bereit. Der Code im Demo-Projekt ist weitestgehend dokumentiert.

IpcTest

Technorati-Tags: | | |

Der Einsatz von HTTP 301 und 302 Statuscodes in ASP.NET

06.07.2008 01:32:58 | Jan Welker

Die Methode Response.Redirect() kennt vermutlich jeder ASP.NET Entwickler. Mit Hilfe dieser Methode wird ein Seitenbesucher auf eine andere Webseite umgeleitet. Doch was steckt dahinter? Wann sollte man die Redirect Methode nutzen und wann nicht?

Schauen wir uns an was passiert, wenn folgender Code ausgeführt wird:

Response.Redirect("http://dotnet-snippets.de/dns/Default.aspx"); 


Der Webserver antwortet dem Client mit dem HTTP Statuscode 302, dies bedeutet das die Webseite temporär umgeleitet wurde.
Zitat von Wikipedia:
Die angeforderte Ressource steht vorübergehend unter der im „Location“-Header-Feld angegebenen Adresse bereit (in HTTP/1.0 „Moved Temporarily“, RFC 1945). Die alte Adresse bleibt gültig.

Im Trace sieht dies folgendermaßen aus:

302  

Wenn eine Seite temporär unter einer anderen Adresse erreichbar ist, sollte Response.Redirect() verwendet werden. Ist eine Seite zukünftig immer unter einer anderen Adresse erreichbar, sollte der HTTP Statuscode 301 verwendet werden.
Der Statuscode 301 steht für eine permanente Umleitung. Zitat von Wikipedia:
Die angeforderte Ressource steht ab sofort unter der im „Location“-Header-Feld angegebenen Adresse bereit. Die alte Adresse ist nicht länger gültig.
Dies ist sehr wichtig für Suchmaschinen, denn somit bekommen Suchmaschinen die Information, dass die alte URL aus dem Index entfernt werden kann.

Wie kann erreicht werden, dass der Statuscode 301 gesendet wird?


HttpContext.Response bietet hierzu die Eigenschaft Status und die Methode AddHeader an:

HttpContext.Current.Response.Status = "301
Moved Permanently"; HttpContext.Current.Response.AddHeader("Location",
newUrl);

Um diese 301 - Weiterleitung jederzeit komfortabel nutzen zu können bietet sich eine Extension Method an:

using System.Web; namespace Welker.Extensions
{ public static class Extensions
{ public static void RedirectPermanently(this HttpResponse
response, string newUrl) { HttpContext.Current.Response.Status
= "301 Moved Permanently"; HttpContext.Current.Response.AddHeader("Location",
newUrl); } } }

Nach dem Einbinden der Erweiterungsmethode kann diese über folgenden Code aufgerufen werden:

Response.RedirectPermanently("http://live.com");

Im Trace kann man den Unterschied deutlich sehen:

301

Have Fun!

Einzeilige Methoden?

05.07.2008 16:06:49 | Albert Weinert

In Stelle immer wieder fest dass es eine ziemlich eingefahrene Meinung dazu gibt ab wann sich eine eigene Methode lohnt und wann nicht. Meist heißt es dass für eins bis drei Zeilen keine Methode notwendig ist und man durch einen Methoden Aufruf ja auch Performance Einbußen hat.

Ja, dieser Ansicht war ich früher auch. Problem an der Sache ist einfach, dass man auch bei wenig Zeilen oft manchmal nicht direkt den Zweck dahinter erkennt. Also müsste man entsprechende Kommentare schreiben die erklären was dort eigentlich passiert.

Hier ein kleines Beispiel welches ich gerade geschrieben hatte.

public void Register(Type interfaceType, Type implementationType, bool isStatic)
{
    lock (servicesLock)
    {
        EnsureValidInterface(interfaceType);
        if (implementationType.FindInterfaces(
                     (m, criteria) => m.Equals(interfaceType), null).Length == 0)
        {
            // Do Some Magic
        }
        services.Add(interfaceType, new IoCInfo(implementationType, isStatic));
    }
}

Hier muss dass if-Statement schon sehr genau angesehen werden damit einem klar wird was dort eigentlich überprüft wird. Somit wird der Fluss beim Lesen doch sehr gestört weil ich erst einmal analysieren muss was da passiert.

Ein kleiner Kommentar drüber und schon wüsste man was passiert. Der Kommentar müsste ungefähr so lauten "Wird das angegebene Interface implementiert?". Doch Hand auf's Herz, machen wir Entwickler dies wirklich? Des weiteren lesen wir doch noch den Code mit und versuchen ihn zu verstehen, somit haben wir nicht wirklich etwas gewonnen.

Jedoch besteht auch die Möglichkeit das if-Statement in eine eigene Methode auszulagern, deren Bezeichnung dazu noch ausdrückt was darin gemacht wird.

private bool InterfaceIsImplemented(Type interfaceType, Type implementationType)
{
    return implementationType.FindInterfaces(
           (m, criteria) => m.Equals(interfaceType), null).Length > 0;
}

Ein Kommentar bei der Verwendung wird nicht mehr gebraucht, die Methode spricht für sich selbst.

public void Register(Type interfaceType, Type implementationType, bool isStatic)
{
    lock (servicesLock)
    {
        EnsureValidInterface(interfaceType);
        if (!InterfaceIsImplemented(interfaceType, implementationType))
        {
            // Do some Magic
        }
        services.Add(interfaceType, new IoCInfo(implementationType, isStatic));
    }
}

Es wird direkt beim lesen des Codes deutlich was man prüft. Dass wie es gemacht wird, interessiert mich erst wenn es sich andersartig verhält. Des weiteren hat man ohne großen Aufwand die Möglichkeit geschaffen den Code an mehreren Stellen zu verwenden ohne in in per Copy & Paste zu vervielfältigen.

Die Performance-Frage ist in den allermeisten Fällen zu vernachlässigen. An erster Stelle sollte Funktion, Lesbarkeit sowie einfache Wartung des Codes stehen. Tritt dann ein Performance Problem auf so kann man sich dann immer noch damit beschäftigen, jedoch nicht ohne wirklich mit einem Profiler nachzumessen, da die Performance oft nicht da liegen bleibt wo man meint.

Nun mag man noch es wäre ja erhöhter Aufwand "so viel" in einzelnen Methoden zu packen, dem kann ich nur widersprechen. Visual Studio bietet seit der Version 2005 eingebautes Refactoring mit dem man mit einem Shortcut eine entsprechende Methode aus dem Code extrahieren kann. Beim schreiben des Codes kann man dies auch berücksichtigen den unbekannte Methodenrümpfe sind auch per Shortcut zu erstellen.

Es gibt AddIns wie ReSharper von JetBrains oder ReFactor! von DevExpress, die weit über die Möglichkeiten von Visual Studio hinausgehen und ein Vielfältigeres Refactoring des Codes erlauben.

HowToCode "ReadYou": Neue Gedanken zum Usersystem mit OpenID, Windows Live und co.

03.07.2008 23:38:03 | Robert Mühsig

Mein letzter direkter “ReadYou” Post ist schon etwas länger her - meine Membership-Idee habe ich in einem HowTo mal verwirklicht, allerdings hatte ich bisher keine Gelegenheit mehr zu coden.

Authentication, Authorization & Personalization
Für mich persönlich war der Kommentar von Andre Loker ein Anstoß darüber nachzudenken, das Membership System komplett zu entfernen bzw. einfach meinen eigenen Provider zu schreiben. Die verschiedenen Tabellen vom Membership-System find ich recht “groß” und unübersichtlich.
Insbesondere passt aber das Membership-System nicht ganz in meine Architektur rein.

Gut, dass nicht nur ich so denke, sondern inzwischen auch Rob Conery zu diesem Ergebnis gekommen ist. Den Anstoß gab Ayende Rahien in dem 15 Teil vom MVC Storefront Screencast.
Inzwischen hat Rob den 16 Teil fertig gestellt - in diesem verwirft er das Membership-System und implementiert prototypisch OpenID.

Hinweis: Meine Anwendung wird stark an die Architektur von Rob angelehnt sein - ich hab dies bereits produktiv in einem Projekt eingesetzt und bin positiv überrascht wie gut dies geklappt hat :)
Die gesamte MVC Storefront Serie ist sehr sehenswert!

Robs Screencast endet bei dem Login mit OpenID - er erwähnt noch, dass er sich nun auch Gedanken macht, wie er die OpenID tatsächlich an einen User knüpfen kann. Selbe Gedanken beschäftigen mich ebenfalls schon eine Weile (und insbesondere habe ich ja noch den mutigen Vorschlag gehabt Windows Live ID mit zu unterstützen ;) ).

Was es nicht heute alles gibt
Ich muss zugeben, dass ich früher mir nicht so einen großen Kopf um das Usersystem an sich gemacht hatte - da wird das Passwort und ein Loginname vergeben und gut ist.
Allerdings bin ich der Meinung, dass hier einige große Veränderungen kommen - warum sich bei X-Diensten registrieren, wenn man doch z.B. eine OpenID hat.
Auch CardSpace ist eine Sache - Produktiv hab ich das (bis auf den Blog Kim Cameron) nicht gesehen.

Jetzt eine Frage an meine werte Leserschaft: Wer von euch weiß was OAuth ist und was man damit machen kann? So richtig schlau werde ich aus der Seite nicht.

Wo stehen wir momentan?
Momentan beschäftigt mich das Grundkonzept von diesen verschiedenen Diensten etwas - da mir leider da auch TDD nicht weiterhelfen kann, versuche ich mich erstmal zu informieren.
Ein paar interessante Links (neben den MVC Storefront) :

Das wäre es (leider) für heute erst mal.
Ein Hinweis noch: Mir ist bewusst, dass ich mich doch recht lange an einem doch trivial klingenden Thema aufhalte - allerdings genau wegen solchen Sachen mach ich das ja auch. Wer würde das denn sonst auch bezahlen? ;)

ShareThis

Neue Version von Moonlight verfügbar (Silverlight für Linux)

03.07.2008 16:03:00 | Steffen Ritter

MoonlightDie Entwicklung von Moonlight (die offizielle Linux-Version von Silverlight) schreitet mit riesen Schritten voran. Das freut mich ganz besonders, denn gerade in Deutschland war die Frage nach einer Linux-Version von Silverlight von Anfang an eines der Hauptthemen in vielen Interviews und Gesprächen die ich zu Silverlight geführt hatte. Auch heute noch scheint sich in diversen hei(s)sen Foren hartnäckig das Gerücht zu halten „dieses Silverlight gibt’s ja nur für Windows“ – aber die Zahl der Informierten wächst permanent. Noch einmal für alle: Silverlight für Mac OS X und Windows wird von Microsoft selbst entwickelt, Silverlight für Linux heisst Moonlight und wird von dem Mono-Team bei Novell mit Unterstützung von Microsoft programmiert. Die neue Version ist auf der Moonlight-Homepage verfügbar: http://www.go-mono.com/moonlight

Einige der Neuerungen und Verbesserungen:

  • Unterstützung für Webkit-basierte Browser
  • Fehler im Windowsless Mode wurden behoben
  • Fehler beim Event Handling wurden behoben
  • Unzählige Fehler im Clock/Animation Framework wurden behoben; Moonlight besteht nun beide Animation Matrix Tests
  • Fehler im Parser wurden behoben
  • Mausprobleme wurden behoben
  • Verbesserungen bei der Verarbeitung von Medien/Streaming
  • Drastische Performanceverbesserungen

Moonlight ist momentan noch Stand Silverlight 1.0, aber erste (einfache) Silverlight 2-Anwendungen laufen bereits mit Moonlight

Load a font from disk, stream or byte array

03.07.2008 11:30:32 | Andre Loker

A user asked in the Gamedev.net Forums how to load a Font from a Stream or a byte array. An interesting question, I think, because there might be applications that come with their own fonts. The obvious way to make the font available is by installing it to the font collection of the O/S. In that case one could simply use the Font constructor that takes the font family name as its first argument and be happy.

However, there are several good reasons to decide against installing a new font to the O/S:

  • Installing a font requires administrative privileges
  • You don't want to clutter the font collection unnecessarily
  • Your app does not have or need an installer because it's a very simple tool. You neither want to add an installer just for installing the font, nor do you want the user to manually install a font.

Basics: load font from file

Therefore, the first thing we would like is to load a true type font file (ie. a .ttf file) directly from disk, without the need to install it first. To achieve this, the .NET framework provides the class System.Drawing.Text.PrivateFontCollection. The method AddFontFile does just what we want: load a ttf file. The Families property returns an array of all font families that have been loaded so far.

The method below shows a method that loads a font file and returns the first font family:

 public static FontFamily LoadFontFamily(string fileName, out PrivateFontCollection fontCollection) {
   fontCollection = new PrivateFontCollection();
   fontCollection.AddFontFile(fileName);
   return fontCollection.Families[0];
 }

The returned FontFamily can then be used to construct specific fonts, like this:

 PrivateFontCollection fonts;
 FontFamily family = LoadFontFamily("TheFont.ttf", out fonts);
 Font theFont = new Font(family, 20.0f);
 // when done:
 theFont.Dispose();
 family.Dispose();
 family.Dispose();

You can then use the font as usual.

Next level: load font from stream or byte array

If you want to deploy your application as a single .exe you would prefer to load the font from an embedded resource rather than loading it from a separate file. PrivateFontCollection provides a method called AddMemoryFont that we can use for this purpose. Loading a file into memory is a snap, but AddMemoryFont accepts an IntPtr and a length. Therefore we need to do fiddle a bit to get the IntPtr from the byte array. Here's a method that loads a font family from a byte array:

 // load font family from byte array
 public static FontFamily LoadFontFamily(byte[] buffer, out PrivateFontCollection fontCollection) {
   // pin array so we can get its address
   var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
   try {
     var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
     fontCollection = new PrivateFontCollection();
     fontCollection.AddMemoryFont(ptr, buffer.Length);
     return fontCollection.Families[0];
   } finally {
     // don't forget to unpin the array!
     handle.Free();
   }
 }

As you can see, I'm using GCHandle and UnsafeAddrOfPinnedArrayElement to get the IntPtr to the first element in the array. If you prefer to use unsafe blocks, go ahead, it's even shorter:

 // load font family from byte array
 public static unsafe FontFamily LoadFontFamilyUnsafe(byte[] buffer, out PrivateFontCollection fontCollection) {
   fixed (byte* ptr = buffer) {
     fontCollection = new PrivateFontCollection();
     fontCollection.AddMemoryFont(new IntPtr(ptr), buffer.Length);
     return fontCollection.Families[0];
   }
 }

For convenience we provide another overload that accepts a stream:

 // Load font family from stream
 public static FontFamily LoadFontFamily(Stream stream, out PrivateFontCollection fontCollection) {
   var buffer = new byte[stream.Length];
   stream.Read(buffer, 0, buffer.Length);
   return LoadFontFamily(buffer, out fontCollection);
 }

With those methods available we can load a Font for example from an embedded resource:

 using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Test.TheFont.ttf")) {
   PrivateFontCollection fonts;
   FontFamily family = LoadFontFamily(s, out fonts);
   Font theFont = new Font(family, 20.0f);
   //...
 }

Note: the documentation of AddMemoryFont have this to say:

To use the memory font, text on a control must be rendered with GDI+. Use the SetCompatibleTextRenderingDefault method, passing true, to set GDI+ rendering on the application, or on individual controls by setting the control's UseCompatibleTextRendering property to true. Some controls cannot be rendered with GDI+.

While I haven't noticed problems when using memory fonts even when compatible text rendering is deactivated (which is preferable), you might experience problems. In this case you have two options:

  • Enable compatibility mode using SetCompatibleTextRenderingDefault, which is not really desirable as the new GDI text rendering engine is superior to the GDI+ engine.
  • Don't use AddMemoryFont, but extract the font to the temp directory and load it from there using AddFontFile

Update 07/07/2008: It seems that you must not dipose the PrivateFontCollection before you're done with the fonts within it; otherwise your app my crash. I updated the methods above to return the PrivateFontCollection instance. The caller has to dispose the collection after he/she is done using the fonts.

HowTo: generische Listen => Lambda Ausdrücke

03.07.2008 10:32:17 | Oliver Guhr

Seit der .Net Version 3.5 gibt es für Listen und Arrays eine Reihe neuer Funktionen um die Objekte zu durchsuchen, zu sortieren und zu ordnen. Um ein “Gefühl” für die neuen Funktionen zu bekommen habe ich eine kleine Demoanwendung geschrieben um ein Paar dieser neuen Funktionen auszuprobieren.

Grundlegende Informationen zu den Lambda Ausdrücken finden man hier: http://weblogs.asp.net/scottgu/archive/2007/04/08/new-orcas-language-feature-lambda-expressions.aspx 

http://www.outofcoffeeexception.de/2008/04/28/LambdaAusdruumlcke+In+C+30.aspx

            List<Person> Employees = PersonManager.GetRandomData(19);            

            Console.WriteLine("shows all employees");
            ViewPersonList(Employees);

            Console.ReadLine();
            Console.Clear();
            Console.WriteLine("shows all Liza's (Where)");
            ViewPersonList(Employees.Where(x => x.Firstname == "Liza").ToList());

            Console.ReadLine();
            Console.Clear();
            Console.WriteLine("order's all employees by PersonalId (OrderBy)");
            ViewPersonList(Employees.OrderBy(x => x.PersonalId).ToList());

            Console.ReadLine();
            Console.Clear();
            Console.WriteLine("order's all employees by surename and firstname (OrderBy)");
            ViewPersonList(Employees.OrderBy(x => x.Surname).ThenBy(x => x.Firstname).ToList());

            Console.ReadLine();
            Console.Clear();
            Console.WriteLine("employee statistics (Min,Max,Sum,Average)");
            Console.WriteLine("the youngest employee is:\t" + Employees.Min(x => x.Age) + "\tyears old");
            Console.WriteLine("the oldest employee is:\t\t" + Employees.Max(x => x.Age) + "\tyears old");
            Console.WriteLine("all employee's are:\t\t" + Employees.Sum(x => x.Age) + "\tyears old");
            Console.WriteLine("the average age is:\t\t" + Employees.Average(x => x.Age));

            Console.ReadLine();
            Console.Clear();
            Console.WriteLine("groups's all employees by age(GroupBy)");
            Console.WriteLine("order's all groups by age(OrderBy)");
            Console.WriteLine("order's all employees in group's by surename(OrderBy)");
            IEnumerable<IGrouping<int, Person>> query = Employees.GroupBy(x => x.Age);
            foreach (IGrouping<int, Person> AgeGroup in query.OrderBy(x => x.Key ))
            {
                Console.WriteLine("----------Age:" + AgeGroup.Key + "------------------");
                ViewPersonList(AgeGroup.ToList().OrderBy(x => x.Surname).ToList());
            }
            Console.ReadLine();



Den Beispielcode gibts hier.

ShareThis

Deep Dives zu den VSX-Beispielen erschienen

03.07.2008 00:34:30 | Rainer Schuster

Ende Juni sind auf den VSX Seiten neue Artikel zu den Beispielen aus dem SDK erschienen. Istvan Novak, der Author von LearnVSXNow, in dem er diese Deep Dives populär gemacht hat, hat im Juni mit Microsoft zusammen gearbeitet um seine Artikel dafür zu erstellen. Heraus gekommen ist wieder einmal eine gute Artikelserie in Form eines Word-Dokumentes, die den Einstieg mit den SDK Beispiele in die Visual Studio Erweiterung gut ergänzt. Ken Levy persönlich - Programm Manager für das Visual Studio - hat es sich nicht nehmen lassen, diese Artikel bereit zu stellen.

DocProject - Editor für MAML

03.07.2008 00:12:20 | Rainer Schuster

DocProject hat großes vor mit seiner Roadmap. Bei Gelegenheit werde ich eine Übersetzung dieser Roadmap aus dem Blog von Dave Saxton posten. Ich bin sehr gespannt wie das Projekt in einem Jahr aussieht. Ich halte nun laufenden Kontakt mit ihm und werde ihm auch immer wieder Feedback zu DocProject geben.

Am meisten bin ich auf den MAML Editor gespannt. Ein echter WYSIWYG-Editor mit dem es einfach sein wird Konzeptuelle Hilfe zu erstellen. Im ersten Schritt ist es eine WPF-Applikation, die dann später als voll integrierbares VSpackage für das Visual Studio 2008 erscheint. Mich interessiert wie gesagt der MAML Editor brennend. Aber warum denn nur? Gibt es nicht auch noch andere Editoren? Nicht so wirklich. Gute Alternativen, auch kommerzielle, die den Anforderungen entsprechen und sich mit Sandcastle integrieren lassen gibt es nach meinen Recherchen nicht.

SHFB hat einen, der auch gut ist, aber nur 50% WYSIWYG. Wie das geht, fragt ihr euch. Es ist ein XML-Editor und per F3 lässt sich eine Vorschau der Seite rendern. In meinem nächsten Artikel zur Serie "Vom Code zur Dokumentation" der nächste Woche erscheint (hier gehts zu Teil 1.) werde ich über SHFB berichten. DocProject leistet das gleiche, was mit SHFB möglich ist. Eine Referenzdokumentation zu erstellen. Den Vorteil, den DocProject besitzt ist die Integration ins Visual Studio - Sourcecodeverwaltung inklusive. Ich hoffe Anfang kommender Woche eine den ersten Preview zu bekommen und euch hier die Funktionalität zu berichten.

“ORMs für .NET im Vergleich” – Eine kleine Kritik

02.07.2008 22:44:00 | Sebastian Jancke

Nach Monaten ohne (öffentliche) Reaktionen und einer Diskussionen auf der ALT.NET.DE Mailing-Liste will ich versuchen, meine Kritik am Artikel “Objektrelationale Mapper für .NET im Vergleich” zu formulieren. Der Artikel wurde von Holger Schwichtenberg geschrieben und in dem Magazin dotnetpro (Ausgabe 4/2008) publiziert.

Da dieser Artikel sicherlich auch von Einsteigenden gelesen wird, oder von Menschen ohne Erfahrung speziell mit NHibernate, finde ich es wichtig, dass keine Halb-Wahrheiten stehen bleiben. Dieser Eintrag soll ein Versuch sein, mit einigen Fehlern und Missverständnisse im besagten Artikel aufzuräumen. Die Aufstellung ist sicherlich nicht komplett und ich maße mir auch keinerlei Wertung über den Artikel oder das Magazin oder den Autor an.

(Abschnitt: Henne oder Ei) Beim Forward Mapping machen NHibernate und das ADO.NET Entity Framework bislang nicht mit; sie können eine Datenbank nicht auf Basis von Geschäftsobjekten generieren.

Ich zitiere dazu (frei) aus der NHibernate Dokumentation, Abschnitt 3.5 “Optional configuration properties”: Die Eigenschaft “hibernate.hbm2ddl.auto” bestimmt, ob das DDL Schema automatisch bei Erzeugung der ISessionFactory exportiert werden soll. Mögliche Werte sind unter anderem “create” und “drop”. Mit “create-drop” wird das Datenbank-Schema zunächst erzeugt und beim Schließen der ISessionFactory dann gelöscht. Ich kann jedem nur empfehlen, diese Optionen auszuprobieren. Bisher habe ich sie auf Entwicklungs-Datenbanken erfolgreich nutzen können, um meine Produktivität in diesem Bereich zu steigern. Gleichzeitig halte ich eine Kopie der bisher ausgelieferten Datenbank vor. Mit Werkzeugen wie “Quest Comparison Suite” oder “RedGate SqlCompare” kann ich dann zum Ende einer Iteration die Änderungen automatisiert in ein Script übertragen lassen.

(Abschnitt: Geschäftsobjektklassen) Die Fachwelt spricht dann von Persistance Ignorance oder von Plain Old CLR Objects (POCOs). In diese Kategorie fällt NHibernate. NDO kann POCOs als Unterobjekte eines zu persistierenden Objekts verwenden.

Hierbei geht wohl die eigentliche Bedeutung des Wortes “POCO” verloren. Frei nach Fowler ist ein POCO ein “Plain Old CLR Object”. Also ein Objekt, das sonst keine Abhängigkeiten und Einschränkungen hat. Vererbung ist vielleicht die stärkste Form der Abhängigkeit. Durch Vererbung von einer zentralen Basisklasse meines Persistenz-Frameworks gehe ich somit eine sehr starke Abhängigkeit ein. Das Objekt ist somit kein POCO mehr. Die Begriffe POJO und POCO implizieren damit im Bezug auf Persistenz auch direkt einen Teil des Begriffes “Persistence Ignorance”. NDO ist somit nicht PI-fähig und die Geschäftsobjekte in diesem Zusammenhang sind keine POCOs.

(Abschnitt: Mapping und Treiber) Nicht alle Werkzeuge verwenden im Untergrund ADO.NET. VOA und NHibernate arbeiten mit JDBC (Java Database Connectivity), da es sich um Portierungen aus der Java-Welt handelt.

Dazu möchte ich wieder die NHibernate Dokumentation frei zitieren, im speziellen den Abschnitt 2 “Architecture”: NHibernate kann grundsätzlich auf zwei Arten eingesetzt werden: Entweder die Anwendung stellt NHibernate die entsprechenden ADO.NET Verbindungen zur Verfügung, oder aber NHibernate schirmt die Anwendung von der gesamten Datenbank-Kommunikation ab. Wie man in diesem Fall an der entsprechenden Grafik ("full cream" architecture) sehen kann, liegt auch dann ADO.NET (neben OLE DB und ODBC) zu Grunde. Noch deutlicher ist die Beschreibung des Interfaces ISession: “… kapselt eine ADO.NET Verbindung”.

 

Abschließend möchte ich noch einige Worte zum Fazit des Autors verlieren. Herr Schwichtenberg legt in seinem Vergleich anscheinend sehr viel Wert auf die Unterstützung mittels grafischer Werkzeuge. Ich kann diese Wahl der Kriterien nicht teilen und auch nicht unterstützen. Für Einsteigende mögen grafische Werkzeuge am Anfang sehr eingängig und leicht zu erlernen sein. Ich bezweifle allerdings, dass die “Dekoration” der Geschäftsobjekte mit Attributen schwieriger ist. Dies ist zwar kein “best practice” – da wir die Persistence Ignorate verlieren – ist aber für den Einstieg akzeptabel.

Schwerer wiegt für mich aber noch das Argument der Skalierbarkeit solcher grafischer Werkzeuge. Dazu reicht meist schon der Versuch, eine “mittelgroße” Anwendung (vielleicht 2-3 Mannmonate Aufwand?) in solch einem grafischen Werkzeug darzustellen. Hierbei scheitert derzeit vor allem der Entity Framework Designer – es werden schlicht alle Objekte auf der gesamten “Leinwand” dargestellt (Scott Allen - Visual Designers Don’t Scale). Auch mit Zoom-Funktion, Gruppierungen und Ausschnitte bleibt immer die Tatsache, das visuelle Werkzeuge in der Regel (bisher) einfach schlecht mit der Größe des Domänen-Modells skalieren.

Technorati Tags: ,

ASP.NET/ASP MVP Award

02.07.2008 09:35:40 | Robert Mühsig

Gestern hat mich folgende freudige Email erreicht:

Sehr geehrte(r) Robert Mühsig,
Herzlichen Glückwunsch! Wir freuen uns, Ihnen den Microsoft® MVP Award 2008 verleihen zu können. Mit dem MVP Award danken wir Ihnen für Ihren Einsatz für die Community, mit dem Sie Tag für Tag dazu beitragen, das Leben der Menschen zu bereichern und die Branche erfolgreicher zu machen. Wir schätzen Ihren außerordentlich bedeutenden Beitrag in den technischen Communities zum Thema Microsoft ASP/ASP.NET im vergangenen Jahr hoch ein.

Ich freu mich, dass meine Bemühungen nicht ganz ungesehen geblieben sind und bedanke mich bei denen, welche mich nominiert haben :)

Notiz am Rande: Wer nicht weiß, was ein MVP ist, dann einfach mal hier nachschauen.

Weiter geht die Reise in die Tiefen von ASP.NET (MVC ;) ) und co. :)

ShareThis

Woohoo! Linerider in Silverlight 2

01.07.2008 16:33:00 | Steffen Ritter

Pünktlich zu Neujahr (das MS-Fiskaljahr endete gestern, am 30. Juni) ist eines der coolsten Onlinespiele in neuer verbesserter Fassung in Silverlight verfügbar: http://linerider.com/play-line-rider-online

Linerider

Wer Linerider noch nicht kennt möge sich eine Stunde in die Ecke stellen oder besser umgehend die Website besuchen und loszeichnen…

Ich glaube ich werde heute keine weitere Arbeit mehr erledigen können, muss das schließlich testen :)...

Buchempfehlung: Foundation of Programming (eBook)

01.07.2008 14:52:12 | Robert Mühsig

Durch Thomas und Alex wurde ich auf dieses sehr interessante kostenlose eBook aufmerksam: Foundation of Programming von Karl Seguin

Auf knapp 80 Seiten werden diese Themen angeschnitten:

  • ALT.NET
  • Domain Driven Design
  • Persistence
  • Dependency Injection
  • Unit Testing
  • Object Relational Mappers
  • Basics Memory, Exceptions und Proxies

(auch wenn Thomas und Alex es schon gebloggt haben - sowas sollte doch nicht untergehen ;) )

ShareThis

Sun.NET bei der .net user group Köln am 08. Juli 2008

01.07.2008 14:49:29 | Albert Weinert

Das Treffen der .net user Group Köln wird am Dienstag, den 8. Juli 2008 um 19.00 Uhr auf dem Sky Beach Köln, Cäcillienstrasse 32, Köln stattfinden.

Diesmal geht’s rund um Sun.NET

Am Strand Wie jedes Jahr in den Sommerferien machen wir ein außerordentliches UserTreffen. Wo wir uns treffen, was essen, diskutieren rund um .NET , Softwareentwicklung sowie Ideen wir man bestimmte Herausforderungen im Entwickler Alltag bewältigen kann. Ein intensiver Gedankenaustausch ohne Themenvorgabe und Vorträge.

Eingeladen ist jeder der kommen möchte ob sie/er schon bei einem Usertreffen war oder nicht.
Da wir nun Sommer haben gehen wir diesmal an den Strand. Sonnenstühle und Schirme sind vor Ort, für Sonnencreme muss jeder selber sorgen.

http://www.skybeach.de

Getränke und kleinere Snacks kann man dort auch erwerben.

Parken kann man im darunterliegenden ARAL Parkhaus, mit der Bahn fährt man am besten bis Neumarkt und geht dann Richtung Deutzer Brücke. Den Sky Beach erreicht man vom ARAL Parkhaus mit dem Aufzug in die 6. Etage.

Ab 19 Uhr geht’s los, bitte meldet euch an damit wir entsprechende Stühle mit dem Handtuch reservieren können.

Anmeldung über http://www.dnug-koeln.de/treffen/infos/ oder XING

Technorati-Tags: ,,

Das Bild ist unter CC von http://www.flickr.com/photos/photophob/

Regeln | Impressum