Dienstag, September 29, 2009

Vorbereitung auf VS 2010

Boah, im aktuellen MSDN-Magazin gibt es massig Artikel zu Parallelisierung in VS 2010. Die Artikel sind sehr interessant und beschreiben ausführlich die Parallel Library (PL). Die Parallel Library gab es zum Glück schon für VS 2008 als Beta. Leider wird es diese nicht in der Finalen Version geben, meine Erfahrungen mit PL waren zu meist positiv. Für .NET 4 kann man diese einfach nur empfehlen, so einfach kann man Applikationen einfach nicht Parallelisieren.

Hier noch mal die Artikel:

Gibt noch viele andere Artikel, ein guter Einstiegspunkt ist das Parallel Computing Developer Center in der MSDN.

Montag, September 21, 2009

My own hypervisor

Wow, ich habe es endlich mal geschafft mir einen eigenen Hypervisor zu installieren. Als Entwickler hat man immer mal den Wunsch etwas Neues auszuprobieren. Neues probiere ich meistens in einer VM aus und auch ein großteil meiner Entwicklungsarbeit läuft in VMs. Wir nutzen auf Arbeit fast ausschließlich VMWare Produkte, d.h. ESX-Server und Workstations, auch Player. Daher habe ich für mich entschieden einen ESXi zu installieren, da dieser dann auch super mit meiner Arbeitsumgebung zusammenarbeitet und ich mir keine Sorgen um eine Windows-Lizenz machen muss. Zu dem gehört der ESX(i) mit sicherheit immer noch zur Besten Software auf dem Gebiet.

Nunja mein erster Installationsversuch scheiterte kläglich, ich bekam immer die Meldung “Failed to load lvm driver”. Ich dachte es handelt sich um ein Problem mit den SATA-Controllern auf meinem Board. Zum Glück kennt Google fast alles und so half etwas Googlen schon zu Einschränkungen des Problems. Die meisten mit einem ähnlichen Problem konnten es auf die Netzwerkkarte einschränken. Dann habe ich doch mal den Hardware Compatibility Guide zum ESXi bei VMWare aufgesucht. Oha, Board ist nicht in der Liste, hmm. Probieren wir es einfach mit einer Netzwerkkartenempfehlung. Also 4 Tage später, als mal etwas Zeit war in den Store gegangen und eine “Intel Pro 1000 GT” besorgt. Aufgrund des Guides habe ich wenig Hoffnung für die Installation gehabt. Allerdings oh Wunder nicht mal 10min später Stand das System. Für das Management soll man den VMware vSphere Client benutzen, der Client kommt mir dank VMWare Infrastructure bereits recht bekannt vor.

Am Wochenende habe ich dann noch einige VMs auf den Server geschoben, um endlich auch zu checken, wie das ganze nun läuft. Ohne besondere Investitionen in super performante Hardware läuft es sehr gut. Ich glaube, mein System ist über 3 Jahre alt und hat nur wenig Upgrades (Platte, Speicher, Netzwerkkarte) erhalten. Freue mich schon auf die nächsten VM mit denen ich arbeiten werde. :)

Sonntag, September 13, 2009

Endlich mal Zeit zum Lesen

Dieses Wochenende, genauer Sonntag, habe ich mir endlich mal die Zeit genommen einige Artikel von Patrick Smacchia zu lesen. Ich habe mir schon seit über einem Monat vorgenommen mich intensiv mit NDepend auseinander zu setzen. Mit dem Lesen der interessanten Artikel komme ich so wenigstens einen Schritt voran.

Die letzten Wochen waren sehr arbeitsreich, allerdings weniger am Computer, sondern viel mehr körperliche Arbeit. Da diese nun aber zum Großteil vorbei ist, kann ich mich wieder auf die IT-Themen konzentrieren. Am liebsten möchte ich alles machen; WPF, WCF, Silverlight, MVC, JQuery, Build Management, Entity Framework, ESXi, …. Ich muss mich doch mal wieder etwas mehr auf weniger Themen fokussieren. Dann sollten in nächster Zeit wieder mehr Blog-Einträge entstehen.

Sonntag, Juli 26, 2009

NDepend – Analyze code quality

Ich bin nun seit fast einem Monat stolzer Besitzern einer NDepend Lizenz. Leider komme ich bisher kaum dazu mal richtig mit den Ergebnissen mich auseinanderzusetzen. Cool ist es Grafisch die Abhängigkeiten zu sehen und auch zu verfolgen. Man sieht nicht nur die Abhängigkeiten, sondern auch eine vielzahl von Code-Metriken, wie zum Beispiel die zyklomatische Komplexität. Gelungen ist auch die Abfrage der Metriken mittels eine Code Query Language. CQL ist dem SQL nach empfunden und auch nach etwas lesen der vorhandenen Abfragen verständlich. Es ist auch eigentlich immer das einfachste vorhandene Queries anzupassen. Ich habe mir für die Arbeit mal einige Einführungen zusammengesucht mittels dem ich besser das Vorgehen für Refactoring lernen will. Auf der NDepend Seite gibt es zudem auch einige Webcasts, leider ohne Sprache, die das Vorgehen ebenfalls erklären.

Ach grundsätzlich ist es sehr hilfreich nach Blog-Posts oder Artikel von Patrick Smacchia, übrigens der Lead-Developer von NDepend. Zudem gibt es noch eine schicke Übersicht zu den Metriken von Scott Hanselman http://www.hanselman.com/blog/content/binary/NDepend%20metrics%20placemats%201.1.pdf.

In den Projekten muss leider immer erst der ”Need”, meist von außen, für das verstärkte Monitoring von Code Metriken und das anschließende verbessern des Codes entstehen. Ich hoffe mal, dass ich irgendwie mehr dazu kommen zumindest meinen Code mal zu optimieren, fals es sinnvoll ist. Nun erstmal möglichst viel lesen.

Sonntag, Juli 19, 2009

SharePoint (Web services) - MSBuild

Ich habe mich seit Freitag, hautpsächlich am Freitag, mal mit der Integration von MSBuilds und SharePoint Webservices beschäftigt. Ich möchte bei einem Integration-Build möglichst ohne Aufwände Daten in einer Sharepoint-Liste aktualisieren, oder auch mal neue Daten anhängen. Beim bingen nach einer out-of-the-box Lösung bin ich leider ins Leere gelaufen, daher der eigene Ansatz – Erstellen eines MSBuild Tasks.

Wer mit SharePoint über die Webservices arbeiten will, der sollte sehr fit mit Xml sein. Die Webservices arbeiten ausschließlich mit Xml Eingaben zur Steuerung. Mit den Beispielen in der MSDN kommt man gut voran, daher kann man sich schnell in die Webservice-Schnittstelle(n) einarbeiten.

Ergebnis der “Session” sind 3 MSBuild-Tasks um mit Listen im SharePoint zu interagieren. Den Source Code dafür gibt es hier: http://www.zieschang-jan.de/documents/MsBuildExtensions.zip. Ob ich den günstigsten Weg für die Abbildung der Eigenschaft für den Aufruf gewählt habe, weiß ich selber nicht so richtig, aber mir ist kein besser Weg dazu eingefallen.

Einrichtung

Um die Tasks nutzen zu können muss das Projekt compiliert werden, anschließend sind im Ausgabe-Verzeichnis  4 Dateien vorhanden. Es gibt nun 2 Möglichkeiten die Tasks zu nutzen:

  1. Kopieren der DLL und der “*.target”-Datei in den MSBuildExtensionsPath, welcher normalerweise “%programfiles%\MSBuild” ist. Anschließend im Import das Target-File referenzieren <Import Project="$(MSBuildExtensionsPath)\CapeVision.MsBuildExtensions.targets"/>
  2. Die Build-Dateien sollen lokal für das Projekt genutzt werden ohne globale Verfügbarkeit. In diesem Fall ist muss die Referenz für das Target angepasst werden, wie das geht ist im Sample.msbuild im Zip gezeigt.

Usage im MSBuild

Einbindung der Tasks:

   1: <PropertyGroup>
   2:   <CapevisionMsBuildExtensionsTasksPath>.</CapevisionMsBuildExtensionsTasksPath>
   3:   <SharePoint>http://sdc-srv-moss/SiteDirectory/mcc/</SharePoint>
   4:   <List>Bücher</List>
   5: </PropertyGroup>
   6: <Import Project="$(MSBuildExtensionsPath)\CapeVision.MsBuildExtensions.targets"/>

Abrufen alle Listen der SharePoint Site

Dazu muss die Url der SharePoint Site übergeben werden, anschließend werden alle Libraries (Bibliotheken) der Site zurückgegeben. Die Rückgabe kann mittels des Output-Parameters abgefragt werden und anschließend wie andere TaskItems behandelt werden. Das Item verfügt über die folgendenen MetaDaten:

  • ID
  • Title
  • Description
  • Modified
  • Created
  • ItemCount
  • EnabledAttachments
   1: <SharepointListGetListsTask SharePointUrl="$(SharePoint)">
   2:   <Output TaskParameter="Libraries" ItemName="myItems"/>      
   3: </SharepointListGetListsTask>
   4: <Message Text="@(myItems)"/>

Hinzufügen eines neuen Eintrages

Zum Anlegen eines Eintrages werden die Listen Felder und die Werte für diese Einträge benötigt. Beides wird als Items an den Task übergeben. Da ein Item benötigt wird und dies mit den Standardmitteln immer eine Datei erfodert wird im Beispiel immer das Build-Script selber referenziert. Die Spalten und die Werte müssen von der Anzahl übereinstimmen und werden entsprechend der Reihenfolge ausgewertet. Zusätzlich wird der Name der Liste benötigt, in die die Daten eingefügt werden.

   1: <ItemGroup>
   2:   <!-- Include schould result in only one file, item requires file present!-->
   3: <ListFields Include="Sample.msbuild">
   4:   <Name>Title</Name>
   5: </ListFields>
   6: <ListFields Include="Sample.msbuild">
   7:   <Name>Description</Name>
   8: </ListFields>
   9: <Add Include="Sample.msbuild">
  10:   <Name>New MsBuild Script Item</Name>
  11: </Add>
  12: <Add Include="Sample.msbuild">
  13:   <Name>This item was inserted by a script/Name>
  14: </Add>
  15: </ItemGroup>
  16: <SharepointListAddTask SharePointUrl="$(SharePoint)" 
  17:                        ListName="$(List)" ListFields="@(ListFields)"
  18:                        Values="@(Add)"/>

Ändern eines Eintrages

Am kompliziertesten ist das Ändern eines Eintrages, es muss zu den Felder und den Werten noch eine Bedingung übergeben werden, welche Einräge zu ändern sind. Aber an sich doch dann selbstsprechend, oder? Es gibt eine Einschränkung, maximal werden 200 Elemente geändert, weil nur 200 mit der Bedingung geladen werden. Die Bedingung kann sich aus mehreren Einträgen zusammensetzen, diese werden mit UND verbunden. Bei Condition könnte noch ein “operator” mitgegeben werden, der definiert, wie der Vergleich auszuführen ist. Der Standard-Operator ist “Eq”, also Gleichheit.

   1: <ItemGroup>
   2:   <!-- Include schould result in only one file, item requires file present!-->
   3:   <ListFields Include="Sample.msbuild">
   4:     <Name>Title</Name>
   5:   </ListFields>
   6:   <Update Include="Sample.msbuild">
   7:     <Name>Updated MsBuild Script Item</Name>
   8:   </Update>
   9:   <Condition Include="Sample.msbuild">
  10:     <field>Title</field>
  11:     <value>New MsBuild Script Item</value>
  12:   </Condition>
  13: </ItemGroup>
  14: <SharePointListUpdateTask SharePointUrl="$(SharePoint)"
  15:                        ListName="$(List)" ListFields="@(ListFields)"
  16:                        Values="@(Update)"
  17:                        QueryCondition="@(Condition)"/>

TODOs

  • Momentan kann man keine ListItems abfragen, dass muss ich definitiv noch für MSBuild nach ausenlegen. Eigentlich ist der Code bereits vorhanden, denn beim Update werden die Einträge für die Referenzierung (ID) benötigt.
  • Ich werde die Abfragesprache für das Updaten und Laden von Items auf SQL-like Queries umstellen. Dazu gibt es bereits ein interessantes Projekt auf Codeplex http://yacamlqt.codeplex.com/. Die Integration sollte auch nicht so schwer sein.

Dienstag, Juni 30, 2009

Improving .NET Application Performance

Ich habe lange überlegt, ob ich zu diesem Thema etwas schreibe, schließlich gibt es schon 100er von Blogs, die das Thema behandeln. Letztendlich habe ich mich doch dazu entschlossen, um einfach mal eine kleine Link-Sammlung zu dem Thema zu haben.

Den Post habe ich Anfang Mai angefangen, in der Zwischenzeit ist es schon fast Juli. Das Thema ist so umfangreich und jeden Tag kommt mehr zu dem Thema. Das letzte ist die Google Initiative “Let's make the web faster”. Das Thema passt zum Glück aber nicht ganz in diesen Teil rein. Eigentlich brauche ich zum Thema Web-App-Performance nichts mehr schreiben, Google hat alles zusammengetragen, was dazu gehört.

Bevor mit der Optimierung einer Anwendung begonnen wird, sollte man sich Ziele überlegen, die zuerreichen sind. Das soll einfach dazu dienen, dass man nicht das Projektbudget unnötig verbraucht. Außerdem sollte man unbedingt man die wesentlichen Kapitel im “Improving .NET Application Performance and Scalability”-Guide lesen, im Kapitel “Performance Best Practices at a Glance” geht es um das allgemeine Vorgehen zur Optimierung von Anwendungen. Mindestens ebenso Hilfreich ist der Pattern & Practice Guidance Explorer, hier sind viele Guides, Checklisten, …. Es ist sehr schwer Anwendungen zu optimieren, wenn das Anwendungsdesign nicht schon viele Prinzipien berücksichtigt hat. Dennoch lässt sich meistens einiges herausholen, allerdings sollte man vor jeder Optimierung MESSEN, anschließend die Optimierung durchführen und erneut Messen. Ach und natürlich die Unit-Tests erneut ausführen, um nicht ein fehlerhaftes Verhalten als Optimierung zu bezeichnen.

Ich hatte dievor einigen Wochen einige interessante Blog-Artikel zu Assemblies und Namespaces gelesen. Die meisten Verbinden mit Komponenten separate Assemblies, allerdings sollte jedem bewußtsein, dass mit zusätzlichen Assemblies die Ladezeiten steigen. (Sicherheitscheck, Auflösungen, …) Hier einer der Links: Advices on partitioning code through .NET assemblies - Patrick Smacchia

Im Blog von Rico Mariani kann man sehr viele nützliche Tips zu Performance finden, wer sich mit dem Thema beschäftigt, sollte sich das mal anschauen.

Loops

Jede Anwendung verfügt über Schleifen, zumindest kenne ich keine außer “Hello World” ohne Schleifen. In Abhängigkeit von der Anzahl und Häufigkeit der Schleifendruchläufe kann hier eine Optimierung der Anwendung auf die Sprünge helfen. In dem Blog Post von Patrick Smacchia “An easy and efficient way to improve net code performance” geht es auch genau um das Thema Loops und deren Einfluss auf die Performance. Für die Performance sind For-Schleifen günstiger, vor allem auf Array oder Listen. Foreach-Schleifen sind durch den IEnumerator/IEnumerable-Aufwand teurer. Hier muss bei jedem durchlauf IEnumerator.MoveNext und IEnumerator.Current aufgerufen werden.

Beim Refactoring von Schleifen sollte man zusätzliche darauf achten, dass nicht der Inhalt der Schleife in eine neue Methode gepackt wird, sondern ebenso die Schleife. Der Overhead, der beim Aurfuf einer Methode entsteht, multipliziert sich in einer Schleife. Mein folgendes Beispiel ist nicht sehr sinnvoll, aber verdeutlicht hoffentlich das Problem.

Ausgang:

   1: private void Loop()
   2: {
   3:     long sum = 1;
   4:     for (int i = 0; i < 10000; i++)
   5:     {
   6:         sum = i*2;
   7:         sum -= i;
   8:     }
   9:     Debug.WriteLine("Resut=" + sum);
  10: }

Ungünstige Optimierung:

   1: private void Loop()
   2: {
   3:     long sum = 1;
   4:     for (int i = 0; i < 10000; i++)
   5:     {
   6:         sum = GetSum(i);
   7:     }
   8:     Debug.WriteLine("Resut=" + sum);
   9: }
  10: private long GetSum(int i)
  11: {
  12:     long sum;
  13:     sum = i*2;
  14:     sum -= i;
  15:     return sum;
  16: }

Besser:

   1: private void Loop()
   2: {
   3:     long sum = 1;
   4:     sum = GetSum(sum);
   5:     Debug.WriteLine("Resut=" + sum);
   6: }
   7: private long GetSum(long sum)
   8: {
   9:     for (int i = 0; i < 10000; i++)
  10:     {
  11:         sum = i*2;
  12:         sum -= i;
  13:     }
  14:     return sum;
  15: }

Hier noch ein Post zur Laufzeit von verschiedenen Loops.

Casting

Dank Generics kann man das “Cast”en sein lassen. Ich casts sind nicht merkbar “teuer”, das Boxing von ValueTypes kostet einige CPU-Cycles. Allerdings kann das Boxing zu Laufzeitfehlern führen, die der Compiler mittels Generics bereits hätte prüfen könne. Ich verwende massig Generics, allerdings finde ich die Regel keine Generics als Public Properties oder Rückgaben zu verwenden, BLÖD.

Threading

… ist eins meiner Lieblingsthemen, leider kommt dieses Thema aber immer zu kurz und bei der Leistungsoptimierung von Anwendungen wird es oft verkannt. Durch den Einsatz von Threading kann man sich viele Probleme in einer Anwendung schaffen, zu dem kann nicht jede Anwendung mit Threading sinnvoll ausgestattet werden. Um die Probleme mit dem Threading zu reduzieren/vereinfachen, gibt es von Microsoft die Parallel Extensions. Es ist so einfach damit Anwendungen zu Parallelisieren und das auch  noch sehr performant. Die Parallel Extensions machen den Rechner nicht mit Threads zu, sondern verfügen über ein intelligentes Thread-Management bei dem eine Anzahl Arbeitsthreads, abhängig vom Prozessor, immer wieder mit den eigentlichen Aufgaben gefüllt wird. Einen Nachteil haben die Parallel Extensions allerdings, es gibt sie nur für Framework 4.0 und für das Framework 3.5 leider nur als CTP.

Eine andere Art Threading effektiv einzusetzen, ist System.Threading.ThreadPool.QueueUserWorkItem. Besonders effektiv ist der Einsatz in Client-Anwendungen, um das User-Interface während langen Operationen verfügbar zu belassen “Responsiveness”. Für Server-Anwendungen muss man etwas aufpassen bei dieser Variante, da der Standard .NET-ThreadPool auch für ASP.NET verwendet wird (evtl. auch WCF?), somit kann es passieren, dass der Server Anfragen ablehnt, weil zu viele Nutzertasks aktiv sind. Es gibt sogar ein schlimmeres Szenario, wenn der Server selber Web-Anfragen ausführt, dann kann es zu Deadlocks kommen. Also dabei etwas aufpassen.

Linq 2 Sql

Compiled Queries sind in Linq 2 Sql der weg um Abfragen zu beschleunigen, dabei wird bei der ersten Ausführung der Anfrage diese in .NET Code generiert und das Füllen der Objekte bei Folgeanfragen deutlich beschleunigt. Von mir gibt es dazu aber kein Sample-Code, es gibt dazu schon massig Infos im Web. Hier nur 2 ausgewählte Adressen:

LINQ to SQL - compiled queries

Solving common problems with Compiled Queries in Linq to Sql for high demand ASP.NET websites

In meinem aktuellen Projekt konnten wir durch den Einsatz von Compiled Queries an gezielten Stellen richtig viel sparen. Ich kann die gesparte Zeit nicht quantifizieren, aber beim Profilen hat man deutlich gesehen, dass alle Datenabrufe um ein vielfaches schneller waren.

Network IO

Ein massgeblicher Einflussfaktor bei vielen modernen Anwendungen ist der Netzwerkverkehr. Grundsätzlich gibt es 2 Regeln für den  Netzwerkverkehr

  1. so wenig wie möglich an Daten transportieren
  2. möglichst viel mit einem Rutsch, wenige Roundtrips (Chatty Interfaces)

Grundsätzlich sollte man bei allen Netzwerkstrecken prüfen, ob nicht die Datenmenge mittels Komprimierung reduziert und so effektiver übertragen werden kann.

Im IIS 6 kann die eingebaute Komprimeriung mittels der folgenden Zeilen aktiviert werden:

   1: cscript c:\Inetpub\AdminScripts\adsutil.vbs set w3svc/filters/compression/parameters/HcDoDynamicCompression true
   2: cscript c:\Inetpub\AdminScripts\adsutil.vbs set w3svc/filters/compression/GZIP/HcScriptFileExtensions asmx dll asp aspx ashx exe svc
   3: cscript c:\Inetpub\AdminScripts\adsutil.vbs set w3svc/filters/compression/Deflate/HcScriptFileExtensions asmx dll asp aspx ashx exe svc
   4: cscript c:\Inetpub\AdminScripts\adsutil.vbs set w3svc/filters/compression/parameters/HcDoStaticCompression true
   5: cscript c:\Inetpub\AdminScripts\adsutil.vbs set w3svc/filters/compression/GZIP/HcFileExtensions html htm css js xml xslt xsd log
   6: cscript c:\Inetpub\AdminScripts\adsutil.vbs set w3svc/filters/compression/Deflate/HcFileExtensions htm css js xml xslt xsd log
   7: iisreset

Wahrscheinlich funktionieren die Zeilen auch im IIS7, das habe ich allerdings noch nicht geprüft. Hier kann die Komprimierung auf jeden Fall leicht auch per Webinterface aktiviert werden.

Für WCF sollte man sich mal das WCF Extension Projekt auf Codeplex ansehen.

WCF Services

WCF ist schon eine Weile mein Nummer eins Thema. Es macht jedes mal Spass mit WCF zu spielen, klar gibt es öfter auch mal Wutanfälle, meistens ist nicht WCF schuld. Eine Sache, bei der WCF einfach schwierig ist, ist das saubere Abräumen/Aufräumen. In WCF-Service Proxies verhält sich die Dispose-Methode nicht, wie in anderen IDisposable-Komponenten, WCF-Service Proxies werfen beim Dispose eine Exception, wenn der Client im Faulted-State ist. Daher sollte man nicht das Using-Pattern implementieren, sondern sollte den Proxy gezielt schließen, wenn es zu einem Fehler kommt, dann sollte man die Kommunikation mittels Abort() abrechen. Eine nette Implementierung für dieses Problem kann man bei Erwyn van der Meer “WCF Service Proxy Helper” sehen. (weiterer Artikel zum Disposing: Disposing a WCF Proxy)

Ich bin ein absoluter verfechter der strikten Entities, damit meine ich alle WCF-Entities, die explicit mittels DataContract markiert werden. Durch das explizite Markieren werden keine ungewollten/unnötigen Daten transportiert. Man kann so außerdem etwas Einfluss auf die Datenmenge nehmen, in dem Standardwerte nicht transportiert werden. Mittels der Eigenschaft EmitDefaultValue kann genau dieses Verhalten verändert werden. WCF ist an sich außerdem sehr schlau, was die Serialisierung und Transport von Daten betrifft. Dies trifft vor allem auf properitären Protokolle mit dem BinaryFormatter zu. Daher würde ich immer empfehlen auch mindestens ein NET-TCP-Binding Endpoint bereitzustellen, so dass .NET Applikation von den internen Mechanismen stark profitieren können.

GC (Garbage Collection)

Kann man am GC etwas optimieren? Nicht viel, es gibt aber eine Einstellung, die die Anwendung positiv beeinflussen können. Man kann sich in jeder Anwendung entscheiden, ob man Workstation GC oder Server GC benutzt. Der Unterschied von beiden GCs ist nur maginal, bei der Server GC findet das Aufräumen in einem eigenen Thread pro CPU statt. Es gibt wahrscheinlich noch weitere Unterschiede, aber das ist der, den ich mir auch merken kann. Man kann den Server GC leicht mittels des Konfigurationsblocks anlegen:

   1: <configuration>
   2:     <runtime>
   3:         <gcServer enabled=“true"/>
   4:     </runtime>
   5: </configuration>

Hier noch ein schöner Artikel zu dem Thema: http://blogs.msdn.com/maoni/archive/2004/09/25/234273.aspx

Memory consumption

Irgendwas wollte ich hier noch reinpacken, wenn es mir wieder einfällt, dann trage ich es nach.

Dienstag, Juni 16, 2009

ASP.NET MVC Performance

Ich bin gerade beim “Bing”en auf eine super Präsentation zum Thema Performance mit ASP.NET MVC gestoßen. Ich kann die Folien nur empfehlen. Auf SlideShare kann man sich die Folien unter http://www.slideshare.net/rudib/aspnet-mvc-performance anschauen oder auch herunterladen. Es geht auch kurz um Omptimierungen in Linq-Queries.

Vielleicht sollte ich auch mal an dem fast 2 Monate alten Artikel zum Thema Performance weiterschreiben, …

Sonntag, April 26, 2009

Linq To Sql – Eager(/Lazy) Loading mit Einschränkungen

Die Woche haben wir im Projekt mal wieder eine Sonderaufgabe für Linq To Sql gefunden, aber dazu komme ich noch.

Wir nutzen intensiv Linq To Sql allerdings machen wir kein Lazy Loading, sondern laden unsere benötigten Objekte komplett (eager loading). Das kann man am einfachsten mit der LoadWith-Option in Linq to Sql machen.

   1: var query = from m in    db.ChildEntities
   2:             select m;
   3: var options = new DataLoadOptions();
   4: options.LoadWith<ChildEntities>(m=>m.ParentEntity);
   5: options.LoadWith<ChildEntities>(m=>m.ChildChildEntities);
   6: db.LoadOptions = options;

Diese Variante nutzen wir fast überall, allerdings haben wir zum Teil den Wunsch die Daten der Child-Entities einzuschränken. LoadWith würde alle verknüpften Elemente sofort mit laden, manchmal benötigt man nur einen bestimmten Teil der Daten. Somit macht es aus dem Performance-Gesichtspunkt und der Ressourcen-Optimierung viel Sinn nur die erforderlichen Daten zu laden. Durch AssociateWith kann man eine Einschränkung für das Laden der Daten definieren.

   1: var query = from m in    db.ChildEntities
   2:             select m;
   3: var options = new DataLoadOptions();
   4: options.AssociateWith<ChildEntities>(m=>m.ChildChildEntity.Where(c=c.Date > DateTime.UtcNow.AddDays(-60)));
   5: options.LoadWith<ChildEntities>(m=>m.ParentEntity);
   6: options.LoadWith<ChildEntities>(m=>m.ChildChildEntities);
   7: db.LoadOptions = options;

Wie schon gesagt, kann man auf die LoadWith definition verzichten, in dem Fall hätte man das Lazy Loading eingeschränkt. Eine super Einführung in Linq To Sql Eager/Lazy Loading hat Hilton Giesenow in seinem Blog geschrieben.

Nun kam aber der zweite Teil der etwas Tricky war. Wir wollten nun mehrere Einschränkungen setzen. Eigentlich eine typische “In” Anweisung in einem SQL-Statement. Bei uns scheiterten die Versuche immer wieder, da half nur noch alles einen Tag liegen zu lassen und nächsten Tag sich das Problem noch mal anzuschauen. Schließlich muss es gehen, wir haben in Anfragen bereits an anderen Stellen mit Linq To Sql ausgeführt. Letztendlich hat uns der “Suchanbieter unseres Vertrauens” (Google) zu der Lösung des Problems geführt. John Liu hatte ein ähnliches Problem und es einfach in einem kurzen Beispiel dargestellt. Man darf nicht mit IList bei der Übergabe arbeiten, sondern muss IEnumerable-Interface nutzen.

   1: var query = from m in    db.ChildEntities
   2:             select m;
   3: var options = new DataLoadOptions();
   4: options.AssociateWith<ChildEntities>(m=>en.Contains(m.ChildChildEntity.Date));
   5: options.LoadWith<ChildEntities>(m=>m.ParentEntity);
   6: options.LoadWith<ChildEntities>(m=>m.ChildChildEntities);
   7: db.LoadOptions = options;

Ich habe gestern Abend mal versucht eine Umwandlung der Statements in Linq To Entities gegoogled. Am Besten waren die Anweisungen auf dem ADO.NET-Team Blog, leider habe ich für die AssociateWith-Anweisung aber keine Entsprechung gefunden.