Ja, ich gebe es offen und ehrlich zu! Ich habe sie noch nie wirklich genutzt, die ASP.NET WebForms ObjectDataSource. Irgendwie hatte ich ein Unbehagen bei der Vorstellung mir das alles bloß zusammen zu klicken und habe daher bisher einen Bogen um die ObjectDataSource gemacht. Außerdem habe ich bisher auch noch niemanden getroffen, der ernsthaft in Erwägung gezogen hätte die ObjectDataSource zu nutzen - oder sich zumindest getraut hätte, dies zuzugeben ;-)

Kürzlich war es dann aber soweit. meine Für eine kleine Demo startete ich damit, die serverseitige ASP.NET MVC Implementierung meiner Beispielanwendung für meinen jQuery Vortrag auf der dotnet Colgone nach Webforms zu konvertieren. Mein Ziel war es dabei, wo immer es nur geht, den WebForms "Baukasten" zu nehmen. Da ich bereits einen bestehenden Business Service hatte, der mir meine Objekte laden und persistieren konnte, kreuzte sie nun also meinen Weg, die ObjectDataSource.

Nach ein paar Klicks durch den Wizzard und einem beherztem F5 bestätigte sich vorerst mein initiales Vorurteil: "Totaler Mist!".

Mein Business Service hatte nämlich eine Abhängigkeit auf eine weitere Klasse, die für die  Datenhaltung zuständig war. Diese Abhängigkeit fand sich in meinem Quellcode in Form eines Konstruktor Parameters wieder. In meiner ASP.NET MVC Implementierung war der DI Container StructureMap für das Auflösen dieser Abhängigkeit zuständig.

Die WebForms Variante brach die Ausführung des Codes nun allerdings mit einer Exception ab und wies mich in freundlichem Gelb darauf hin, dass mein Business Objekt keinen parameterlosen Konstruktor hätte.

In der Hoffnung, eine Factory für mein Business Objekt angeben zu können durchsuchte ich also die Eigenschaften der ObjectDataSource. Leider wurde ich nicht fündig, fluchte ein wenig darüber, dass meine Anforderung doch gar nicht so ungewöhnlich wäre und beendete Visual Studio frustriert.

Glücklicherweise guckte ich ein wenig später doch noch mal nach einer Lösung. So kann zwar keine Eigenschaft für eine Factory angegeben werden, stattdessen wird jedoch ein Ereignis zur Verfügung gestellt, in dem ich das entsprechende Business Objekt erstellen und meiner ObjectDataSource zuweisen kann.

Konkret sieht dies wie folgt aus:

protected void AufgabenDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
    AufgabenService service = ObjectFactory.GetInstance<AufgabenService>();
    e.ObjectInstance = service;
}

Nun habe ich über die ObjectFactory zwar einen direkten Verweis innerhalb meiner CodeBehind Datei auf den genutzten DI Container (in meinem Fall StructureMap), dies ist mir aber immer noch lieber, als die Abhängigkeit zur Persistenzschicht in meinem Business Service hart zu verdrahten.

Und die Moral von der Geschicht ...

... lautet: Erst ausprobieren und dann (gegebenenfalls) meckern ;-)


Kick it on dotnet-kicks.de
 
7/14/2010 - 07:08 AM | Comments [0] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Wie Craig Shoemaker bereits in seinem Blog geschrieben hat, werden die Infragistics NetAdvantage Controls für Silverlight und WPF ab der Version 10.2 auch mit Deutschen Oberflächentexten ausgeliefert. In diesem Beitrag möchte ich die Notwendigen Schritte noch einmal im Schnelldurchlauf in deutscher Sprache am Beispiel von Silverlight erläutern.

Initiales Setup

Mein Silverlightprojekt besteht aus einer einzigen Seite, in der ein XamGrid definiert wurde:

<UserControl x:Class="NAGermanLocalization1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:ig="http://schemas.infragistics.com/xaml">

    <Grid x:Name="LayoutRoot" Background="White">
        <ig:XamGrid HorizontalAlignment="Left" Name="xamGrid1" VerticalAlignment="Top"  AutoGenerateColumns="false"  >
            <ig:XamGrid.FilteringSettings>
                <ig:FilteringSettings AllowFiltering="FilterMenu" />
            </ig:XamGrid.FilteringSettings>
            <ig:XamGrid.GroupBySettings>
                <ig:GroupBySettings AllowGroupByArea="Top" />
            </ig:XamGrid.GroupBySettings>
            <ig:XamGrid.Columns>
                <ig:TextColumn Key="Id" />
                <ig:TextColumn Key="Name"/>
                <ig:TextColumn Key="Vorname"/>
                <ig:TextColumn Key="Land" />
            </ig:XamGrid.Columns>
        </ig:XamGrid>
    </Grid>
</UserControl>

Für das Grid habe ich wie der vorherige Codeausschnitt zeigt Filtern und Gruppieren aktiviert.

Meine Codebehind Datei sieht ähnlich schmal aus:

using System.Collections.Generic;
using System.Windows.Controls;

namespace NAGermanLocalization1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            InitGrid();
        }

        private void InitGrid()
        {
 	        List<Kunde> kunden = new List<Kunde>();
            for (int i = 0; i<10; i++)
            {
                Kunde kunde = new Kunde{Id = i, Name = string.Format("Name {0}", i), Vorname = string.Format("Vorname {0}", i), Land = "DE"};
                kunden.Add(kunde);
            }
            xamGrid1.ItemsSource = kunden;
        }
    }

    public class Kunde
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Vorname { get; set; }
        public string Land { get; set; }
    }
}

An der Oberfläche schaut das ganze nun wie folgt aus:

GridVorher

DialogVorher

Und einmal auf Deutsch bitte ...

Um das ganze nun zu lokalisieren, sind lediglich zwei Schritte erforderlich.

Zunächst muss das Silverlight Projekt entladen und anschließend im Textmodus editiert werden:

unloadproject editproject

Der vorhandene leere Tag SupportedCultures muss mit dem Wert de versehen werden.

supportedcultures

Anschließend muss die Projektdatei gespeichert und erneut geladen werden. Als letzter Schritt muss dann innerhalb der Webseite, die das Silverlight Control hostet noch die Zeile <param name="uiculture" value="de" /> innerhalb des Object Tags eingefügt werden:

uiculture

Wenn nun nichts schief gegangen ist, sollten die Oberflächentexte nun auf Deutsch erscheinen:

GridNachher

DialogNachher

Fazit

Lokalisierte Oberflächen werden mit Infragistics NetAdvantage 10.2 zum Kinderspiel, da das umständliche Setzen der Oberflächentexte über die runtime resource-string customization in Zukunft entfällt.


Kick it on dotnet-kicks.de
 
6/30/2010 - 11:53 PM | Comments [1] | Categories: .NET | DotNetGerman Bloggers | Infragistics | Silverlight
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Passend zur Fußball WM konnte ich einen virtuellen Hattrick landen. Kürzlich ist nämlich mein dritter Artikel in Folge auf der deutschen ASP.NET Site http://www.asp.net/de veröffentlicht worden. Cool :-)

aspnet_de

Nachdem ich die Artikel des Tages nun also "gestürmt" habe, frage ich mich nur noch, wie ich mein Blog in den Feed links bekomme ...


Kick it on dotnet-kicks.de
 
6/14/2010 - 11:32 PM | Comments [1] | Categories: .NET | ASP.NET | Community | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Das Beispielprojekt meines jQuery Vortrags während der dotnet Cologne kann ab sofort hier herunter geladen werden.

Es handelt sich dabei um ein kleines ASP.NET MVC 2 Projekt, in dem folgendes genutzt wurde:

  • ASP.NET MVC 2 ;-)
  • StructureMap als IOC Container
  • SQLite als leichtgewichtige In-Memory-DB
  • NHibernate für den Datenbankzugriff
  • jQuery für den Wow-Faktor ;-)

Die Solution liegt passend zur Veranstaltung im VS 2010 Format vor. Bei Bedarf kann ich aber auch eine VS 2008 Solution bereit stellen.

Hauptaugenmerk solltet ihr auf die Datei aufgabenlist.js setzen. Hier befindet sich der relevante jQuery / JavaScript Code. Der ganze Rest ist nur "Infrastruktur", damit ich jQuery an einem halbwegs realistischen Beispiel zeigen kann ;-)

An Feedback zu den Quellcodes bin ich immer interessiert. Am besten über das Kontaktformular, oder die während des Vortrags mitgeteilte E-Mail Adresse.

Ich weiß übrigens, dass das ASP.NET MVC Projekt keine Unit Tests beinhaltet. Angesichts der Projektgröße und der verfügbaren Zeit habe ich hier ein wenig geschludert. Thomas Bandt hat mich während des Vortrags übrigens auch darauf aufmerksam gemacht, dass mein JavaScript Code nicht via Unit Tests geprüft wurde. Auch hier gelobe ich Besserung :-)

Literaturempfehlungen

Im Anschluss an den Vortrag wurde ich außerdem gefragt, welche Literatur ich zu dem Thema empfehlen könnte.

Nun, da sich jQuery hauptsächlich mit der Modifikation des DOMs, insbesondere dem Ein- / Ausblenden sowie dynamischem CSS befasst, sollte man meiner Meinung nach zunächst über solide (X)HTML und CSS Kenntnisse verfügen (kein Witz).

Zu diesem Thema kann ich das Buch Head First HTML with CSS & XHTML empfehlen:

Außerdem können generelle JavaScript Kenntnisse auch nicht schaden ;-) Hier habe ich persönlich sehr gute Erfahrung mit dem Buch Professional JavaScript for Web Developers gemacht.

Speziell zum Thema jQuery hat mir das Buch jQuery in Action, Second Edition sehr gut gefallen. Ich habe es mir im Rahmen des Manning Early Access Program als E-Book bestellt. Wer lieber ein gedrucktes Exemplar haben möchte, muss sich noch ein wenig gedulden, kann es aber dann auch z. B. bei Amazon bestellen.


Kick it on dotnet-kicks.de
 
5/31/2010 - 11:24 PM | Comments [0] | Categories: .NET | ASP.NET | Community | DotNetGerman Bloggers | jQuery
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Letzten Freitag hatte ich die Freude, an der dotnet Cologne teilzunehmen. Mit über 300 Teilnehmern war es ein wirklich riesiges Event, dass sich vor "professionellen", oder besser gesagt kommerziellen Konferenzen nicht verstecken muss.

Der Teilnehmer

Vor Ort war ich in mehreren Rollen. Zum einen natürlich als Teilnehmer. In dieser Rolle nutzte ich die Möglichkeit, endlich mal die Gesichter zu einigen Bekannten aus der Community zu sehen und auch mal persönlich das ein oder andere Wort zu wechseln. Außerdem hörte ich mir auch spannende Vorträge, unter anderem von Jörg Krause zu Sharepoint als Entwicklungsplattform, Neues in Silverlight 4 von Stefan Lange sowie Neues in ASP.NET 4.0 von Jan Welker.

Irritierend fand ich, dass gefühlte 90 % der Besucher von Jörg Krauses Sharepoint Vortrag keinerlei Sharepoint Vorkenntnisse hatten und somit wohl nicht im geringsten wussten, wie so eine Site, eine Sitecollection, ein Web, eine Liste, ein Webpart ... an der Oberfläche wohl aussehen. Die gleiche Situation habe ich übrigens auch mehrfach schon auf der Shareconnect (Basta Sharepoint Days) beobachtet. Vielleicht wäre hier einfach mal ein "Was ich über Sharepoint wissen sollte, bevor ich Visual Studio aufmache" Vortrag angebracht. Leider habe ich einen solchen Vortrag bisher noch auf keiner (Entwickler-)Konferenz im Angebot gesehen. Nichts desto trotz fand ich Jörgs Vortrag prima! Auch die anderen besuchten Vorträge haben mir gut gefallen, daher mein Fazit als Teilnehmer:

Voller Erfolg! Gute Vorträge, gute Kontaktmöglichkeiten zur Community, was will man mehr.

Der Aussteller

Unter den Sponsoren des Events war unter anderem auch die Firma Infragistics. Wie auch auf anderen Konferenzen lies ich es mir als fleißiger Infragistics MVP  in den Pausen natürlich nicht nehmen, Kiril und Nils tatkräftig zu unterstützen. Dies war auch bitter nötig, da der Andrang am Stand weit höher war, als ich es von anderen Konferenzen gewohnt war. Kamen wir normalerweise zu zweit immer ganz gut zurecht, waren dieses Mal sogar drei Personen eigentlich schon fast zu wenig.

Fazit als Aussteller: Tolle Veranstaltung! Viele Kontakte, interessante und zum Teil sogar sehr trickreiche Fragen, genauso muss es sein!

Der User Group Leader

Jeder der schon mal ein User Group Treffen besucht hat wird sich sicherlich fragen:

Wo kommen eigentlich die Sprecher her?

Nun ja, als sie ganz klein waren, wird sie voraussichtlich einer der beiden hier unten gebracht haben.

image

Foto: Valter Jacinto | Portugal   http://www.flickr.com/photos/valter/87429062/sizes/m/
Creative Commons License

Irgendwann werden die Jungs und Mädels dann aber groß und spätestens dann stellt sich für einen User Group Leiter die Frage:

Wo bekomme ich eigentlich Sprecher her?

Ein besonders guter Ort, Sprecher für die eigene User Group zu finden ist selbstverständlich eine Konferenz, denn dort treten Speaker häufig in Rudeln auf ;-)

Also machte ich mich während der dotnet Cologne auf den Weg und zog Sprecher für die nächsten Treffen der .NET User Group Koblenz an Land.

Die Ausbeute war übrigens mit zwei definitiven, einer relativ verbindlichen und einer losen Zusage recht gut. Daher auch hier: dotnet Cologne, 12 Points ;-)

Der Sprecher

Zu guter letzt (und angesichts der Agenda ist dies wörtlich gemeint), war ich auch als Sprecher unterwegs. Mein Thema war die Einführung in jQuery unter dem spontan geänderten Titel:

jQuery - oder warum Sie JavaScript in Zukunft nicht mehr hassen werden.

Den Verlauf des Vortrags würde ich wie folgt beschreiben

  • Der Saal füllt sich, die Menge wird still. Ich will loslegen, aber mein Mikro überlegt sich, dass es sich lieber in meinem T-Shirt verdreht. Kein Mensch hört mich ... so ein Mist
  • Mikro Problem gelöst, schnell durch die Folien gejagt. Auf gehts zur Demo!
  • Meine ASP.NET MVC Anwendung reißt niemand vom Hocker und stößt kaum auf Interesse
  • Ist zum Glück nicht schlimm, schließlich ist mein Thema ja auch jQuery und nicht ASP.NET MVC ;-)
  • Die ersten UI Gimmicks (alternierende Tabellenzeilen, Hover Effekte) zaubern ein müdes Lächeln auf die Gesichter der Menge - da muss wohl noch mehr her
  • Ich erstelle mit einer Zeile jQuery Code auf- und zuklappbare Bereiche in der Sidebar der Anwendung. Im Publikum sehe ich die ersten funkelnden Augen
  • Auf der Welle muss ich weiter reiten, also jetzt schnell ein wenig Ajax;-)
  • In der Einleitung habe ich etwas vom Update Panel erzählt. Heißt dann wohl ich sollte auch ein wenig WebForms zeigen. Ich entschließe mich also, im Firebug mal zu zeigen, was über die Leitung geht wenn man Ajax mit dem Update Panel erlegt erledigt. Als ich zum ViewState scrolle scheinen einige Teilnehmer zu glauben ich hätte gerade die Matrix gehacked oder zumindest gedebugged.
  • Oh je, nur noch 3 Minuten Zeit und ich habe doch versprochen früher Schluss zu machen - jetzt muss schnell etwas großartiges her. Ich greife also noch mal in die Trickkiste und greife zu meinem größten Trumpf:
    runde Ecken;-)
    Puh, geschafft, die Zuschauer jubeln. Ein Glück, dass es runde Ecken gibt ;-)

Mein Fazit als Sprecher lautet also:

Wahnsinn! Auch wenn es nur ein Einsteiger Vortrag war und laut Handzeichen mindestens die Hälfte der Anwesenden jQuery bereits kannte und nutzte war das Publikum allem Anschein nach während des Vortrags voll dabei. So macht vortragen Spaß!

Der Grillfreund

Zum Abschluss fand für einige Teilnehmer, Sprecher und Sponsoren dann noch die durch Microsoft gesponsorte und durch Jan Welker gestifftete Grill-Party des dotnet Forums statt. Essen und Getränke waren sehr lecker, die Gespräche spannend, von daher auch hier mein Kompliment.

Fazit

Der Besuch der dotnet Cologne hätte in keinster Weise besser laufen können. Großes Lob und alle Achtung an die Organisatoren! Nächstes Jahr bin ich - in welcher Form auch immer - sicherlich auch wieder mit dabei!

 

Hey, du hast dir die Zeit genommen, den ganzen Beitrag zu lesen, oder zumindest bis hier hin zu scrollen. Nimm dir doch bitte auch noch die Zeit, ihn über den unten stehenden Button bei dotnet-kicks.de zu kicken!


Kick it on dotnet-kicks.de
 
5/31/2010 - 11:03 PM | Comments [0] | Categories: .NET | ASP.NET | Community | DotNetGerman Bloggers | jQuery | Vorträge
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Habe ich für meine Blog Beiträge gefunden. Zumindest die englischen, die einen Bezug zu den Infragistics NetAdvantage Controls haben.

Seit kurzem gibt auf der Infragistics Homepage nämlich einen MVP Blog, für das ich einer der Autoren bin.

Selbstverständlich gibt es auch schon einen ersten Blog Post von mir. Dieser beinhaltet neben einer kurzen (OK, langen ;-)) Vorstellung ein kleines jQuery Script, welches hilfreich beim Einsatz des Infragistics ASP.NET Aikido WebDropdown Controls ist.

igmvpblog


Kick it on dotnet-kicks.de
 
5/23/2010 - 04:24 PM | Comments [0] | Categories: .NET | ASP.NET | DotNetGerman Bloggers | Infragistics | jQuery
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Vor ein paar Wochen haben unter anderem Darius, Alex und Thomas darüber geblogged, was sie vor 10 Jahren programmiert haben.

Natürlich kam ich auch ins Grübeln und so fiel mir ein, dass ich zu dieser Zeit meine Zeit meist mit VB 6 Desktop Anwendungen, Outlook Formularanwendungen, oder aber PHP oder Classic ASP Webanwendungen verbracht habe. Allerdings habe ich seitdem mehrfach den Arbeitgeber gewechselt und die Source Codes daher auch nicht mehr im Zugriff. Somit war das Thema, einen Blogeintrag zu dieser Frage zu schreiben für mich eigentlich erledigt.

Eigentlich ...

Aber auch nur eigentlich, denn lustigerweise schickte mir letzte Woche ein ehemaliger Kollege aus heiterem Himmel einen alten Screenshot, den er irgendwo gefunden hatte.

Bei diesem Screenshot handelte es sich um einen Gag Screen einer Anwendung, die wir 1999 für die Finanzbuchhaltung der Nürburgring GmbH geschrieben hatten. Das Bild öffnete sich übrigens, wenn ich mich recht erinnere beim Klick auf ein Label. In den vier Jahren, in denen ich die Anwendung anschließend noch weiter betreut habe, kam allerdings nie eine Anfrage zu dieser Bildschirmmaske. Daher gehe ich mal davon aus, dass es niemand gefunden hat :-)

Auf dem Bild zu sehen sind übrigens mein damaliger Kollege, der ein ehemaliger Schulkamerad und mein späterer WG Mitbewohner war, und ich. Das Bild wurde allerdings nicht während der Arbeit, sondern während eine Pre-Game Grill Party eines Spiels der Frankfurt Galaxy aufgenommen. Zu rätseln, wer von uns beiden wer ist, überlasse ich den Lesern dieses Blogs.

programmierer

Schöne Erinnerung! Vielen Dank für das Bild Oliver!


Kick it on dotnet-kicks.de
 
5/17/2010 - 11:14 PM | Comments [4] | Categories: .NET | DotNetGerman Bloggers | Fun
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Moderne Webseiten beinhalten in der heutigen Zeit einen hohen Anteil clientseitiger Quellcodes in Form von JavaScript. Für diesen Anwender führt dies im Idealfall zu einer verbesserten Benutzbarkeit der Applikation, da diese neben netten UI Effekten nun meist durch Ajax auch weitaus flüssiger bedienbar ist.

Bei vielen Entwicklern solcher dynamischer Webseiten führt der vermehrte JavaScript Anteil allerdings häufig zu Wein- und/oder Schreikrämpfen, denn nur die wenigsten mögen JavaScript wirklich. Die Ursachen für diese Antipathie liegen jedoch selten an JavaScript als Sprache selbst, sondern in den meisten Fällen eher an der unterschiedlichen Implementation des DOMs / BOMs durch die verschiedenen Browserhersteller. Diese Inkonsistenz führt nämlich dazu, dass JavaScript Code, der in einem Browser bzw. einer Version eines Browsers problemlos läuft im nächsten Browser Fehler verursacht und schlichtweg nicht läuft.

Weiter wird häufig bemängelt, das Selektionen fernab von einem einfachen document.getElementById(IdMeinesElements) relativ kompliziert sind. Als Beispiel wären hier zu nennen: alle Elemente mit einer bestimmten Klasse, ungerade Zeilen einer Tabelle, das aktivierte Element einer RadioButtonGroup usw.

Und nun?

Nun gibt es folgende Strategien mit diesen Problemen umzugehen:

  1. Man drückt sich immer davor JavaScript zu schreiben und wälzt dies auf einen Kollegen ab
  2. Man kopiert sich irgendwoher JavaScript Codeschnippsel, die angeben browserunabhängig zu sein und hofft, das dem auch so ist
  3. Man investiert Unmengen Zeit in die eigene browserübergreifende Implementierung diverser Funktionalitäten
  4. Man bedient sich eines der am Markt verfügbaren JavaScript Frameworks

Strategie Nr. 1 mag eine Weile ganz gut funktionieren, früher oder später wird aber der Zeitpunkt kommen, an dem man sich nicht mehr drücken kann (ich spreche da aus eigener Erfahrung ;-)).

Strategie Nr. 2 funktioniert immer dann, wenn der Umfang des benötigten JavaScript Codes überschaubar ist. Gemeinsam mit der Applikation wird aber auch irgendwann der zusammenkopierte Code wachsen und die Wartbarkeit entsprechend sinken.

Strategie Nr. 3 ist sicherlich eine prima Idee für alle die außerdem auch ein eigenes Logging Framework, einen eigenen OR Mapper usw implementiert haben, oder kurz gesagt für alle die gerne das Rad neu erfinden ;-)

Gangbar scheint also nur Strategie Nr. 4 zu sein. Schaut man sich nun am Markt um, stößt man unweigerlich auf jQuery. Die freie JavaScript Library adressiert unter anderem genau die zuvor genannten Probleme und hat in letzter Zeit einen wahren Hype verursacht. Dieser Rummel wurde sicherlich auch dadurch verstärkt, dass Microsoft jQuery offiziell supportet, mit Visual Studio ausliefert und die Arbeiten an der hauseigenen JavaScript Bibliothek ASP.NET Ajax Library zugunsten von jQuery eingestellt hat.

Wie legen wir nun aber mit jQuery los? Genau diese Frage möchte ich in diesem und den folgenden Blog Posts beantworten. Da jQuery in einem neuen ASP.NET MVC Projekt automatisch hinzugefügt wird und ASP.NET MVC Entwickler somit sowieso jQuery gewöhnt sind, soll als Beispiel zunächst eine Webforms Anwendung dienen.

Auf die Plätze, fertig, los!

Wenn man jQuery benutzen möchte, besteht der erste Schritt darin, die freie JavaScript Library in seine Seite einzubinden. Dazu lädt man unter http://www.jQuery.com einfach die aktuelle Version der Bibliothek herunter und kopiert diese anschließend zum Beispiel in einen Unterordner Scripts seiner Webanwendung. jQuery ist übrigens in einer für Menschen lesbaren (z. B. jQuery-1.4.2.js) und in einer verkleinerten Version (z, B. jQuery-1.4.2.min.js) verfügbar. In der verkleinerten Version sind unnötige Leerzeichen, Zeilenumbrüche und Kommentare entfernt. Außerdem wurden die Namen der Variablen und nicht öffentlichen Funktionen auf ein oder zwei Buchstaben verkürzt. Sinn dieser Maßnahme ist es die zum Client übertragene Datenmenge zu reduzieren. So ist die verkleinerte Version knappe 100 kb kleiner als die lesbare Variante. Für Produktivszenarien sollte demnach also in jedem Fall die .min Version genutzt werden, wohingegen während der Entwicklungszeit eher die lesbare Variante eingebunden werden sollte. So kann man den Scriptcode im Fall der Fälle nämlich noch debuggen.

So, genug der Vorrede und zurück zur Praxis. Tatsächlich einbinden können wir jQuery nun über folgende Zeile:

<script src="Scripts/jquery-1.4.2.js" type="text/javascript"></script>

Platzieren sollte man diese Zeile übrigens innerhalb des Kopfbereichs der Seite:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="Scripts/jquery-1.4.2.js" type="text/javascript"></script>
</head>

Prima, aber was mache ich nun damit?

Gute Frage! Jetzt haben wir jQuery zwar eingebunden, aber wie geht es jetzt weiter. Dazu sollten wir zunächst kurz einen Blick darauf werfen, was wir normalerweise mit JavaScript machen. In den meißten Fällen reduziert sich dies auf:

  • Elemente aus dem DOM zu selektieren
  • Selektierte Elemente zu manipulieren (ein- / ausblenden, Styledefinitionen zu verändern...)
  • Code bei bestimmten Ereignissen ausführen (z. B. click event)
  • Neue Elemente dem DOM hinzuzufügen
  • Elemente aus dem DOM zu löschen
  • AJAX Aufrufe zum Server zu machen und die Antwort zu Verarbeiten

Als kleinen Einstieg picken wir uns exemplarisch die ersten drei Punkte heraus. Wir werden also DOM Elemente selektieren und diese manipulieren. Geschehen wird dies bei dem Klick auf einen Button.

Dazu werden wir eine Meldungszeile, ähnlich wie man sie von Stackoverflow kennt nachbauen.

01_so_message

Der erste Schritt besteht in der Erstellung eines HTML und CSS Grundgerüsts:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>jQuery Demo</title>

    <style type="text/css">
        body
        {
            margin: 0px;
        }
        #message
        {
            background-color: #FFFF88;
            border-bottom: solid 1px #000000;
            font-weight: bold;
            text-align: center;
            padding: 8px;
            margin: 0px;
            display:none;
            font-family: Arial, Verdana, Sans-Serif;
        }
        #message a
        {
            float: right;
            border: solid 3px black;
            font-family: Arial, Verdana, Sans-Serif;
            font-weight: bold;
            text-decoration: none;
            color: Black;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div id="message"><span>Dummy Nachricht</span><a href="Default.aspx">X</a>
    </div>
    <div id="content">
        <p>
            Dies ist eine jQuery Demowebsite</p>
        <button id="showMessage">
            Klick mich</button>
    </div>
    </form>
</body>
</html>

Wie man sieht, wird ein DIV Element  mit dem Namen "message" erstellt, dass als Unterlemente ein SPAN Element und einen Link enthält. Über CSS wird dieses DIV Element noch ein wenig gestyled und initial ausgeblendet. Außerdem ist auf der Seite noch ein Button definiert. Ein Klick auf diesen Button soll der Auslöser sein, um die Meldung anzuzeigen.

Wie aber genau sieht nun der Code aus, um die Nachricht - mit verändertem Text - anzuzeigen?

Mit der Hilfe von jQuery ist dies garnicht so schwer:

$("#showMessage").click(function(evt) {
    evt.preventDefault();
    $("#message span").text("Du hast den Button geklickt. Klicke nun auf das X, um die Nachricht auszublenden.");
    $("#message").fadeIn(2000);
});
Auf den ersten Blick mag der Code etwas verwirrend aussehen. Vor allem durch die vielen $-Zeichen. Daher ein paar erklärende Worte:

In Zeile 1 selektieren wir mit dem Befehl: $("#showMessage") zunächst das DOM Element mit der ID showMessage, also unseren Button. Das $-Zeichen ist übrigens ein Alias für die jQuery Funktion. Statt $("message") hätte ich also auch jQuery("$message") schreiben können. Die Rückgabe des Aufrufs - unabhängig ob via jQuery(...) oder $(...) ist ein Objekt vom Typ jQuery. Dieses Objekt beinhaltet die selektieren DOM Elemente bzw. das selektierte DOM Element sowie einige weitere Funktionen.

In unserem Fall bekommen wir also ein Objekt vom Typ jQuery zurück, dass das DOM Element showMessage beinhaltet. Für dieses DOM Element wird nun eine anonyme Funktion als Handler für das Ereignis Click registriert.

In Zeile 2 wird mit dem Befehl evt.preventDefault(); nun die Standardaktion die der Browser bei diesem Ereignis, also z. B. das posten des Formulars nach Klick auf den Button, verhindert.

In Zeile 3 werden anschließend alle span Elemente innerhalb des DOM Elements mit der Id message selektiert. In unserem Fall ist dies also genau eins. Für dieses span Element wird mit der Funktion text jetzt ein neuer Text vergeben. Außerdem wird das Element mit der Id message in Zeile 4 langsam (über einen Zeitraum von zwei Sekunden) eingeblendet.

Der Quellcode zum Ausblenden der Nachricht sieht ähnlich aus:

$("#message a").click( function(evt) {
     evt.preventDefault();
     $("#message").fadeOut("slow");
 });

In Zeile 1 wird an alle A-Elemente innerhalb des DOM Elements mit der Id message ein Eventhandler für das Ereignis Click angehangen. Dieser verhindert in Zeile 2 die Standardaktion des Links und blendet in Zeile 3 unsere Nachrichtenzeile wieder aus. Dieses mal wird statt einer Angabe in Millisekunden der String "slow" als Argument übergeben. Dieser ist in den jQuery Quellcodes mit einem Wert von 600 ms hinterlegt.

Perfekt, aber wo schreibe ich den Code nun rein?

So, jetzt wo wir eigentlich den ganzen Quellcode fertig haben stellt sich natürlich die Frage, wie wir ihn in unsere Seite einbinden. Eine naive Implementierung sähe wie folgt aus:

<head>
<!-- ... -->
<script type="text/javascript">
    $("#showMessage").click(function(evt) {
        evt.preventDefault();
        $("#message span").text("Du hast den Button geklickt. Klicke nun auf das X, um die Nachricht auszublenden.");
        $("#message").fadeIn(2000);
    });
    $("#message a").click( function(evt) {
        evt.preventDefault();
        $("#message").fadeOut("slow");
    });
</script>
<!-- ... -->
</head>

Der Code würde also einfach in ein Scripttag innerhalb des Head Tags kopiert werden. Dies läuft so nicht! Der Grund ist, dass mit diesem Code versucht wird, ein Eventhandler an ein DOM Element zu binden, das es zu diesem Zeitpunkt noch garnicht gibt.

Wie sieht aber die Lösung für das Problem aus?

Alles zu seiner Zeit

Wie wir zuvor gesehen haben, ist unser Code wirkungslos, wenn wir ihn ausführen ehe es ein entsprechendes DOM Element gibt. Daher sollten wir ihn erst auslösen, sobald das DOM vollständig initialisiert ist.

Ein weg dies zu erreichen wäre es, den Code aufzurufen wenn das Ereignis window.onload eintritt:

window.onload = function() {
  $("#showMessage").click(function(evt) {
     evt.preventDefault();
     // Restlicher Code hier
  });
}

Diese Variante würde bereits fehlerfrei funktionieren. Allerdings wird das Ereignis onload erst ausgelöst, wenn das DOM vollständig initialisiert wurde und alle externen Ressourcen, wie zum Beispiel Bilder oder Stylesheets geladen wurden. Dies kann von Fall zu Fall recht lange dauern, so dass der Anwender den Button bereits anklicken könnte, ohne dass unser Script ausgeführt wird. Zum Glück bietet jQuery einen besseren Ansatz, nämlich $(document).ready. In dieser Variante wird das Ereignis ready ausgelöst, sobald das DOM vollständig initialisiert wurde, aber bevor externe Ressourcen geladen wurden.

Das vollständige Beispiel mit $(document).ready sieht dann wie folgt aus:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>jQuery Demo</title>

    <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>

    <style type="text/css">
        body
        {
            margin: 0px;
        }
        #message
        {
            background-color: #FFFF88;
            border-bottom: solid 1px #000000;
            font-weight: bold;
            text-align: center;
            padding: 8px;
            margin: 0px;
            display:none;
            font-family: Arial, Verdana, Sans-Serif;
        }
        #message a
        {
            float: right;
            border: solid 3px black;
            font-family: Arial, Verdana, Sans-Serif;
            font-weight: bold;
            text-decoration: none;
            color: Black;
        }
    </style>

    <script type="text/javascript">
        $(document).ready(function() {
            $("#showMessage").click(function(evt) {
                evt.preventDefault();
                $("#message span").text("Du hast den Button geklickt. Klicke nun auf das X, um die Nachricht auszublenden.");
                $("#message").fadeIn(2000);
            });
            $("#message a").click(function(evt) {
                evt.preventDefault();
                $("#message").fadeOut("slow");
            }
        }
      });
    </script>

</head>
<body>
    <form id="form1" runat="server">
    <div id="message"><span>Dummy Nachricht</span><a href="Default.aspx">X</a>
    </div>
    <div id="content">
        <p>
            Dies ist eine jQuery Demowebsite</p>
        <button id="showMessage">
            Klick mich</button>
    </div>
    </form>
</body>
</html>

Ausblick und Fazit

Wie dieser Blog Eintrag gezeigt hat, ist jQuery eine recht komfortabel zu bediene JavaScript Library, mit der sich mit wenigen Zeilen Script Code interessante Effekte erzielen lassen. Neben der intuitiven und Browser unabhängigen API besticht die Bibliothek vor allem durch die Fülle an Funktionen und erhältlichen Plug-Ins. Selbstverständlich konnte ich hier nur einen kurzen (ersten) Einblick verschaffen. Da ich das Thema Clientseitige Entwicklung in (ASP.NET) Webanwendungen jedoch für sehr interessant halte, habe ich vor in der nächsten Zeit weitere Einträge zum Thema jQuery mit folgenden Schwerpunkten zu schreiben:

  • Nutzen von Content Delivery Networks (CDNs)
  • jQuery Selektoren
  • Möglichkeiten der DOM Manipulation
  • jQuery und Firebug
  • jQuery Plugins selber entwickeln
  • jQuery UI
  • jQuery Utility Funktionen
  • jQuery und Ajax für Webforms und ASP.NET MVC

Eventuell werde ich im Anschluss an die Artikel auch kurze Video Tutorials bereitstellen.

Bevor ich allerdings loslege würde mich natürlich interessieren, ob das Thema für euch überhaupt von Interesse ist. Am liebsten in Form eines kurzen Kommentars.

Sollte jemand von euch übrigens das Verlangen haben, das Thema jQuery, oder auch generell ASP.NET mit mir persönlich in lockerer Atmosphäre zu diskutieren: Am 28. Mai werde ich einen Einsteigervortrag zu jQuery auf der dotnet Cologne 2010 halten. Über zahlreiche Besucher des Vortrags und natürlich auch spannende Diskussionen danach würde ich mich sehr freuen.

In den Pausen findet man ihr mich übrigens wahrscheinlich im Ausstellerbereich am Stand der Firma Infragistics. Dort wäre ich dann zusätzlich auch für den ein oder anderen Plausch über die Infragistics NetAdvantage Komponenten zu haben.

War dieser Artikel hilfreich für dich? Dann kicke ihn doch bitte bei dotnet-kicks.de!


Kick it on dotnet-kicks.de
 
5/4/2010 - 07:47 PM | Comments [4] | Categories: .NET | ASP.NET | Community | DotNetGerman Bloggers | Infragistics | jQuery
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Startet man eine ASP.NET Anwendung zum ersten Mal, kommt man nicht gerade in einen Geschwindigkeitsrausch. Dies liegt zum Beispiel daran, dass der IIS den ASP.NET Worker Prozess hochfahren muss. Außerdem läuft eventuell im Ereignis Application_Start hinterlegter Initialisierungscode und schließlich müssen die vorliegenden Assemblies noch durch den JITer in nativen Code überführt werden.

All dies führt dazu, dass man relativ selten so etwas wie "rasend schnell" hört, wenn vom ersten Zugriff auf eine ASP.NET Anwendung gesprochen wird.

Glücklicherweise ist all dies nach dem ersten Request einer Seite kein Problem mehr. Der Worker Prozess ist da, der Initialisierungscode lief und die Just In Time Compilation lief auch.

Zu einem Problem wird die Situation jedoch, wenn man seine ASP.NET Anwendung während der Entwicklung als ein Bestandteil des Nightly Builds automatisch deployed und auch seinem Kunden Zugriff auf diesen täglich frischen Applikationsstand gibt.

Da Kunden morgens nämlich meist früher als Entwickler im Büro sind, sind sie auch die ersten, die die Webanwendung öffnen, um zu sehen, was am Vortrag umgesetzt wurde. Ist dieser erste Zugriff nun aus den oben genannten Gründen langsam, ist negatives Feedback des Kundens zur Applikationsperformance - oder sogar noch schlimmer: im Stillen sinkendes vertrauen in Applikation und Entwickler nicht selten die Folge.

Um dieses Problem zu umgehen, habe ich ein kleines Powershell Script geschrieben, das nach Angabe einer URL die einzelnen Seiten einer Webapplikation ansurft. 

function warmup-site( [string] $rootUrl){
	
	$proxy = New-Object System.Net.WebProxy("mein.firmen.proxy:8080")
	$proxy.UseDefaultCredentials = 1
	
	$wc = New-Object System.Net.WebClient
	$wc.Proxy = $proxy
	
	$pages = @("default.aspx", "seite1.aspx", "seite2.aspx", "subfolder/seite3.aspx")

	# Jede Seite 3 Mal ansurfen, um Sie "warmzuklicken"
	for ($i=0; $i -lt 3; $i++){
		foreach($page in $pages){
			trap [System.Net.WebException] {
			  write-error $("TRAPPED: " + $_.Exception.ToString());
			  continue;
		   	}
			$content = $wc.DownloadString($rootUrl+$page)
			write $("URL " + $rootUrl+$page + " angesurft");
		}
	}

}

warmup-site("http://meine.url/")

Bisher gebe ich die einzelnen Seiten die angefragt werden sollen noch manuell innerhalb des Arrays $pages an. Im nächsten Schritt würde ich jedoch lieber nur noch die URL der Sitemap angeben, diese auslesen und dann sämtliche in der Sitemap aufgeführten URLs anfragen. Da ich jedoch noch Powershell Neuling bin, könnte es noch ein wenig dauern, bis ich die Lösung so weit automatisiert habe. Sollte einer der Leser nun sagen: "Das ist doch ein Dreizeiler, den ich in der Kaffeepause aus dem Ärmel schütteln könnte", dann würde ich darum bitten genau diesen Dreizeiler über die Kommentarfunktion meines Blogs hier einzustellen ;-)

Was bringts?

Die Anwendung startet beim ersten Request nun bedeutend schneller. Somit ist der Kunde beruhigt und wir können uns auf die wichtigen Sachen des Projekts konzentrieren ;-)

Habe übrigens gesehen, dass es ab dem IIS 7 (oder 7.5?) von Hause aus eine Warmup gibt. Hat jemand von euch Erfahrung damit? Im Moment ist es für mich zwar noch nicht akut, da wir den IIS 6 nutzen, wäre aber Interessant für die Zukunft ein paar Erfahrungsberichte zur Hand zu haben.


Kick it on dotnet-kicks.de
 
4/13/2010 - 09:05 AM | Comments [1] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Vor einiger Zeit habe ich ein kleines Einsteigertutorial zum Umgang mit WinDbg geschrieben. Etwas später folgte dann auch ein kurzes Video hierzu.

Sowohl im Tutorial als auch im Video war eine Windows Forms Anwendung das Ziel meiner Debuggingaktivitäten. In einem kurzen Nebensatz erwähnte ich, dass die Zielanwendung bei der gezeigten Vorgehensweise, nämlich dem direkten Anhängen an den Prozess der Wahl, zumindest zeitweise blockiert wird.

In einer Windows Forms Anwendung mag dies kein Problem sein, schließlich bin ich ja alleine auf dem System. Unschön wird es jedoch, wenn man Probleme in einer produktiven ASP.NET Anwendung sucht. Verständlicherweise reagieren hier nämlich die wenigsten Anwender erfreut, wenn die Website steht weil gerade jemand im Hintergrund daran herumfummelt. Außerdem ist es je nach IT Richtlinie auch problematisch den lokalen Administrator des Kundens davon zu überzeugen, dass man mal gerade physikalisch oder auch nur per Remote Desktop an seinen Webserver will.

Und nun?

Abhilfe schafft hier die VBScript Datei adplus.vbs innerhalb des WinDbg Verzeichnisses. Sie ermöglicht es, ein aktuelles Speicherabbild des problematischen Prozesses in Form einer *.DMP Datei zu erstellen. Diese kann wiederum in WinDbg geöffnet und analysiert werden.

Adplus kann in zwei verschiedenen Modi genutzt werden: hang und crash.

Im hang Modus wird einmalig ein aktueller Memory Dump des Prozesses zum Zeitpunkt der Ausführung des Scripts gezogen. Dieser Modus eignet sich besonders wenn die Applikation "hängt" ;-), also in Deadlock Szenarien, oder aber auch um Memory Leaks zu finden. Zur besseren Analyse sollten im Fall ein Memory Leaks jedoch mehrere Dumps bei verschieden hoher Speicherauslastung gezogen werden.

Der Befehl zum Erzeugen eines Dumps im hang Modus lautet übrigens:

adplus.vbs -hang -p processId

Die Id des Prozesses kann entweder über den Taskmanager, oder aber im Fall einer ASP.NET Anwendung die in einem IIS 6 gehostet wird, über den Befehl IISAPP gefunden werden.

Alternativ zum Parameter -p kann über den Parameter -pn auch ein Prozessname angegeben werden. Im Fall einer ASP.NET Anwendung wäre dies also W3WP.exe (Windows Server mit IIS) bzw. WebDev.WebServer.exe im Falle des lokalen Entwicklungsservers. Außerdem kann über den optionalen Parameter -o auch ein dediziertes Ausgabeverzeichnis angegeben werden. Standardmäßig wird nämlich sonst ein Unterverzeichnis, unterhalb des Ordners aus dem adplus heraus aufgerufen wurde, angelegt.

Innerhalb des Zielverzeichnisses wird nun eine Datei mit der Endung .DMP angelegt. Diese kann in WinDbg über den Menüpunkt File -> Open Crash Dump ... geöffnet und mit den bereits bekannten Befehlen analysiert werden.

Der zweite Modus neben hang ist der Modus crash.

Der Aufruf erfolgt hier ähnlich zum hand Modus, mit dem einzigen Unterschied, dass statt -hang -crash übergeben wird.

adplus.vbs -crash -p processId

Im Crash Modus bleibt der Debugger so lange am entsprechenden Prozess angehangen, bis dieser unfreiwillig beendet wird. Tritt dieser Fall ein, wird ein Dump auf die Festplatte geschrieben. Außerdem werden Dumps geschrieben, wenn ein zuvor definierter Breakpoint erreicht wird, oder aber eine access violation Exception auftritt.

Fazit

Mit adplus liegt ein leichtgewichtiges Skript zur Hand, dass bei der Problemanalyse innerhalb produktiver Umgebungen enorm helfen kann. Gerade in Umgebungen, in denen das Live-System nicht durch Debugging blockiert werden darf, oder aber der Administrator keinen Zugriff auf das System gewährt, kann das Skript seine stärken Ausspielen.


Kick it on dotnet-kicks.de
 
4/12/2010 - 10:07 PM | Comments [0] | Categories: .NET | ASP.NET | DotNetGerman Bloggers | WinDbg
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Einer der vielen Vorteile als Infragistics MVP ist es, dass man als relativ früh in die Produktentwicklung involviert wird, um Feedback geben zu können.

Dies kann entweder Feedback über neue Features, API Veränderungen, oder aber auch ganz neue Produkte sein.

Letztes Frühjahr war es wieder so weit. Über der Infragistics NDA Mailing Liste kam eine E-Mail, in der die ersten Arbeiten an einem neuen Produkt, nämlich NetAdvantage for Powershell, angekündigt wurden. Als absoluter Powershell Fan ergriff ich natürlich die Gunst der Stunde und bot meine Hilfe in Form von Alpha Tests, Feedback, etc an.

Nach regem Austausch zwischen dem Director of Product Management Joseph Berres, dem leitenden Entwickler Ambrose J. Tall und einigen Infragistics MVPs stand Anfang September der erste, wenn auch etwas wacklige Proof-Of-Concept Prototyp. Diesen konnte ich mir dann auch auf der Basta von Dr. Tony Komischke, Director of User Experience bei Infragistics, vorführen lassen. Zeigen konnte er mir eine erste Version des Powershell Grids: Infragistics NetAdvantage for Powershell powerGrid. Sehr gerne gesehen hätte ich damals auch die Charting Komponente powerChart. Leider war diese zu diesem Zeitpunkt jedoch noch nicht in einem vorzeigbaren Stadium.

Sehr interessant fand ich übrigens Tony Komischkes Antwort auf meine Frage wie die Idee für eine Powershellproduktsuite aufkam:

Die Idee hatte einer unserer internen IT Mitarbeiter. Er beschwerte sich während eines Meetings, dass in letzter Zeit immer mehr Serverprodukte mit abgespeckter grafischer Administrationsoberfläche auf den Markt kämen. Stattdessen müsste er sich nun mit farblosen Kommandozeilenwerkzeugen herumschlagen.

Diese Situation war der eigentliche Auslöser. Schnell war uns klar, dass Windows Administratoren ansprechende GUIs und keine fade Kommandozeile möchten. Würden Sie Kommandozeilen mögen, wären Sie schließlich Linux Administratoren geworden.

An diesem Punkt setzt NetAdvantage for Powershell. Mit diesem Produkt gibt Infragistics Windows Administratoren zurück, was sie verdienen!

Seit der Basta ist einige Zeit vergangen. Das Produkt nähert sich seiner endgültigen Form und steht ab morgen registrierten Benutzern als CTP im Downloadbereich der Infragistics Homepage zur Verfügung.

Erste Trainingsvideos wird Infragistics New Media Evangelist Jeff Shoemaker voraussichtlich kommende Woche online stellen.

Als Infragistics MVP hatte ich die Freude, das Produkt bereits ausgiebig zu testen. Anbei einige Impressionen:

Die Standardausgabe der Powershell.

01_original

Ausgabe als PowerGrid. Sehr schön umgesetzt wurden das Sortable-Table-Pattern sowie die Tabellenlinien.

02_powerGrid

Ausgabe als PowerChart. Als Anzeigeform habe ich ein Balkendiagramm gewählt.

03_powerChart


Kick it on dotnet-kicks.de
 
4/1/2010 - 12:43 AM | Comments [2] | Categories: .NET | DotNetGerman Bloggers | Infragistics
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

In Teil 5 meiner ASP.NET Webforms und Ajax Serie wies René Drescher-Hackel mich darauf hin, dass ich einen Big Player vollkommen außer acht gelassen habe: AjaxPro von Michael Schwarz.

Das wollte ich natürlich nicht auf mir sitzen lassen. Daher befassen wir uns heute mit:

AjaxPro :-)

AjaxPro ist eine freie Library von Michael Schwarz, die Ajax unter ASP.NET ohne Postbacks ermöglicht. Vergleichbar ist AjaxPro somit am ehesten mit den in Teil 4 vorgestellten PageMethods. Ein großer Vorteil gegenüber diesen ist jedoch die Möglichkeit, AjaxPro auch unter ASP.NET 1.1 einsetzen zu können (ja - ich kenne wirklich noch aktive ASP.NET 1.1 Projekte ;-)). Angemerkt werden sollte noch, dass AjaxPro keiner Weiterentwicklung mehr unterliegt. Michael Schwarz schrieb in in seinem Blog, dass er die Arbeit an der Library einstellt. Als Alternative empfiehlt er den Einsatz von PageMethods.

Lost gehts

AjaxPro ist relativ einfach eingebunden. Es genügt einen Verweis auf die Datei AjaxPro.2.dll hinzuzufügen.

01addreference

Weiter muss AjaxPro in der Datei web.config des Webprojekts bekannt gemacht werden. Dies geschieht über das Hinzufügen eines entsprechenden Http-Handlers:

<httpHandlers>
   ...
   <add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro.2"/>
</httpHandlers>

Anschließend müssen die Methoden einer Seite, die später via Ajax aufgerufen werden sollen, mit dem Attribut AjaxMethod annotiert werden. Dies funktioniert also ähnlich wie die bereits vorgestellten PageMethods. Der Quellcode dieses Beitrags basiert übrigens wieder auf dem Quellcode der vorherigen Teile dieser Serie.

public partial class Teil7 : Page
{
    [AjaxPro.AjaxMethod]
    public string ReadStaticFile()
    {
        string fileContent;
        using (var reader = 
            new StreamReader(
                System.Web.HttpContext.Current.Server.MapPath("~/static.html")))
        {
            fileContent = reader.ReadToEnd();
        }
        return fileContent;
    }
}

Ein netter Nebeneffekt im Vergleich zu Pagemethods ist übrigens, dass die entsprechende Methode zwar statisch sein kann, dies aber nicht muss. Außerdem muss sich die Methode noch nicht einmal zwingend innerhalb einer Webseite (ASPX-Datei) befinden. Stattdessen kann Sie Mitglied einer beliebigen öffentlichen Klasse sein.

Um diese Methode nun komfortabel per JavaScript aufrufen zu können, muss die zugehörige Klasse noch als AjaxTyp registriert werden. Dies geschieht in der Methode Page_Load der Klasse Teil7.

protected void Page_Load(object sender, System.EventArgs e)
{
    AjaxPro.Utility.RegisterTypeForAjax(typeof(Teil7));
}

Der Aufruf innerhalb der Seite kann nun wie folgt erfolgen:

<a href="#" id="StaticFileLink" onclick="Teil7.ReadStaticFile(ajaxdemo_callback)">Hier klicken zum Request einer statischen Datei</a><br />

Bei dem übergebenen Parametger ajaxdemo_callback handelt es sich um eine Callback Funktion, die die Antwort des Servers verarbeitet. In unserem Beispiel sieht sie wie folgt aus:

<script type="text/javascript">
    function ajaxdemo_callback(result) {
       var content = document.getElementById('content');
       content.innerHTML = result.value;
    }
</script>

Die Seite ist nun funktionsfähig und kann gestartet werden. Ein Blick in die Firebug Ausgabe zeigt, dass neben der eigentlichen Seite automatisch noch 4 JavaScript Dateien geladen wurden. Diese sind für die Serverkommunikation zuständig und haben eine Gesamtgröße von nur 25 kb! Im Vergleich zu den zuvor vorgestellten Lösungen ist dies das beste Ergebnis!

02_initialesladen

Auch beim Aufruf der serverseitigen Methode schlägt sich AjaxPro gut. Hier gehen lediglich 111 Byte über die Leitung:

03_ajaxcall

Der Aufruf selbst hat eine Datei mit der Endung *.ashx innerhalb des Pfads /ajaxpro zum Ziel. In meinem Beispiel lautet die URL: http://localhost:2800/AjaxDemo/ajaxpro/Teil7,App_Web_mke8f-yp.ashx. Er wird somit durch den zuvor in der web.config eingetragenen Http-Handler behandelt. Außerdem ist innerhalb der URL, wie man sehen kann, der Name der Klasse in der sich die entsprechende Methode befindet kodiert. Die Methode selbst wird im Header des Aufrufs als Attribut X-AjaxPro-Method übergeben.

Webservices

Nachdem sich PageMethods relativ einfach aufrufen ließen, stellt sich nun natürlich die Frage, wie die Methoden eines Webservices aufgerufen werden können.

Eine Variante wäre es, die Methoden innerhalb des Webservices nicht nur mit dem Attribut [WebMethod] zu anotieren, sondern zusätzlich auch mit [AjaxPro.AjaxMethod] zu versehen. Diese Variante ist selbstverständlich nur für eigene und nicht für externe Webservices möglich.

Ein allgemeingültiger Ansatz besteht darin, händisch Proxy-Methoden innerhalb der Seite für die gewünschten Webservice Methoden anzulegen.

[AjaxPro.AjaxMethod]
public string CallHelloWorldService()
{
    return new AjaxDemoService().HelloWorld();
}

[AjaxPro.AjaxMethod]
public string CallEchoService(int value)
{

    return new AjaxDemoService().Echo(value);
}

Auf dem Client birgt der zusätzliche Code keine Überraschungen. Der Aufruf der serverseitigen Methoden erfolgt analog des ersten Beispiels. Die einzige Besonderheit ist der Aufruf der Methode CallEchoService, da diese einen Parameter erwartet.

<script type="text/javascript">
    function callEchoService() {
        var echoTextBox = document.getElementById('EchoTextBox');
        var value = echoTextBox.value;
        Teil7.CallEchoService(value, ajaxdemo_callback);
    }

    function ajaxdemo_callback(result) {
        var content = document.getElementById('content');
        content.innerHTML = result.value;
    }
</script>

Wie der Beispielcode zeigt, wird der erwartete Parameter einfach vor die Angabe der JavaScript Callback Funktion gepackt.

Fazit

AjaxPro ist eine kleine und leichtgewichtige Ajax Library, die ohne unnötigen Ballast daher kommt. Durch die Ähnlichkeit zu Pagemethods und Scriptservices fiel mir die Einarbeitung ziemlich leicht und nahm kaum mehr als eine halbe Stunde in Anspruch.

Trotz dieser Vorteile kann ich mir nicht vorstellen, AjaxPro als Standardwerkzeug in aktuellen Projekten zu nutzen. Stattdessen werde ich wohl weiterhin auf jQuery setzen. Der Grund für diese Entscheidung liegt ganz einfach darin, dass ich jQuery zu UI Manipulationszwecken meist sowieso schon auf meinen Seiten referenziert habe. Daher ist es naheliegend für AjaxRequests auch zu jQuery zu greifen.

Sehr gut vorstellen könnte ich mir jedoch, AjaxPro in ASP.NET 1.1 Projekten zu nutzen, die sich noch in Pflege befinden. In diesen fehlt die Möglichkeit, PageMethods und Scriptservices einzusetzen, so das AjaxPro dort das perfekte Werkzeug wäre.

An dieser Stelle auch noch einmal vielen Dank an René für den hilfreichen Tipp.


Kick it on dotnet-kicks.de
 
3/29/2010 - 10:12 PM | Comments [0] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Von Administratoren geliebt, von Entwicklern verflucht: Das Transportsystem von SAP. Prinzipiell kann man sich das so vorstellen, dass dieses System vorsieht, dass der Quellcode nur an einer Stelle, nämlich im SAP Entwicklungssystem erstellt und modifiziert wird.

Anschließend wird ein sogenannter Transport (so was ähnliches wie ein Setuppaket) erstellt. Dieses kann dann je Konfiguration erst mal nur in das Testsystem eingespielt werden. Dort wird das Paket auf seine Funktionstüchtigkeit getestet. Wurden diese Tests bestanden, wird es in das Integrationssystem "transportiert". Dort wird das Zusammenspiel mit anderen Anpassungen getestet, es wird also geprüft, ob das Paket nichts anderes kaputt macht ;-)

War auch dieser Test erfolgreich, wird der Transport aus dem Integrationssystem in das Produktionssystem transportiert. Ein direktes Einspielen von Transporten in das Produktionssystem beziehungsweise sogar ein direktes Ändern der Quellcodes ist nicht vorgesehen.

Auch wenn diese Vorgehensweise zunächst recht streng, bürokratisch und viel zu aufwändig wirkt, hat sie durchaus ihren Sinn. Dieser zugegeben etwas starre Prozess sorgt dafür, dass SAP Produktionssysteme normalerweise immer stabil und möglichst fehlerfrei sein sollten. Er trägt also Maßgeblich zu SAPs gutem Ruf bei!

Warum erzähle ich das eigentlich?

Ok, nette Einführung, aber ist das hier nicht eigentlich ein auf .NET fokussierter Blog? Ja :-) Also keine Sorge, ich bin nicht ins Lager der SAP Consultants gewechselt ;-) Allerdings habe ich von ungefähr 2005 bis 2008 die SAP Schnittstelle meines ehemaligen Arbeitgebers gemeinsam mit einem Kollegen weiterentwickelt. Da blieb der ein oder andere Blick unter die Haube diverser SAP Systeme nicht aus.

Nun aber zurück zum Thema:

In meiner beruflichen Laufbahn war ich an einigen (ASP).NET Projekte beteiligt, die von einem SAP transportähnlichen System sehr profitiert hätten. Überspitzt gesagt sah es dort nämlich häufig so aus, dass ein Entwickler nach einer Quellcodeänderung Visual Studio genutzt hat um einen neuen Releasestand zu erstellen und diesen dann schnell auf den produktiven Webserver gepackt hat. Selbstverständlich wurde das ganze vorher nicht in einem Test- oder Integrationssystem installiert und unter Umständen wusste der Kunde und/oder Projektleiter sogar nichts von dem adhoc Update.

Agil != Chaos !!!

Der Entwickler handelte unter dem Deckmantel der agilen Entwicklung nach dem Prinzip "nach mir die Sinnflut", einfach drauf damit, wird schon klappen. In diesem Zusammenhang finde ich es übrigens immer wieder äußerst verwunderlich, wie viele Entwickler unkoordiniertes und schlampiges Vorgehen einfach als agil bezeichnen, um ihr Chaos damit zu entschuldigen. Agil ist allerdings alles andere als chaotisch, unprofessionell, unkoordiniert oder schlampig. Aber gut, dass ist ein anderes Thema - ich schweife schon wieder aus ;-)

In einem meiner aktuellen Projekte wollte ich einer solchen Vorgehensweise direkt einen Riegel vorschieben. Nicht dass die Entwickler dieses Projekts chaotisch wären, das ist ganz und gar nicht der Fall, aber wir wollen ja auch niemanden in Versuchung führen ;-)

Ich hatte einen Traum ...

Meine Ziele lautete also wie folgt:

  1. Releasestände einer Software sollen automatisch auf einem dedizierten Buildrechner und nicht auf einem Entwickler-PC erstellt werden.
  2. Jedes Release hat eine eindeutige Versionsnummer. Kompilate auf dem Entwicklerrechner haben jedoch immer die Nummer 1.0.0.0 (dazu später mehr)
  3. Jedes Release soll ein Set an Unit Tests durchlaufen haben.
  4. Jedes Release soll mit einer bestimmten Qualität als Metadatum versehen werden.
  5. In Abhängigkeit von dieser Qualität und der vorherigen Qualität soll die Software bei bestimmten Statusübergängen automatisch auf bestimmte Systeme verteilt werden.
  6. Das Qualitätsmetadatum wird durch einen dedizierten Personenkreis gesetzt (zum Beispiel QM-Manager oder Projektmanager, Softwarearchitekt oder Chefentwickler)

Realisiert werden sollte das ganze mit Visual Studio 2008 und dem Team Foundation Server. Die Punkte 1 - 3 stellen in einer solchen Umgebung kein Problem dar.

Punkt Nr. 1 erledigen wir durch Teambuild. Hier läuft ein Buildagent auf einem dedizierten Rechner. Durchgeführt werden sowohl Continuous Integration Builds, als auch Nightly Builds.

Punkt Nr. 2 haben wir durch ein einfaches Build Target in unserem Buildscript erledigt. Die Versionsnummer haben wir wie folgt aufgebaut:

  • Major: Fixer Wert
  • Minor: Fixer Wert
  • Build: Aktueller Changeset des Teamprojekts im TFS
  • Revision: Fortlaufende Nummer

Diese Nummer wird nur während des Builds durch Teambuild erzeugt. Entwicklerbuilds haben immer die Version 1.0.0.0. So fange ich ab, dass ein Entwickler doch einfach mal auf seinem Rechner eine Version baut und versucht zu deployen. Natürlich ist dies nur ein weicher Schutz. Mit ein wenig Aufwand lässt sich das System natürlich leicht umgehen indem ein Entwickler eine korrekte Versionsnummer erzeugt. Allerdings ist der Aufwand schon wieder so hoch, dass sich dann doch lohnt, den vorgesehenen Prozess einzuhalten ;-)

Sollte jemand Interesse an diesem Teil des Buildscripts haben: Einfach einen Kommentar posten.

Punkt Nr. 3 ist natürlich auch kein Problem. Aufgrund der guten VS Integration haben wir uns für MS Test für dieses Projekt entschieden. Diese Tests als Bestandteil des Buildprozess laufen zu lassen war demnach kein Problem.

Punkt Nr. 4 ist auch bereits von Haus aus mit dem TFS möglich. Jeder Build der durch Teambuild durchgeführt wird hat ein Metadatum Buildqualität. Die möglichen Werte der Buildqualität lassen sich frei definieren. Auch lässt sich über entsprechende Rechte einschränken, wer diese Qualität setzen darf. Wir haben bei uns die Werte "Entwicklung", "Test", "Integration", "Produktion" und "Abgelehnt" definiert.

Punkt Nr. 5 kann mit TFS Bordmitteln erst mal nicht realisiert werden. Hier kommt nun das Open Source Projekt TFS Deployer ins Spiel. TFS Deployer installiert man als Windowsdienst. Nach entsprechender Konfiguration lauscht er nun auf eine Änderung der Buildqualität. Dazu muss der Benutzer unter dem der Dienst läuft allerdings in der TFS Gruppe Valid Users sein und für das entsprechende Temaprojekt zumindest Leserechte haben. Bemerkt TFS Deployer nun einen Statusübergang, für den er ein Deploymentscript hat, führ er dieses automatisch aus.

Angelehnt an die Einführung von Dave Comfort und Scott Colestock kann man sich das ganze nun wie folgt vorstellen:

tfsdeployer

Konkret sieht dies so aus, dass innerhalb der TFS Quellcodeverwaltung unterhalb des Verzeichnisses der Builddefinition ein Verzeichnis Deployment angelegt wird, also zum Beispiel $/TeamProjekt/TeamBuildTypes/MeinProjektNightlyBuild/Deployment. In diesem Verzeichnis wird nun die Datei DeploymentMappings.xml angelegt. In dieser Datei wird für jeden gewünschten Statusübergang festgelegt, welches Powershellscript auf welchem Computer ablaufen soll. Die Datei DeploymentMappings kann zum Beispiel wie folgt aussehen:

<DeploymentMappings xmlns="http://www.readify.net/TFSDeployer/DeploymentMappings20061026">
  <Mapping xmlns=""
           Computer="tfsbuildserver"
           OriginalQuality="*"
           NewQuality="Entwicklung"
           Script="EntwicklerDrop.ps1"
           RunnerType="PowerShell"
           NotificationAddress="someone@mycompany.com;someoneelse@mycompany.com" />
  <Mapping xmlns=""
           Computer="tfsbuildserver"
           OriginalQuality="Entwicklung"
           NewQuality="Test"
           Script="EntwicklungNachTest.ps1"
           RunnerType="PowerShell"
           NotificationAddress="someone@mycompany.com;someoneelse@mycompany.com" />
  <Mapping xmlns=""
           Computer="tfsbuildserver"
           OriginalQuality="Test"
           NewQuality="Integration"
           Script="TestNachIntegration.ps1"
           RunnerType="PowerShell"
           NotificationAddress="someone@mycompany.com;someoneelse@mycompany.com" />
  <Mapping xmlns=""
           Computer="tfsbuildserver"
           OriginalQuality="Integration"
           NewQuality="Produktion"
           Script="TestNachIntegration.ps1"
           RunnerType="PowerShell"
           NotificationAddress="someone@mycompany.com;someoneelse@mycompany.com" />
</DeploymentMappings>

In diesem Script werden vier Statusübergänge definiert. Erst vom leeren Zustand nach Entwicklung, dann weiter nach Test, Integration und Produktion. Jeder dieser Status entspricht einem Build-Quality Eintrag im TFS. Diese sind wie angemerkt frei definierbar. Über die Eigenschaft Script wird nun definiert, welches Powershellscript ausgeführt werden soll. Diese Powershellscripte müssen übrigens im selben Ordner liegen, wie die Datei DeploymentMappings.xml.

Tritt nun einer der definierten Statusübergänge auf, ruft TFS Deployer das gewünschte Script im Kontext des Service Users auf und übergibt ein BuildData Objekt. Die verschiedenen des Eigenschaften BulidData Objekts können dann über die Variable $TfsDeployerBuildData zugegriffen werden.

In unserer Umgebung nutzen wir übrigens eine modifzierte Variante des Beispiels der Seite Team Foundation Server Build Recipes.

function publish-site( [string] $sourcepath, [string] $destinationpath, [bool] $renameconfig, [bool] $deleteexisting) {
	$droplocation = $TfsDeployerBuildData.DropLocation
	
	$websourcepath = $droplocation + $sourcepath
	$webdestinationpath = $destinationpath
	
	new-item -force -path $webdestinationpath -itemtype "directory"
	if ($deleteexisting) {
		get-childitem $webdestinationpath | remove-item -force -recurse
	}
	get-childitem $websourcepath | copy-item -force -recurse -destination $webdestinationpath

	if ($renameconfig) {
		$configFile = $webdestinationpath + "web.production.config"
		remove-item $configFile -force
		$configFile = $webdestinationpath + "web.development.config"
		remove-item $configFile -force
		$configFile = $webdestinationpath + integration.config"
		remove-item $configFile -force


		$configFile = $webdestinationpath + "web.test.config"
		$configFileDest = $webdestinationpath + "web.config"
		move-item $configFile $configFileDest -force
	}
}

publish-site "\Release\_PublishedWebsites\MyWebApplication\" "\\MeinWebServer\MeineFreigabe\" 1 1

Dies funktioniert in unserer Umgebung recht gut, da wir vom Build Server direkten Zugriff auf die verschiedenen Web Server (Entwicklung, Test, Integration, Produktion) haben. Für jede Webapplikation besteht auf den Servern eine Windows Freigabe, die direkt auf das Wurzelverzeichnis der Applikation zeigt. Zugriffsrechte hat hier nun der Benutzer, unter der TFS Deployer Dienst läuft. So stellen wir sicher, dass keiner der Entwickler "mal schnell" etwas patcht ;-) Noch sicherer wäre es natürlich gewesen, den TFS Deployer Dienst direkt auf dem Web Server laufen zu lassen, und ihm lesenden Zugriff auf die erstellen Binaries des Buildprozesses zu geben.

Als Erweiterung zum Standardscript wärmen wir nach dem Deployment Vorgang die Applikation noch auf, indem wir einige Seiten ansurfen. Somit ist sichergestellt, dass der Workerprozess beim ersten Zugriff durch einen Benutzer schon da ist und die entsprechenden Seiten auch schon kompiliert wurden.

Den Übergang von "Nichts" zu "Entwicklung" machen wir übrigens automatisch als letzten Schritt unseres Buildscripts. Somit wird der Nightly Build Automatisch in die Entwicklungsumgebung gejagt.

Und was hat man nun davon?

Mit dem TFS Deployer haben Nutzer des TFS eine einfache Möglichkeit, benannte Softwarestände automatisch in verschiedene Umgebungen zu verteilen. Die dadurch entstehende Nachvollziehbarkeit lässt aus meiner Erfahrung Kunden und somit auch die Projektverantwortlichen im internen Team besser schlafen.

Ein wenig schade ist, dass man innerhalb des TFS zwar verschiedene Buildqualitäten, jedoch keine Regeln für gültige Übergänge anlegen kann. Da unsere Scripte jedoch auf festen Übergängen definieren bedeutet dies, dass derjenige, der die Buildqualität setzt, genau wissen muss welche Übergänge gültig sind. Schaltet er nämlich zum Beispiel von Test direkt auf Produktion passiert nämlich nichts.

Angeregt zu diesem Blog Post hat mich übrigens Robert Mühsigs Posts Build / Deployment / WTF.


Kick it on dotnet-kicks.de
 
3/25/2010 - 07:11 AM | Comments [7] | Categories: .NET | ASP.NET | DotNetGerman Bloggers | TFS
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Cool, mein Blogeintrag zum Thema Viewstate hat es auf die offizielle Microsoft ASP.NET Seite geschaft. Nun ja, zumindest auf die deutsche Variante davon :-)


Kick it on dotnet-kicks.de
 
3/23/2010 - 03:20 PM | Comments [2] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Vor einiger Zeit habe ich ein kurzes Tutorial zur Bedienung der Windows Debugging Tools hier in meinem Blog veröffentlicht.

Dieses Tutorial traf auf großen Anklang. Zumindest wenn ich meinen Statistiken trauen darf. Denn laut diesen kommen die meisten Besucher durch eine Google Suche nach WinDbg auf mein Blog.

Um dieser Tatsache gerecht zu werden, habe ich mich dazu entschlossen das ursprüngliche Tutorial nun auch als Video bereit zu stellen. Ich hoffe damit auch Gregor zufrieden zu stellen, der meinte dass die geschriebene Variante doch etwas schwierig zu befolgen wäre ;-)

The Camtasia Studio video content presented here requires a more recent version of the Adobe Flash Player. If you are you using a browser with JavaScript disabled please enable it now. Otherwise, please update your version of the free Flash Player by downloading here.

Über Kommentare und Rückfragen würde ich mich sowohl hier auf meinem Blog, als auch unter der im Video angegebenen E-Mail Adresse sehr freuen.

War dieser Eintrag hilfreich für dich? Dann kicke ihn doch bitte.


Kick it on dotnet-kicks.de
 
3/19/2010 - 01:19 AM | Comments [10] | Categories: .NET | DotNetGerman Bloggers | Tutorials | WinDbg
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Wie Stephen Walter in seinem Blog angekündigt hat, stoppt Microsoft die Entwicklung des eigenen clientseitigen ASP.NET Ajax Library und steuert stattdessen Code zu jQuery bei. Bedeutet Microsoft hat jQuery nicht "übernommen", sondern steuert wie jeder andere einfach nur Quellcode bei bzw. schlägt Features vor, die anschließend durch das jQuery Team geprüft werden.

Da die Beta der ASP.NET Ajax Library nun bereits seit November verfügbar war, überraschte mich diese Ankündigung ein wenig. Andererseits fragte ich mich, je tiefer ich mich mit der Library beschäftigte, sowieso weshalb man den clientseitigen Teil neben jQuery überhaupt brauchen würde.

Jedem der sich nun genötigt fühlt, jQuery zu lernen, dem kann ich nur wärmstens Dave Wards Blog empfehlen. Außerdem ist seine Tekpub jQuery Serie die er gemeinsam mit James Avery aufzeichnet natürlich auch Pflichtprogramm.


Kick it on dotnet-kicks.de
 
3/17/2010 - 10:41 AM | Comments [0] | Categories: .NET | Ajax | DotNetGerman Bloggers | jQuery
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

In den letzten 5 Einträgen meines Blogs habe ich über verschiedene Möglichkeiten geschrieben Ajax in einer ASP.NET Webforms zu implementieren.

Angefangen mit dem manuellen Weg über das XmlHttpRequest Objekt ging es weiter zu Client Callbacks, dem Updatepanel, dem ASP.NET Ajax Framework sowie jQuery.

Der Fokus meiner Beiträge lag darauf nicht nur einfach zu zeigen welche Möglichkeiten es gibt, sondern zusätzlich auch zu zeigen, welche Datenmengen über die Leitung gehen und ob bzw. welche Teile des ASP.NET Page Life Cycles durchlaufen werden.

Ich hoffe dass ich bei dem ein oder anderen Leser für manchen Aha Effekt sorgen konnte. Zumindest ging es mir persönlich bei der ersten detaillierten Auseinandersetzung mit dem Thema so. Schließlich ist man als ASP.NET Webforms Entwickler traditionell doch eher auf dem Server zu Hause und realisiert vorerst garnicht welchen Overhead Client Callbacks oder das Updatepanel mit sich bringen.

Mein persönliches Fazit ist, dass Updatepanel und Client Callbacks korrekt eingesetzt in einigen Fällen vielleicht berechtigte Alternativen sind, in den meisten Fällen jedoch zum Ajax Framework oder jQuery gegriffen werden sollte.

Meine zurzeit favorisierte Lösung ist - ähnlich wie bei vielen anderen sicherlich auch - der Einsatz von jQuery.

Da mein letztes Beispiel noch ein wenig aufgebläht war, möchte ich an dieser Stelle zwei kurze Tipps geben, die die Arbeit mit jQuery und Ajax ein wenig erleichtern.

Der erste Punkt beschäftigt sich mit der großen Redundanz zwischen den verschiedenen Ajax Aufrufen:

$("#StaticFileLink").click(function(e) {
    e.preventDefault();
    $.ajax({
        type: "POST",
        url: "Teil5.aspx/ReadStaticFile",
        data: "{}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(msg) {
            $("#content").html(msg.d);
        }
    });
});

$("#HelloWorldLink").click(function(e) {
    e.preventDefault();
    $.ajax({
        type: "POST",
        url: "AjaxDemoService.asmx/HelloWorld",
        data: "{}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(msg) {
            $("#content").html(msg.d);
        }
    });
});

$("#EchoLink").click(function(e) {
    e.preventDefault();
    var number = $("#EchoTextBox").val();
    var jsonData = "{ 'number' : '" + number + "'}";
    $.ajax({
        type: "POST",
        url: "AjaxDemoService.asmx/Echo",
        data: jsonData,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(msg) {
            $("#content").html(msg.d);
        }
    });
});

Bereits beim ersten Blick auf den Quellcode fällt auf, dass die Werte einiger Parameter statisch zu sein scheinen. So haben folgende Parameter stets einen fixen Wert:

  • type
  • contentType
  • dataType

Außerdem ist zumindest in den ersten beiden Aufrufen auch der Eintrag für data gleich.

Ein Weg um diese Redundanz herum zu kommen wäre eine eigene Funktion, die nur die Variablen Parameter entgegen nimmt.

Standardwerte setzen

Eine andere Alternative besteht darin, Standardwerte für jQuery Ajax Aufrufe zu setzen. Dies sähe dann so aus:

<script type="text/javascript">
    $(document).ready(function() {
        $.ajaxSetup({
            type: "POST",
            data: "{}",
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        });
        $("#StaticFileLink").click(function(e) {
            e.preventDefault();
            $.ajax({
                url: "Teil6.aspx/ReadStaticFile",
                success: function(msg) {
                    $("#content").html(msg.d);
                }
            });
        });

        $("#HelloWorldLink").click(function(e) {
            e.preventDefault();
            $.ajax({
                url: "AjaxDemoService.asmx/HelloWorld",
                success: function(msg) {
                    $("#content").html(msg.d);
                }
            });
        });

        $("#EchoLink").click(function(e) {
            e.preventDefault();
            var number = $("#EchoTextBox").val();
            var jsonData = "{ 'number' : '" + number + "'}";
            $.ajax({
                url: "AjaxDemoService.asmx/Echo",
                data: jsonData,
                success: function(msg) {
                    $("#content").html(msg.d);
                }
            });
        });
    });
</script>

Neu hinzugekommen sind die Zeilen 2 - 8. Diese setzen Standardwerte für alle folgenden Ajax Aufrufe. Diese Variante kann einiges an Code sparen, ist allerdings mit Vorsicht zu genießen. Sollte nämlich zum Beispiel ein jQuery Plug-In auf der Seite genutzt werden, dass auch die $.ajax Funktion nutzt, könnte es zu Seiteneffekten kommen. Konkret wäre dies der Fall, wenn einer der per .ajaxSetup gesetzten Parameter nicht überschrieben, aber mit einem anderen Wert erwartet wäre. Typischerweise würde dies für den contentType oder oder dataType geschehen.

Serialisieren - einfach gemacht

Eine weitere Unschönheit des gezeigten Quellcodes besteht darin, dass die String Variante des in JSON notierten Objekts data von Hand zusammen gebaut wurde. Dies ist natürlich nicht sonderlich schick. Abhilfe schafft die Funktion stringify des Objekts JSON. Einige Browser wie Firefox ab Version 3.5 oder IE ab der Version 8 haben bereits ein eingebautes Objekt JSON. Für alle anderen gibt es unter http://www.json.org/js.html eine JavaScript Library zum Download, die entsprechenden Support nachrüstet, falls noch nicht vorhanden.

Konkret sähe dies dann wie folgt aus:

<script src="scripts/json2.js" type="text/javascript"></script>

[ ... ]


$("#EchoLink").click(function(e) {
    e.preventDefault();
    var number = $("#EchoTextBox").val();
    var jsonData = { 'number': number };
    var jsonString = JSON.stringify(jsonData);
    $.ajax({
        url: "AjaxDemoService.asmx/Echo",
        data: jsonString,
        success: function(msg) {
            $("#content").html(msg.d);
        }
    });

Wie man sieht, wird nun in Zeile 9 zuerst ein JavaScript Objekt jsonData in JSON Notation erzeugt. Dies wird anschließend über JSON.stringify in Zeile 10 in einen String konvertiert. Bei diesem konkreten Beispiel mag der Vorteil noch nicht auf der Hand liegen, spätestens bei komplexen Objekten lernt man die Funktion stringify jedoch schnell zu schätzen.

Bei der Recherche zu den Beiträgen dieser Serie bin ich übrigens auf einen sehr gut geschriebenen Eintrag von Roberto Bez gestolpert. Roberto stellt in seinem Blog Post die verschiedenen Varianten kompakt gegenüber. Die Lektüre des Artikels kann ich jedem Ajax interessierten Webforms Entwickler wärmstens empfehlen.

Weiter hat René Drescher-Hackel in einem Kommentar zu meinem letzen Beitrag, dass mit Ajax.NET Professional (AJAX.PRO) eine weitere effiziente Alternative zur Verfügung steht. Ich muss gestehen, dass mir die Existenz dieses Frameworks bis jetzt vollkommen entgangen ist. Gelobe allerdings Besserung und werde es mir gerne ansehen und mein Beispiel damit umsetzen.


Kick it on dotnet-kicks.de
 
3/11/2010 - 09:44 PM | Comments [0] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Eigentlich altbekannt, trotzdem bin ich gerade mal wieder darauf hereingefallen und schreibe des deshalb hier auf:

Ich wollte in einer ASP.NET Webforms Anwendung ein Servercontrol serverseitig über

meinControl.Visible = false;

ausblenden, um es nach einer Nutzeraktion auf dem Client wieder einzublenden:

$(Id$="MeinControl").show();

Leider funktioniert das allerdings nicht, da ein serverseitiges Visible = false dafür sorgte, dass das Control erst garnicht gerendert wurde und somit auf dem Client nicht verfügbar war.

Die simple Lösung im Codebehind:

meinControl.Style.Add(HtmlTextWriterStyle.Display, "none");

Dann klappts auch mit dem Einblenden ;-)

War dieser Beitrag hilfreich? Dann kick ihn doch bitte.


Kick it on dotnet-kicks.de
 
3/11/2010 - 07:41 PM | Comments [2] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Im vierten Teil dieser Serie habe ich gezeigt, wie Pagemethods und Scriptservices mit dem ASP.NET Ajax Framework angesprochen werden können. Im Vergleich zu den vorherigen Teilen, die auf Client Callbacks bzw. das Updatepanel setzen, konnte über diesen Weg die übertragene Datenmenge erheblich verkleinert werden, da unter anderem der Viewstate nicht mehr übertragen werden musste. Außerdem wurde serverseitig nicht mehr der komplette Page Life Cycle durchlaufen, was weitere Performanceverbesserungen mit sich brachte.

Im Gegenzug zu diesen Verbesserungen mussten wir allerdings in Kauf nehmen, dass beim ersten Request drei zusätzliche JavaScript Dateien des Ajax Frameworks mit einer Gesamtgröße von 85 kb geladen wurden.

Hält man im Hinterkopf, dass der Viewstate einer Seite schnell 50 kb und mehr beträgt und dieser bei jedem Ajax Request der vorherigen Methoden hin und her übertragen wurde, lassen sich diese 85 kb jedoch sicherlich leicht verschmerzen.

Doch wie sieht es aus, wenn neben dem Ajax Framework auch jQuery in die Seite eingebunden wurde, um die Oberfläche zu tunen?

In diesem Fall stellt sich die Situation anders dar, nun ist das ASP.NET Ajax Framework clientseitig nämlich reiner Ballast. Zumindest wenn es darum geht Pagemethods und Scriptservcies aufzurufen. Das kann jQuery nämlich auch.

Also weg damit!

Wie müssen wir aber vorgehen, um Pagemethods und Scriptservices aus jQuery rufen zu können?

Zunächst kopieren wir den kompletten Code unseres letzten Beispiels. Wenn wir nun einen Blick auf den Code werfen sehen wir in der *.aspx Datei einen ScriptManager.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">
  <Services>
    <asp:ServiceReference Path="~/AjaxDemoService.asmx" />
  </Services>
</asp:ScriptManager>

Dieser war notwendig, damit das ASP.NET Ajax Framework die JavaScript Proxies erstellte, die uns Aufrufe in der Form PageMethods.Methodenname bzw. WebServiceName.Methodenname erlaubten.

Der ScriptManager ist allerdings überflüssig wenn man Pagemethods und Scriptservices via jQuery rufen möchte. Daher fliegt er raus. Weiter entfernen wir den bestehenden inline JavaScript Code der Seite und räumen Sie noch ein wenig auf. Das Ergebnis sieht wie folgt aus:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Teil5.aspx.cs" Inherits="Teil5" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>ASP.NET Webforms Anwendungen und Ajax (Teil 5): Scriptservices mit jQuery</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <h1>
                ASP.NET Webforms Anwendungen und Ajax (Teil 5): Scriptservices mit jQuery</h1>
            <p>
                <a href="#" id="StaticFileLink">Hier klicken zum Request einer statischen Datei</a><br />
                <a href="#" id="HelloWorldLink">Hier für Hello World WebService klicken </a><br />
                <a href="#" id="EchoLink">Hier für Echo WebService klicken. Geben Sie bitte vorher eine
                    Zahl in nebenstehendem Feld ein: </a>&nbsp;
                <input type="text" id="EchoTextBox" value="4711" />
            </p>
            <div id="content">
                Bitte klicken Sie auf einen der Links, damit dieser Bereich gefüllt wird.
            </div>
        </form>
    </body>
</html>

Der serverseitige Code des vorherigen Beispiels wurde übrigens nicht verändert.

Unsere Seite umfasst nun schlanke 1,6 kb, wie die folgende Abbildung zeigt:

01_Seite

Allerdings kann sie auch noch nichts ;-)

Darum werden wir uns aber in den nächsten Schritten kümmern.

jQuery let's go

Als erstes benötigen wir eine Referenz auf jQuery. Diese erhalten wir über folgenden Script Tag, den wir innerhalb des Head Bereichs der Seite einfügen:

<script src="scripts/jquery-1.3.2.min.js" type="text/javascript"></script> 

Als nächstes fügen wir den Code ein, um den Link zur Anzeige der statischen Datei zum fliegen zu bekommen. Dieser sieht wie folgt aus:

<script type ="text/javascript">
    $(document).ready(function() {
        $("#StaticFileLink").click(function(e) {
            e.preventDefault();
            $.ajax({
                type: "POST",
                url: "Teil5.aspx/ReadStaticFile",
                data: "{}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function(result) {
                    $("#content").html(result.d);
                }
            });
        });
	 });
</script>

Relevant für uns sind die Zeilen 5 - 14. Da es jedoch vielleicht den ein oder anderen Leser gibt, der noch nie mit jQuery gearbeitet hat, möchte ich auch zu den anderen Zeilen ein paar Worte verlieren.

Auffällig sind zunächst die vielen $-Zeichen innerhalb des Codes. Dabei handelt es sich um einen Alias für die Funktion jQuery. Überall wo ein $ steht, könnte man sich also auch jQuery denken.

Die Funktion jQuery nimmt ein DOM Element bzw. einen CSS Selektor als Argument entgegen. In Zeile 2 wird das DOM Element document, also eine Referenz auf unser eigentliches HTML Dokument übergeben. Anschließend wird an das Ereignis ready eine anonyme Funktion gehangen. Das Ereignis ready tritt übrigens auf, sobald das DOM vollständig initialisiert wurde, jedoch ehe weitere Inhalte wie Beispielsweise Bilder herunter geladen wurden. Somit hat man innerhalb des ready Ereignisses zwar Zugriff auf das vollständig initialisierte DOM, muss aber nicht den kompletten Seitenaufbau abwarten.

In Zeile 3 wird die jQuery Funktion mit dem CSS Selektor #StaticFileLink gefüllt, um das DOM Element mit der ID StaticFileLink, also unseren Link, zu selektieren. An das Ereignis click des Links wird wieder eine anonyme Funktion gehangen.

In Zeile 4 wird über e.preventDefault(); die Standardaktion des Links verhindert. In unserem Fall also, dass beim Klick auf dem Link dem Inhalt des href Attributs gefolgt wird. Stattdessen wird ab der Zeile 5 definiert, dass ein ajax Aufruf gefeuert werden muss.

Dabei werden folgende Werte an die verschiedenen Parameter der Funktion ajax übergeben:

Parameter Wert Erklärung
type POST (fix) Ajax Anfragen an Pagemethods oder Scriptsservices müssen aus Sicherheitsgründen immer vom Typ POST sein, um JSON hijacking Angriffe abzuwehren. Eine sehr gute Beschreibung hierzu gibt es im Blog von Phil Haack.
url {Seitenname.aspx}/{Methodenname} Ziel des Ajax Aufrufs. Im Falle einer Page Method nach dem Schema {Seitenname.aspx}/{Methodenname}, im Fall von ScriptServices {Servicename.asmx}/{Methodenname}. Wichtig ist, dass die entsprechende Methode im Falle von Pagemethods statisch ist und mit dem Attribut WebMethod dekoriert wurde. Im Falle eines ScriptServices ist auf die Dekorierung des Services mit dem Attribut ScriptService zu achten.
data JSON String Erwartet die Methode Parameter, sind diese hier als JSON String, und nicht als JSON Objekt zu übergeben! Falls die Methode keine Parameter erwartet, sollte stets ein leeres JSON Objekt übergeben werden (also "{}"). Andernfalls übergibt jQuery als contentType statt application/json automatisch text/html.
contentType application/json; charset=utf-8 (fix) Als Content Type ist stets application/json; charset=utf-8 zu übergeben. Dies ist das Zeichen für das ASP.NET Ajax Framework, serverseitig die Anfrage an den ScriptService Handler und nicht an den Soap Webservice Handler weiterzuleiten.
dataType json (fix). Gibt den Datentyp der Antwort an. Gültige Antworten sind unter anderem json, script, xml oder text. Im Fall von Pagemethods und ScriptServices kommt immer JSON zurück.
succes Anonyme Funktion bzw. Funktionsname Eine Anonyme Funktion bzw. der Name einer Funktion, die die Rückgabe der Methode verarbeitet.
Im Falle von PageMethods und ScriptServices wird die Antwort aus Sicherheitsgründen übrigens immer in ein Objekt Namens d verpackt (siehe auch Zeile 12 des Scripts). Die Ursache hierfür ist ähnlich wie beim Parameter type und wird auch näher in Phil Haacks Blog Eintrag erklärt.

Der Aufruf des Services sollte somit ausreichend erklärt sein. Einzig Zeile 12 bedarf noch einer kleinen Erklärung:

Via $("#Content") holt jQuery sich eine Referenz auf den Zielbereich in mithilfe der Funktion html die Antwort des Ajax Requests geschrieben wird.

Der Code zum Aufruf der beiden Webservice Methoden sieht erwartungsgemäß ähnlich aus:

            $("#HelloWorldLink").click(function(e) {
                e.preventDefault();
                $.ajax({
                    type: "POST",
                    url: "AjaxDemoService.asmx/HelloWorld",
                    data: "{}",
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function(msg) {
                        $("#content").html(msg.d);
                    }
                });
            });

            $("#EchoLink").click(function(e) {
                e.preventDefault();
                var number = $("#EchoTextBox").val();
                var jsonData = "{ 'number' : '" + number + "'}";
                $.ajax({
                    type: "POST",
                    url: "AjaxDemoService.asmx/Echo",
                    data: jsonData,
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function(msg) {
                        $("#content").html(msg.d);
                    }
                });
            });

Erklärenswert ist einzig Zeile 18. Hier wird ein Json String zusammen gesetzt, der später an die entsprechende Methode des Webservices übergeben wird. Dies geht im Falle eines Arguments noch ganz gut, wird aber spätestens bei zwei oder mehr Argumenten sehr lästig. Abhilfe schafft hier die Methode stringify des Objekts JSON. Die Methode überführt ein Json Objekt in einen entsprechenden String. Moderne Browser bringen dieses Objekt inkl. passender Methode gleich mit. In allen anderen Fällen hilft der JSON Parser / Stringifier Json2.

Das Ergebnis der Mühe sieht im Live-Betrieb dann übrigens wie folgt aus:

02_AjaxRequest

Zum Download der jQuery Library ist, wie auf der Abbildung gezeigt, ein weiterer Request notwendig, der knapp 58 kb umfasst. Dies sind fast 30 kb weniger als es im vorherigen Beispiel auf Basis des ASP.NET AJAX (client) Frameworks der Fall waren.

Fazit

Ajax Aufrufe werden mithilfe des jQuery Frameworks zum Kinderspiel. Gerade wenn man jQuery bereits für Oberflächenmanipulationen in einer Seite nutzt, lohnt sich die Anwendung von jQuery für Ajax Aufrufe anstatt der Nutzung des ASP.NET Ajax (Client) Frameworks.


Kick it on dotnet-kicks.de
 
3/1/2010 - 11:33 PM | Comments [2] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Nachdem ich in Teil 1 der Serie den manuellen Ansatz zur AJAX Implementierung gezeigt habe und in den Teilen 2 und 3 auf (halb-)automatische Alternativen einging, möchte ich dieses Mal einen anderen Weg zeigen, ASP.NET Scriptservices in Verbindung mit ASP.NET AJAX.

Rekapitulieren wir noch einmal:

Teil 1 der Serie zeigte einen manuellen Ansatz, der hauptsächlich damit kämpfte, dass man innerhalb des JavaScript Codes selbst darauf achten musste, die verschiedenen Besonderheiten der unterschiedlichen Browser zu berücksichtigen. Dies geschah in den Teilen 2 und 3 zwar automatisch hinter den Kulissen, dafür hatten wir bei diesen Alternativen jedoch das Problem, dass bei jedem AJAX Request sowohl der komplette ViewState übertragen wurde und vor allem auf dem Server auch der komplette Page Life Cycle durchlebt wurde.

Leichtgewichtig durch die Ajax Welt

Heute möchte ich deshalb einen Ansatz zeigen, der die Vorteile der bisher vorgestellten Ansätze vereint, jedoch nicht über die selben Nachteile verfügt.

Konkret bedeutet dies, dass die Implementierung auf Clientseite browserübergreifend stattfinden wird, zwischen Client und Server nur die wirklich notwendigen Daten übertragen werden und der Page Life Cycle auf der Serverseite nicht durchlaufen wird.

Page Methods

Der erste Schritt Richtung einer leichtgewichtigen Implementierung führt über die sogenannten Page Methods. Page Methods sind statische Methoden einer *.aspx Seite, die mit dem [WebMethod] Attribut annotiert werden. Für den Client erscheinen Page Methods wie normale Methoden eines Web Services.

Ausgehend vom Beispielcode der letzten drei Teile der Serie passe ich den Servercode für die Methode, die den Inhalt einer Datei zurückliefert also wie folgt an:

using System.IO;
using System.Web.UI;
using System.Web.Services;

public partial class Teil4 : Page
{
	[WebMethod]
    public static string ReadStaticFile()
    {

	     string fileContent;
         using (var reader =
             new StreamReader(
                 System.Web.HttpContext.Current.Server.MapPath("~/static.html")))
         {

             fileContent = reader.ReadToEnd();
         }
         return fileContent;
     }
}

Als einzige Neuerung im Vergleich zur vorherigen Version fällt das zuvor beschriebene Attribut [WebMethod] auf.

Um die Methode ReadStaticFile nun clientseitig über das ASP.NET Ajax Framwork nutzen zu können, müssen wir innerhalb der *.aspx Seite bei unserem ScriptManager noch die Eigenschaft EnablePageMethods mit dem Wert true versehen.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True"/>

Als Ergebnis rendert der Server bei einem Aufruf der Seite nun folgenden Java Script Code inline in die Seite hinein:

 <script type="text/javascript">
         //<![CDATA[
         var PageMethods = function() {

            PageMethods.initializeBase(this);
            this._timeout = 0;
            this._userContext = null;
            this._succeeded = null;
            this._failed = null;
         }
         PageMethods.prototype = {
             _get_path: function() {
                 var p = this.get_path();
                 if (p) return p;
                 else return PageMethods._staticInstance.get_path();
             },
             ReadStaticFile: function(succeededCallback, failedCallback, userContext) {
                 /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
                 /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
                 /// <param name="userContext" optional="true" mayBeNull="true"></param>
                 return this._invoke(this._get_path(), 'ReadStaticFile', false, {}, succeededCallback, failedCallback, userContext);
             } 
         }
         // und vieles mehr ;-)

Wie man sieht, wird ein JavaScript Objekt PageMethods angelegt, welches über eine Funktion ReadStaticFile verfügt. Die Funktion hat also genau denselben Namen wie unsere serverseitige Funktion. Weiter nimmt Sie drei optionale Parameter entgegen:

  • succeededCallBack: Funktion, die aufgerufen wird, wenn die Ajax Anfrage erfolgreich beendet wurde
  • failedCallBack: Funktion, die aufgerufen wird, wenn die Ajax Anfrage fehlerhaft beendet wurde
  • userContext: Der Inhalt dieser Variable wird an die Funktionen succeededCallBack bzw. faildCallBack durchgereicht.

Der clientseitige Aufruf der Page Method gestaltet sich nun relativ einfach. Als erstes fügen wir der *.aspx Seite einen neuen ClientScriptBlock hinzu:

<script type="text/javascript">       
  function getStaticFile() {
    PageMethods.ReadStaticFile(onSuccess, onError, "MeinKontext");
  }

  function onSuccess(result, userContext, methodName) {
    var contentDiv = $get('content');
    if (userContext) {
      result += "<p>Der UserContext war: <strong>" + userContext + "</strong></p>";
    }

    result += "<p>Der Methoden Name war: <strong>" + methodName + "</strong></p>";
    contentDiv.innerHTML = result;
  } 

  function onError(result) {
    var contentDiv = $get('content');
    contentDiv.innerHTML = result;
  }

</script>

Einstiegspunkt für unseren späteren Code (Click Ereignis unseres Links) ist die Funktion getStaticFile. Sie ruft die Methode ReadStaticFile des Objekts PageMethods. Als Argumente übergeben wir zum einen die Funktionen für die erfolgreiche bzw. fehlerhafte Behandlung der Ajax Anfrage sowie als drittes die Zeichenfolge "MeinKontext".

Die Funktion onSuccess ist nun die Funktion, die die (erfolgreiche) Antwort des Servers verarbeitet. Sie nimmt neben der eigentlichen Antwort des Servers, auch den zuvor übergebenen userContext entgegen. Außerdem wird der Name der aufgerufenen Servermethode im Parameter methodName übergeben. Da JavaScript relativ flexibel ist, was die Anzahl der Parameter angeht und Funktionen einzig durch ihren Namen und nicht anhand ihrer Signatur identifiziert, können die beiden letzten Parameter übrigens auch problemlos weg gelassen werden.

Innerhalb der Methode onSuccess holen wir uns via $get zunächst eine Referenz auf das Div Element in das wir später unser Ergebnis schreiben möchten. $get ist übrigens eine Funktion des ASP.NET Ajax Client Frameworks. Sie ist (vereinfacht gesagt) die Kurzschreibweise für die JavaScript Funktion document.getElementById.

Weiter werden noch userContext (falls vorhanden) und der aufgerufene Methodenname an das Ergebnis angehangen. Im letzten Schritt wird das komplette Ergebnis schließlich in unser Container Div geschrieben.

Der Aufruf  der Funktion getStaticFile geschieht schließlich im Event onclick eines HTML Links (a Tag).

   17     <p>

   18         <a href="#" onclick="getStaticFile()">Hier klicken zum Request einer statischen Datei

   19         </a>

  // ....

   27     </p>

   28     <div id="content">

   29         Bitte klicken Sie auf einen der Links, damit dieser Bereich gefüllt wird.

   30      </div>

Schweres Leichtgewicht

Ruft man die Seite nun im Browser auf, fällt auf, dass neben der eigentlichen Seite auch drei JavaScript Dateien des Microsoft ASP.NET Ajax Client Frameworks geladen werden.

Erster Request der Seite

Die erste Anfrage umfasst nun also 93 kb, von denen sich 85 kb auf die MS AJAX Script Dateien verteilen.

Seinen Vorteil spielt die Herangehensweise jedoch bei den Ajax Anfragen aus. Wie man im folgenden Screenshot sieht, gehen hier gerade einmal 127 Byte über die Leitung. Weiter erkannt man deutlich, dass während des Posts kein ViewState mehr zum Server übertragen wird.

02_ajaxpost

Auch die Antwort ist relativ schmal und beschränkt sich auf das nötigste:

03_ajaxantwort

Die Antwort ist übrigens im JSON (Java Script Object Notation) Format serialisiert. Hätte unser Post Daten in Form von Argumenten für unsere Page Method enthalten, wären diese auch automatisch im JSON Format serialisiert worden. Eine etwas besser lesbare Variante der JSON formatierten Antwort enthalt der Tab JSON des Firebugs:

04_ajaxjson

In unserem Beispiel ist die Antwort zwar noch recht übersichtlich, so dass wir den Tab JSON eigentlich nicht benötigen würden, spätestens bei der Rückgabe komplexer Objektstrukturen lernt an den Tab jedoch schnell zu schätzen ;-)

Es geht noch besser!

Obwohl die bisherige Implementierung bereits ganz gut ist, gibt es noch zwei Kritikpunkte. Zum einen erscheint die Plazierung einer statischen Methode innerhalb der *.aspx Seite ein wenig eigenartig. Da sie weder auf Instanz Exemplar-Felder, noch auf Exemplar-Methoden der Seite zugreifen kann, ist sie innerhalb der Seite eigentlich fehl am Platz.

Außerdem stört mich der automatisch generierte JavaScript Script Block innerhalb der Seite, der die Proxies für die PageMethod Aufrufe zur Verfügung stellt. Lieber wäre mir die Variante einer eingelinkten externen JavaScript Datei.

Beide Punkte können wir glücklicherweise recht einfach lösen, nämlich durch den Einsatz von Script Services. Script Services sind normale Web Services, die mit dem ScriptService Attribut annotiert werden. Erstellt man einen Web Service mit Visual Studio 2008, ist dieses Attribut sogar bereits über der Klasse enthalten. Allerdings wird es automatisch auskommentiert.

Um einen Web Service nun also als Script Service zur Verfügung zu stellen, genügt es den Kommentar, wie in Zeile 13 des folgenden Listings gezeigt, zu entfernen.

   11 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

   12 // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.

   13 [System.Web.Script.Services.ScriptService]

   14 public class AjaxDemoService : WebService {

   15 

   16     [WebMethod]

   17     public string HelloWorld() {

   18         return "Hello World";

   19     }

   20 

   21     [WebMethod]

   22     public string Echo(int number)

   23     {

   24         return string.Format("Sie haben {0} eingegeben.", number);

   25     }  

   26 }

Prinzipiell reicht dies aus, um den Service via Ajax anzusprechen. Um uns das Leben jedoch ein wenig einfacher zu machen, lassen wir das ASP.NET Ajax Framework für uns - analog zur Page Method Lösung - JavaScript Proxies für unsere Methoden Aufrufe erstellen. Dazu fügen wir innerhalb des ScriptManager Tags der Seite ein Kind Element <Services> ein. Diesem Element fügen wir als weiteres Kind Element eine ServiceReference ein, deren Pfad auf unseren Service verweißt.

   10     <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="True">

   11         <Services>

   12             <asp:ServiceReference Path="~/AjaxDemoService.asmx" />

   13         </Services>

   14     </asp:ScriptManager>

An dieser Stelle sei noch einmal darauf hingewiesen, dass dieser Schritt reine Bequemlichkeit und nicht zwingend erforderlich ist. Ohne ihn könnten wir unseren Service genauso via JavaScript rufen und würden genauso JSON als Serialisierungsformat nutzen können. Allerdings müssten wir den clientseitigen Aufruf dann komplett selbst entwickeln und hätten keine Proxies zur Verfügung.

Da wir jedoch bequem sind, lassen wir uns Proxymethoden generieren. Diese können wir dann in unserem bestehenden Client Script Block innerhalb der Seite aufrufen.

Wie die folgende Abbildung zeigt, erhalten wir dann sogar Intellisense und erfahren somit, welche Argumente eine bestimmte Methode entgegen nimmt.

05_VS

Der Rest gestaltet sich ähnlich der Page Method Implementierung. Als wichtiger Unterschied wäre noch zu erwähnen, dass die generierten Proxies standardmäßig in eine externe JavaScript Datei geschrieben werden und somit nicht mehr als Client Script Block in der Seite auftauchen.

Die Kommunikation zwischen Client und Server läuft analog der Page Method Implementierung.

Der Client sendet in seiner Anfrage unter anderem einen Header Content-Type mit dem Wert "application/json; charset=utf-8". Dieser ist besonders wichtig, denn er steuert den Serialisierungsmechanismus des Servers. Hat dieser Header einen anderen Wert, wird die Antwort XML Serialisiert. Somit ist sichergestellt, dass JavaScript Clients JSON erhalten, wohingegen andere Clients XML erhalten.

06_ajaxheader2

Die nächste Abbildung zeigt, dass auch die Post Parameter via JSON serialisiert werden:

07_ajaxpost2

Für die Antwort ist dies natürlich auch weiterhin der Fall:

07_ajaxantwort2

Fazit:

Dieser Teil der Serie hat gezeigt, dass sich die Menge der übertragenen Daten während eines Ajax Requests mit der Hilfe von Page Methods, Script Services und dem ASP.NET Ajax Framework auf ein Minimum reduzieren lassen. Außerdem wird bei dieser Variante der ASP.NET Page Life Cycle im Gegensatz zu den vorherigen Beispielen nicht durchlaufen.

Beides in Kombination kann zu gewaltigen Performancevorteilen verglichen mit Client Callbacks oder dem Updatepanel führen.

Einziger Wermutstropfen ist die Übertragung der 85 kb großen ASP.NET Ajax Framework Java Script Dateien während des ersten Requests.

Eine Abhilfe für dieses Problem werden wir uns im nächsten Teil der Serie ansehen.

Wie immer freue ich mich natürlich über Kommentare zu diesem Blog Beitrag.

 

Dieser Beitrag war hilfreich? Dann bitte nicht vergessen Ihn zu kicken!


Kick it on dotnet-kicks.de
 
2/18/2010 - 10:14 PM | Comments [0] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Zu Anfang hoch gelobt, später ausgepfiffen und verpönt. Was sich im ersten Moment nach der typischen Geschichte deutscher Castingshow Teilnehmer anhört, ist in Wirklichkeit das Schicksal des ASP.NET AJAX Updatepanels.

Woll kaum ein anderes Control hat nach seiner Einführung einen solchen Hype verursacht und ist später so tief gefallen.

Der Reihe nach

Ehe wir im dritten Teil meiner ASP.NET Ajax Serie prüfen werden, ob das Updatepanel seinen schlechten Ruf zu recht oder unrecht hat, möchte ich zunächst jedoch kurz ein paar allgemeine Worte über das Steuerelement verlieren.

Das Updatepanel kam mit den ASP.NET Ajax Extensions in der Version 1.0 für das .NET Framework 2.0 auf den Markt und musste für Visual Studio 2005 noch separat heruntergeladen und installiert werden. Ab Visual Studio 2008 ist das UpdatePanel sofort von Hause aus in der Version 3.5 des ASP.NET Ajax Frameworks enthalten.

Ziel des Updatepanels ist es, ASP.NET Entwicklern die Möglichkeit zu geben Ajax zu implementieren, ohne selbst clientseitigen Code schreiben zu müssen. Im Gegensatz zu den Client Callbacks, die ich im Teil 2 der Serie vorgestellt habe, können ASP.NET Entwickler also in Ihrer Komfortzone - dem Server - bleiben.

Der klassische Ansatz über ASP.NET Postbacks

Basis für den heutigen Blogpost wird wieder eine leicht modifizierte Variante des Beispielcodes der vorherigen beiden Teile der Serie sein. Da das Updatepanel aus Entwicklersicht rein serverseitig arbeitet, sind zunächst einige Veränderungen am bestehenden Quellcode notwendig.

Zum einen sind sämtliche HTML Links (a Tags) durch ASP.NET LinkButtons zu ersetzen. Außerdem habe ich den HTML Div Tag, der das Ergebnis der Click-Aktionen anzeigte, durch ein ASP.NET Literal Control ersetzt. Den zuvor in die Seite integrierten JavaScript Code des letzten Beispiels habe ich komplett entfernt.

    1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Teil3.aspx.cs" Inherits="Teil2" %>

    2 

    3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    4 <html xmlns="http://www.w3.org/1999/xhtml">

    5 <head runat="server">

    6     <title></title>

    7 </head>

    8 <body>

    9     <form id="form1" runat="server">

   10     <h1>

   11         ASP.NET Webforms Anwendungen und Ajax (Teil3): Das Updatepanel</h1>

   12     <div>

   13         <p>

   14             <asp:LinkButton ID="StaticLinkButton" runat="server" OnClick="StaticLinkButton_Click">Hier klicken zum Request einer

   15                 statischen Datei</asp:LinkButton>

   16             <br />

   17             <asp:LinkButton ID="HelloWorldLinkButton" runat="server" OnClick="HelloWorldLinkButton_Click">Hier

   18                 für Hello World WebService klicken</asp:LinkButton>

   19             <br />

   20             <asp:LinkButton ID="EchoLinkButton" runat="server" OnClick="EchoLinkButton_Click">Hier für Echo WebService klicken.

   21                 Geben Sie bitte vorher eine Zahl in nebenstehendem Feld ein:</asp:LinkButton>

   22             &nbsp;<asp:TextBox ID="EchoTextBox" runat="server" Width="40px">4711</asp:TextBox>

   23         </p>

   24     </div>

   25     <asp:Literal ID="contentLiteral" runat="server">Bitte klicken Sie auf einen der Links,

   26     damit dieser Bereich gefüllt wird.</asp:Literal>

   27     </form>

   28 </body>

   29 </html>

Serverseitig hat der Code keine Überraschungen parat. Je HTML Link Button gibt es einen Handler für das Click Event, in dem der Inhalt des Literal Controls gesetzt wird.

    1     protected void StaticLinkButton_Click(object sender, EventArgs e)

    2     {

    3         this.contentLiteral.Text = this.ReadStaticFile();

    4     }

    5 

    6     protected void HelloWorldLinkButton_Click(object sender, EventArgs e)

    7     {

    8         this.contentLiteral.Text = this.CallHelloWorldService();

    9     }

   10 

   11     protected void EchoLinkButton_Click(object sender, EventArgs e)

   12     {

   13         this.contentLiteral.Text = this.CallEchoService();

   14     }

   15 

   16     private string ReadStaticFile()

   17     {

   18         string fileContent;

   19         using (var reader = new StreamReader(Server.MapPath("~/static.html")))

   20         {

   21             fileContent = reader.ReadToEnd();

   22         }

   23         return fileContent;

   24     }

   25 

   26     private string CallHelloWorldService()

   27     {

   28         return new AjaxDemoService().HelloWorld();

   29     }

   30 

   31     private string CallEchoService()

   32     {

   33         int value;

   34         int.TryParse(this.EchoTextBox.Text, out value);

   35         return new AjaxDemoService().Echo(value);

   36     }

Startet man die Seite nun, sieht man dass ein GET Request auf die Seite Teil3.aspx abgesetzt wird. Die Antwort umfasst 2.1 kb.

01ersterRequest

Klickt man nun auf einen der Link Buttons, wird ein Postback ausgeführt und die komlette Seite neu vom Server auf den Client übertragen. Dies überrascht auch nicht, schließlich wurde noch keinerlei Ajax Funktionalität implementiert.

02normalerPostback 

Ein erster Versuch mit dem Updatepanel

Um die Seite nun zu "ajaxifizieren", benötigen wir zwei Controls. Das erste ist der ASP.NET Scriptmanager. Seine Hauptaufgabe ist es, Scripte zum Client zu liefern. Er muss vor allen AJAX Controls innerhalb der Seite erscheinen. Außerdem ist noch anzumerken, dass der Scriptmanager nach dem Highlander prinzip arbeitet: "Es kann nur einen geben!" - nun ja, zumindest pro Seite. Andernfalls würde zur Laufzeit eine InvalidOperationException ausgelöst werden.

Dies bedeutet übrigens, dass ich beim Einsatz von Masterpages keinen ScriptManager auf einer Contentseite platzieren darf, falls  auf der Masterpage bereits ein Scriptmanager genutzt wurde. Stattdessen müsste ich auf der Contentseite einen ScriptmanagerProxy einsetzen. Diesen benötige ich allerdings nur, falls ich auf der Contentseite deklarativ noch weitere Scripte hinzufügen möchte, die auf der Masterpage nicht gebraucht werden.

Da ich langsam etwas ausschweifend werde: Zurück zum Thema!

Das zweite Control, das wir auf unserer Seite brauchen ist ein Updatepanel. Es dient als Container des Bereichs, der später aktualisiert werden soll. Updatepanels dürfen im Gegensatz zum Scriptmanager übrigens mehrfach auf einer Seite vorkommen.

 03toolbox

Nachdem ich Scriptmanager und Updatepanel auf die Seite gezogen, und die die Clientcontrols in das Updatepanel verschoben habe, sieht das Markup meiner Seite wie folgt aus.

    1 <body>

    2     <form id="form1" runat="server">

    3     <asp:ScriptManager ID="ScriptManager1" runat="server">

    4     </asp:ScriptManager>

    5     <h1>

    6         ASP.NET Webforms Anwendungen und Ajax (Teil3): Das Updatepanel</h1>

    7     <asp:UpdatePanel ID="UpdatePanel1" runat="server">

    8         <ContentTemplate>

    9             <p>

   10                 <asp:LinkButton ID="StaticLinkButton" runat="server" OnClick="StaticLinkButton_Click">Hier klicken zum Request einer

   11                 statischen Datei</asp:LinkButton>

   12                 <br />

   13                 <asp:LinkButton ID="HelloWorldLinkButton" runat="server" OnClick="HelloWorldLinkButton_Click">Hier

   14                 für Hello World WebService klicken</asp:LinkButton>

   15                 <br />

   16                 <asp:LinkButton ID="EchoLinkButton" runat="server" OnClick="EchoLinkButton_Click">Hier für Echo WebService klicken.

   17                 Geben Sie bitte vorher eine Zahl in nebenstehendem Feld ein:</asp:LinkButton>

   18                 <asp:TextBox ID="EchoTextBox" runat="server" Width="40px">4711</asp:TextBox>

   19             </p>

   20             <asp:Literal ID="contentLiteral" runat="server">Bitte klicken Sie auf einen der Links,

   21     damit dieser Bereich gefüllt wird.</asp:Literal>

   22         </ContentTemplate>

   23     </asp:UpdatePanel>

   24     </form>

   25 </body>

Da keine Anpassungen im Code Behind meiner Seite notwendig sind, kann ich die Anwendung nun direkt starten.

Ein Blick auf den Netzwerkverkehr zeigt, dass bei der ersten Anfrage der Seite neben der eigentlichen Seite noch drei JavaScript Dateien übertragen wurden. Die Verweise auf diese drei Script Dateien wurden durch den ScriptManager automatisch in die Seite eingetragen. Allerdings werden nun statt der initialen 2.1 kb knapp 90 kb zum Client übertragen.

04ersterRequestMitUpdatePanel

Den eigentlichen Vorteil sehen wir, sobald wir auf einen der Links klicken. Nicht nur, dass die Seite nicht komplett neu geladen wird und somit das lästige flackern beim Seitenneuaufbau entfällt, auch die Menge der übertragenen Daten hat sich auf 1,5 kb reduziert.

05AsyncRequest1Post

Anscheinend haben wir also alles richtig gemacht. Leider aber auch nur anscheinend. Ein näherer Blick auf die Antwort des Servers zeigt, dass neben dem aktualisierten Inhalt auch das Markup für unsere drei Aktionslinks sowie die Textbox erneut übertragen wurden.

06AsyncRequest1Antwort

Das liegt daran, dass wir einen klassichen Updatepanel Anfängerfehler gemacht haben. Wir haben den kompletten Inhalt der Seite in das Updatepanel geschoben. Somit wird dieser auch vollkommen korrekt wieder vollständig vom Server zum Client übertragen.

Das ist selbstverständlich nicht das, was wir wollten. Schließlich ist unsere Ajax Implementierung nur augenwischerei wenn im Hintergrund doch wieder (fast) alles zum Client übertragen wird.

Ein zweiter Anlauf

Was können wir also tun? Nun, da wir nur den Inhalt des Steuerelements contentLiteral aktualisieren möchten, sollten wir offensichtlich auch nur diesen in das Updatepanel aufnehmen. Dazu schieben wir einfach die drei Aktionslinks wieder über das Updatepanel.

Wenn wir die Seite nun starten, stellen wir jedoch fest, dass ein Klick auf einen der Aktionslinks wieder einen vollständigen Postback inklusive komplettem Rendern der Seite auslöst.

Die Ursache hierfür ist, dass ein Updatepanel standardmäßig nur einen partiellen Postback verursacht und sich somit erneuert, wenn eines seiner Childcontrols einen Postback initiert.

Glücklicherweise lässt sich dieses Verhalten jedoch leicht ändern. Für jedes Updatepanel können sogenannte Trigger registriert werden, die einen partiellen Postback auslösen. Ein Trigger ist dabei nichts anderes, als ein Event eines Servercontrols.

Das Registrieren eines solchen Triggers kann entweder über den Designer oder über das ASP.NET Markup geschehen. Um es über das Markup zu lösen, müssen wir unser Beispiel wie folgt anpassen:

    1     <asp:UpdatePanel ID="UpdatePanel1" runat="server">

    2         <ContentTemplate>

    3             <asp:Literal ID="contentLiteral" runat="server">Bitte klicken Sie auf einen der Links,

    4     damit dieser Bereich gefüllt wird.</asp:Literal>

    5         </ContentTemplate>

    6        <Triggers>

    7             <asp:AsyncPostBackTrigger ControlID="StaticLinkButton" EventName="Click" />

    8             <asp:AsyncPostBackTrigger ControlID="HelloWorldLinkButton" EventName="Click" />

    9             <asp:AsyncPostBackTrigger ControlID="EchoLinkButton" EventName="Click" />

   10         </Triggers>

   11     </asp:UpdatePanel>

Lädt man die Seite nun erneut, stellt man fest, dass die Aktionslinks wieder partielle Postbacks auslösen. Außerdem werden auch nur die notwendigen Daten übertragen.

07AsyncRequest2Antwort

Zu viel des Guten

Das Ergebnis sieht eigentlich schon ganz gut, so das man meinen könnte, dass wir nun fertig wären. Leider gibt es aber noch ein kleines Problem. Um dies zu verdeutlichen lege ich ein weiteres Updatepanel auf der Seite an. Dieses Panel erhält ein Literal-Control, welches serverseitig mit der aktuellen Uhrzeit gefüllt wird.

    1     <asp:UpdatePanel ID="UpdatePanel2" runat="server">

    2         <ContentTemplate>

    3             <asp:Literal ID="TimeLiteral" runat="server"></asp:Literal>

    4         </ContentTemplate>

    5     </asp:UpdatePanel>

 

    1     protected void Page_Load(object sender, EventArgs e)

    2     {

    3         this.TimeLiteral.Text = DateTime.Now.ToLongTimeString();

    4     }

Meine Erwartungshaltung wäre, dass bei einem Klick auf einen der drei Aktionslinks nur das ursprüngliche Updatepanel aktualisiert wird, da die Links nur hier als Trigger registriert wurden. Das neue Updatepanel sollte nicht aktualisiert werden.

Ein kurzer Test zeigt dass dem nicht so ist:

08_2UpdatePanels1 08_2UpdatePanels2

Wie die beiden Abbildungen zeigen, wurde nicht nur der Bereich des ersten Updatepanels, sondern auch der des zweiten Updatepanels aktualisiert.

Glücklicherweise lässt sich dieses Verhalten jedoch steuern! Das Updatepanel verfügt über eine Eigenschaft UpdateMode. Diese gibt an, wann sich das Updatepanel aktualisieren soll. Standardmässig ist diese Eigenschaft mit dem Wert Always belegt. Stellt man den Wert jedoch auf Contidional, wird das Updatepanel nur noch aktualisiert, wenn entweder eines seiner Childcontrols einen Postback initiert hat, einer der registrierten Trigger ausgelöst wurde, oder explizit die Methode Update serverseitig gerufen wurde.

Die folgenden Abbildungen zeigen die Auswirkung der Änderung des UpdateModes:

08_2UpdatePanels3

08_2UpdatePanels4

08_2UpdatePanels5

Fazit

Richtig eingesetzt ist das Updatepanel meiner Meinung nach besser als sein Ruf und für einfache Szenarien durchaus geeignet. Ajax Funktionalitäten können mit wenig Aufwand und ohne manuellen Scriptcode implementiert werden.

Achtet man darauf, nicht die komplette Seite in ein Updatepanel zu integrieren und den UpdateMode auf Conditional zu setzen, kann das Updatepanel in vielen Szenarien guten Gewissens genutzt werden. Man sollte sich allerdings darüber im Klaren sein, dass serverseitig bei jeder Anfrage der komplette Page-Lifecycle durchlaufen wird. Zeitaufwändige Operationen im Page_Load können - ähnlich wie bei den Client Callbacks - die Ajax Anfragen demnach erheblich verlangsamen!

Was man machen kann, wenn man den Pagelifecycle nicht komplett durchlaufen möchte, werde ich übrigens im nächsten Teil dieser Serie zeigen.


Kick it on dotnet-kicks.de
 
2/12/2010 - 12:51 AM | Comments [0] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

In meinem letzten Blog Eintrag habe ich erklärt, wie Ajax von Hand mit Hilfe des XMLHttpRequest Objekts implementiert werden kann.

Eine Alternative zu dieser händischen Implementierung ist die Nutzung des ASP.NET Client Callback Frameworks, welches mit der Version 2.0 von ASP.NET erschien.

Um Client Callbacks nutzen zu können muss eine ASP.NET Seite bzw. ein ASP.NET Control das Interface ICallbackEventHandler implementieren. Das Interface definiert die beiden Methoden:

RaiseCallbackEvent ist die Methode, den Aufruf des Clients entgegen nimmt. GetCallbackResult gibt das Ergebnis der serverseitigen Verarbeitung an den Client zurück.

Startet der Client eine Anfrage an eine Seite / ein Control welches ICallBackEventHandler implementiert, wird eine reduzierte Variante des ASP.NET Page Lifecycle / ASP.NET Control Lifecycle ausgeführt. Im wesentlichen entspricht die Callback Variante der "klassichen" Postback Variante, es fehlen jedoch die Methoden, die sich um das Rendering des Outputs kümmern. Außerdem werden keine Postback Events ausgeführt.

Das ganze sieht bezogen auf die Phasen also ungefähr so aus:

04lifecycle

Los gehts

Sehen wir uns das ganze nun einmal an einem konkreten Beispiel an. Als Ausgangsbasis soll eine modifizierte Kopie der Seite des letzten Beispiels mit folgendem Markup dienen:

    1     <div>

    2         <p>

    3             <a href="#">Hier klicken zum Request einer

    4                 statischen Datei </a>

    5             <br />

    6             <a href="#">Hier für Hello World WebService klicken </a>

    7             <br />

    8             <a href="#">Hier für Echo WebService klicken. Geben Sie bitte vorher eine Zahl in nebenstehendem Feld ein: </a>&nbsp;

    9             <asp:TextBox ID="TextBox1" runat="server" Width="40px">4711</asp:TextBox>

   10         </p>

   11     </div>

   12     <div id="content">

   13         Bitte klicken Sie auf einen der Links, damit dieser Bereich gefüllt wird.

   14     </div>

 

Der Server

Auf Serverseite müssen wir nun in unserer Seite das Interface ICallbackEventHandler implementieren. Ausgehend von unserem Markup gilt es drei Operationen zu unterstützen:

  • Zurückgeben des Inhalts einer statischen Datei
  • Aufruf einer parameterlosen Methode eines Webservices, die "Hello World" zurück gibt
  • Aufruf einer Methode Echo eines Webservices, welche einen numerischen Wert entgegen nimmt und einen String zurück gibt.

Welche Operation auszuführen ist, unterscheiden wir anhand des einzigen Arguments der Methode RaiseCallbackEvent: eventArgument.

    1 public partial class Teil2 : Page, ICallbackEventHandler

    2 {

    3     private string callbackResult;

    4 

    5     public void RaiseCallbackEvent(string eventArgument)

    6     {

    7         switch (eventArgument)

    8         {

    9             case "static":

   10                 this.ReadStaticFile();

   11                 break;

   12             case "HelloWorld":

   13                 this.CallHelloWorldService();

   14                 break;

   15             case "Echo":

   16                 this.CallEchoService();

   17                 break;

   18             default:

   19                 this.ReturnUnknownOperationError();

   20                 break;

   21         }

   22     }

   23 

   24     public string GetCallbackResult()

   25     {

   26         return this.callbackResult;

   27     }

Je nach Wert des Arguments eventArgument wird eine andere Methode zur weiteren Verarbeitung aufgerufen. Jede dieser Methoden schreibt ihr Ergebnis wiederum in das Feld callbackResult. Dieses wird wiederum als Ergebnis der Methode GetCallbackResult an den Client zurück gegeben.

Der Inhalt der verarbeitenden Methoden sieht wie folgt aus:

Auslesen der statischen Datei:

    1 private void ReadStaticFile()

    2 {

    3     using (var reader = new StreamReader(Server.MapPath("~/static.html")))

    4     {

    5         this.callbackResult = reader.ReadToEnd();

    6     }

    7 }

Aufruf der Methode HelloWorld des Webservices:

    1 private void CallHelloWorldService()

    2 {

    3     this.callbackResult = new AjaxDemoService().HelloWorld();

    4 }

Aufruf der Methode Echo des Webservices:

    1 private void CallEchoService()

    2 {

    3     int value;

    4     int.TryParse(this.TextBox1.Text, out value);

    5     this.callbackResult = new AjaxDemoService().Echo(value);

    6 }

Wie man beim Aufruf der Webservice Methoden sieht, geschieht dieser Aufruf nun vom Server aus und nicht vom Client. Dadurch dass im Gegensatz zum vorherigen Beispiel der Client nun nicht mehr direkt den Service ruft, musste ich mir einen Proxy auf Serverseite erstellen.

Schön, aber wie komme ich nun auf den Server?

Bisher haben wir zwar geklärt, was auf dem Server alles geschehen muss, damit Client Callbacks funktionieren, offen ist allerdings noch, wie man überhaupt vom Client auf den Server kommt.

Der Aufruf des Servers geschieht über die JavaScript Funktion WebForm_DoCallback. Einen entsprechenden Aufruf der Methode kann man sich über den ClientScriptManager der Seite anhand der Methode GetCallbackEventReference erzeugen lassen. Zeile 5 des folgenden Listing demonstriert dies.

    1 protected void Page_Load(object sender, EventArgs e)

    2 {

    3     var clientScript = Page.ClientScript;

    4     string callbackReference =

    5         clientScript.GetCallbackEventReference(this, "argument", "requestFinished", "context", true);

    6 

    7     string script = string.Format("function sendAjaxRequest(argument, context){{\n {0}; }}", callbackReference);

    8 

    9     if (!clientScript.IsClientScriptBlockRegistered("callback"))

   10     {

   11         clientScript.RegisterClientScriptBlock(this.GetType(), "callback", script, true);

   12     }

   13 }

Die Methode GetCallbackEventReference hat in der höchsten Überladung folgende Parameter:

  Parameter Bedeutung
control Servercontrol, dass den Callback behandelt.
argument Argument, das an die Methode RaiseCallbackEvent des behandelnden Controls geschickt wird.
clientCallback Name der Javascript Funktion, die die Rückgabe des Servers im Erfolgsfall behandelt.
context Javascsript Code, der ausgeführt wird, ehe die Anfrage an den Server geschickt wird. Achtung, hierbei handelt es sich nicht um einen JavaScript String, sondern um einen Befehl / eine Funktion
clientErrorCallback Name der Javascript Funktion, die die Rückgabe des Servers im Fehlerfall behandelt. In meinem Beispiel habe ich diesen Paramter nicht gesetzt.
useAsync Boolscher Wert, der angibt, ob die Anfrage asynchron (true) oder synchron (false) abgesetzt werden soll.

Der Einfachheit halber verpackt man den generierten Aufruf der Methode WebForm_DoCallback in eine eigene JavaScript Funktion, die nur noch die wirklichen variablen Parameter argument und context entgegen nimmt, wie in Zeile 7 geschehen. Anschließend registriert man wie in Zeile 9 - 11 noch einen ClientScriptBlock für diese Funktion, der wie folgt in die Seite gerendert wird.

    1 <script type="text/javascript">

    2     //<![CDATA[

    3     function sendAjaxRequest(argument, context) {

    4         WebForm_DoCallback('__Page', argument, requestFinished, context, null, true);

    5     } //]]>

    6 </script>

Die generierte Methode sendAjaxRequest kann man anschließend wie folgt aufrufen:

    1             <a href="#" onclick="sendAjaxRequest('static', showMessage());">Hier klicken zum Request einer

    2                 statischen Datei </a>

    3             <br />

    4             <a href="#" onclick="sendAjaxRequest('HelloWorld', '');">Hier

    5                 für Hello World WebService klicken </a>

    6             <br />

    7             <a href="#" onclick="sendAjaxRequest('Echo', null);">Hier

    8                 für Echo WebService klicken. Geben Sie bitte vorher eine Zahl in

    9             nebenstehendem Feld ein: </a>

Zeile 1 zeigt, wie der Parameter context genutzt wird. Ich übergebe einfach die JavaScript Funktion showMessage als Argument. Diese wird nun automatisch aufgerufen, ehe der Request gestartet wird. Dies ist zum Beispiel bei länger dauernden Ajax Aufrufen sinnvoll, bei denen man dem Benutzer einen Hinweis anzeigen möchte, wie folgendes Listing zeigt:

    1 <script type="text/javascript">

    2     function showMessage() {

    3         var contentDiv = document.getElementById('content');

    4         contentDiv.innerHTML = 'Lade...';

    5     }

    6 </script>

Wichtig ist, dass der Parameter keinen String entgegen nimmt (wie leider in vielen Beispielen fälschlicherweise gezeigt), sondern eine JavaScript Funktion!

Eigentlich Interessant für unser Beispiel ist jedoch nicht der Parameter context, sonder der Parameter argument. Der Inhalt dieses Parameters (und nur dieser!) wird an die Methode RaiseCallbackEvent des Servers übergeben. Er steuert somit die auszuführende Funktionalität.

Alles beim Alten?

Spielt man nun ein wenig mit der vorgestellten Lösung herum, bemerkt man schnell, dass der Aufruf der Methode Echo stets ausgibt, das man 4711 eingegeben hat - auch wenn der Wert der Textbox zwischenzeitlich geändert wurde.

Ursache hierfür ist, dass ASP.NET Client Callbacks für alle Formularfelder stets die initialen Werte zurücksendet.

Screenshot der Beispielanwendung

Abhilfe schafft folgender JavaScript Code:

    1 __theFormPostData = '';

    2 WebForm_InitCallback();

Diesen packt man einfach in eine JavaScript Funktion, z. B. mit dem Namen repostForm.

Gibt man diese nun als context während des Aufrufs an:

    1 <a href="#" onclick="sendAjaxRequest('Echo', repostForm());">Hier [...]

werden auch die geänderten Eingaben des Benutzers übertragen.

Screenshot zeigt dass die geänderten Werte übertragen wurden

Fazit

ASP.NET Client Callbacks sind eine schnelle und einfache Lösung um Ajax Funktionalitäten unter ASP.NET ab der Version 2.0 zu implementieren.

Bei aller Einfachheit sollte man jedoch beachten, dass diese Lösung auch einige Schwächen hat:

  1. Auf dem Server wird fast der komplette Page Lifecycle ausgeführt. Daher sollten Codebestandteile, die bei einem Callback nicht zwingend ausgeführt werden müssen immer durch ein if(!Page.IsCallback) ... geklammert werden, um unnötige Wartezeiten zu verhindern.
  2. Bei jedem Callback wird der komplette Viewstate zum Server übertragen. Gerade bei großem Viewstate und schlechter Internetanbindung verlangsamt dieser Umstand die Ajax Anfrage enorm.
  3. Es kann immer nur ein Argument an den Server übergeben werden. Möchte man mehr als eins übergeben, muss man die Werte durch ein Trennzeichen trennen und auf der Serverseite auseinanderpflücken
  4. Das übergebene Argument wird nicht wieder vom Server auf den Client zurück übertragen. Somit ist es in der verarbeitenden Javascript Funktion auf dem Client unmöglich herauszufinden, welche Operation ursprünglich gestartet wurde. Abhilfe schafft auch hier ein zusammengesetzter Rückgabewert, oder aber eine eigene verarbeitende Funktion je Operation.

Hat man diese Schwächen jedoch im Blick, lassen sich mit ASP.NET Client Callbacks wunderbare Ajax Applikationen implementieren.

Unter der Haube arbeiten diese Client Callbacks übrigens genauso wie das händische Beispiel aus dem letzten Blogpost - nämlich mit dem XMLHttpRequest Objekt:

Screenshot des JavaScript Debuggers, zeigt Zugriff auf XMLHttpRequest Objekt


Kick it on dotnet-kicks.de
 
2/5/2010 - 01:20 AM | Comments [7] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Das man mit statischen, oder auch dyanamischen Seiten, die bei jeder Anfrage die komplette Seite neu aufbauen, heute keinen Blumentopf mehr gewinnt, dürfte jedem klar sein. Verwöhnt durch Webanwendungen wie zum Beispiel Google-Maps oder Outlook Web Access erwarten Endanwender Webanwendungen, die einen ähnlichen Bedienkomfort wie Desktop Anwendungen aufweisen.

Als Entwickler solcher Webanwendungen bringt uns dies in die Situation, dass wir unsere Entwicklungstätigkeiten nun nicht mehr rein auf den Server beschränken können, sondern auch auf dem Client aktiv werden müssen, um die hohen Erwartungen unserer Anwender zu erfüllen.

Was "auf dem Client aktiv werden" konkret bedeutet möchte ich in einer kleinen Artikelreihe in meinem Blog beantworten.

Der Client - das unbekannte Wesen

Heutzutage sind eine Reihe Ajax Frameworks verfügbar, die uns die "schmutzige" Arbeit auf dem Weg zur dynamischen Webseite abnehmen wollen. Da es meiner Meinung nach aber nie schaden kann, wenn man weiss was diese magischen Toolsets und Frameworks unter der Haube machen, werden wir heute die Ärmel hoch krempeln und die "Drecksarbeit" auf dem Client selbst erledigen.

Ehe wir los legen, sollten wir zuvor jedoch einen Blick auf die notwendigen Zutaten werfen und kurz klären, worum es sich dabei im einzelnen handelt.

  1. JavaScript

    Das J in AJAX. Eine von vielen Entwicklern zu unrecht gefürchtete und/oder belächelte Client Script Sprache ;-) Der Scriptcode wird entweder direkt in eine Seite eingebettet, oder aber in eine externe Datei ausgelagert. JavaScript wird zur Manipulation des DOM genutzt

  2. das Document Object Model (DOM) des Browsers 

    Das DOM des Browsers ist eine Schnittstelle, die den Zugriff auf das zugrundeliegende (X)HTML Dokument bereitstellt. Über das DOM habe ich die Möglichkeit, Aussehen und Struktur der einer Webseite zu verändern.

  3. das XMLHttpRequest Objekt

    Eines der Objekte des window Objekts des Browsers(zumindest in modernen Browsern. Andernfalls ein ActiveX Objekt). Es ermöglicht einem Script das Absetzen einer (asynchronen) Anfrage an einen Webserver. 

Im Überblick sieht das Zusammenspiel der drei Komponenten ungefähr so aus:

Zusammenspiel zwischen JavaScript, DOM und XMLHttpRequest

Los gehts!

Wir wissen zwar jetzt, was wir alles brauchen, allerdings fehlt immer noch die Antwort, wie die Komponenten nun genutzt werden müssen, um AJAX in die eigene Webseite zu bekommen.

Starten wir dazu mit einem kleinen und sehr einfachen Beispiel.

Wir erstellen eine einfache ASPX Seite, die aus einem Link und einem Platzhalter Bereich bestehen soll. Sobald der Anwender auf den Link klickt, soll dieser Platzhalter mit dem Inhalt einer statischen Datei, welche auf dem Server liegt, gefüllt werden.

Der zugehörige HTML Code der ASPX Seite sieht wie folgt aus:

    1     <form id="form1" runat="server">

    3     <div>

    4         <p>

    5             <a href="#">Hier klicken zum Request einer

    6                 statischen Datei </a>

    7         </p>

    8     </div>

    9     <div id="content">

   10         Bitte klicken Sie den Link, damit dieser Bereich gefüllt wird.

   11     </div>

   12     </form>

Der Link, welcher später die Ajax Aktion initiieren soll, wird in den Zeilen 5 und 6 definiert. Der Bereich welcher später ersetzt werden soll, ist der Inhalt des DIV Tags aus Zeile 9.

Noch ist das ganze jedoch nicht sonderlich dynamisch. Dazu benötigen wir noch etwas JavaScript:

    1     <script type="text/javascript">

    2         var xmlHttp;

    3         window.onload = function() {

    4             initializeXmlHttp();

    5         }

    6 

    7         function initializeXmlHttp() {

    8             if (window.XMLHttpRequest) { // IE7, IE8, Mozilla, Safari, Opera

    9                 xmlHttp = new XMLHttpRequest();

   10             } else if (window.ActiveXObject) {

   11                 try {

   12                     xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); //IE 5.x, 6

   13                 }

   14                 catch (e) {

   15                     alert('Ajax Zugriffe sind aufgrund der aktuellen Sicherheitseinstellungen leider nicht möglich');

   16                 }

   17             }

   18         }

   19 

   20         function sendAjaxRequest(url) {

   21             if (xmlHttp) {

   22                 xmlHttp.open("GET", url, true);

   23                 xmlHttp.onreadystatechange = requestFinished;

   24                 xmlHttp.send(null);

   25             }

   26         }

   27 

   28         function requestFinished() {

   29             if (xmlHttp.readyState==4) {

   30               if (xmlHttp.status==200) {

   31                 var contentDiv = document.getElementById('content');

   32                 contentDiv.innerHTML = xmlHttp.responseText;

   33               }

   34             }

   35         }

   36    </script>

Das Script deklariert zunächst in Zeile 2 eine Variable, die einen Verweis auf das XMLHhtpRequest Objekt hält. Wie zuvor beschrieben ermöglicht dieses das Absetzen einer asynchronen Abfrage an den Server.

Die Funktion initializeXmlHttp ab Zeile 7 füllt dieses Objekt nun mit Leben. Der Code sollte recht selbsterklärend sein. Zeile 8 prüft, ob das XMLHttpRequest Objekt am window Objekt hängt. Falls ja, kann ein neues XMLHttpRequest Objekt erstellt werden. Ist dies nicht der Fall, muss es als ActiveX Objekt erstellt werden.

Die Funktion sendAjaxRequest ab Zeile 20 setzt die eigentliche Abfrage ab. Dazu prüft Sie in Zeile 21 zuerst, ob die Variable xmlHttp initialisiert werden konnte. Wäre dies nicht der Fall, könnte keine Abfrage abgesandt werden. Anschließend wird in Zeile 22 eine asynchrone Verbindung geöffnet mit der Methode open geöffnet. Diese Methode erhält als Parameter die Methode ("GET" oder "POST"), die URL, sowie ein Flag, ob die Anfrage asynchron abgesetzt werden soll.

Zeile 23 gibt eine CallBack-Funktion an (in unserem Fall requestFinished), die aufgerufen werden soll, sobald eine Antwort auf die Anfrage zurück gekommen ist.

Zeile 24 sendet die Anfrage nun schließlich ab.

Die Funktion requestFinished ab Zeile 28 verarbeitet die Antwort des Webservers. Da eine Anfrage verschiedene Phasen durchlebt, prüft die Funktion in Zeile 29 anhand der Eigenschaft readyState ab, ob die Anfrage tatsächlich komplett beendet wurde.

Da zu Ende nicht immer "hat auch geklappt" heißen muss, wird in der nächsten Zeile geprüft, ob die Anfrage auch erfolgreich beendet wurde (HTTP Statuscode 200).

Die eigentliche Verarbeitung findet nun in den Zeilen 31 und 32 statt. Zeile 31 holt sich eine Referenz auf den zu ersetzenden Bereich (unser Div mit der ID Content). Zeile 32 überschreibt diesen Inhalt schlussendlich mit der Antwort des Servers.

Damit das ganze auch funktioniert muss die Funktion sendAjaxRequest aus Zeile 20 noch aus unserem HTML Code heraus aufgerufen werden. Dazu erweitern wir unseren Link aus den Zeilen 5/6 einfach um das Attribut onclick.

    5             <a href="#" onclick="sendAjaxRequest('static.html');">Hier

    6                 klicken zum Request einer statischen Datei </a>

Ausgeführt sieht das ganze dann wie folgt aus:

Erster Aufruf der Seite Nach Aufruf des Links

Ganz nett ...

wir wissen nun also, wie wir statische Dateien nachladen können, aber steht das X in AJAX nicht für XML Webservices?

Genau! Deshalb habe ich das Demoprojekt auch um einen kleinen Webservice mit zwei sinnlosen Methoden erweitert:

    1 public class AjaxDemoService : System.Web.Services.WebService {

    2 

    3     [WebMethod]

    4     public string HelloWorld() {

    5         return "Hello World";

    6     }

    7 

    8     [WebMethod]

    9     public string Echo(int number)

   10     {

   11         return string.Format("Sie haben {0} eingegeben.", number);

   12     }  

   13 }

Wie rufen wir einen solchen Service nun aber per JavaScript auf?

Die Antwort findet sich relativ schnell, wenn man die URL des Webservices im Browser eingibt. In der nun erscheinenden Seite werden sämtliche Methoden des Webservices aufgelistet. Ein Klick auf eine dieser Methoden verrät schlussendlich, wie die Methode aufgerufen werden muss. Im Beispiel der Methode HelloWorld sieht die Ausgabe für eine HTTP Post Anfrage zum Beispiel wie folgt aus:

Beschreibung des POST Aufrufs der Webservice Methode

Für die Methode Echo wird folgende Ausgabe produziert:

4_echo_http_post

Im Vergleich zu unserer bisherigen Vorgehensweise beim dynamischen Nachladen der statischen Datei fällt auf, dass die Methode, über die die Anfrage gestartet wird, nun nicht mehr GET ein kann, sondern POST sein muss.

Außerdem wird nun auch ein bestimmter Content-Type, nämlich application/x-www-form-urlencoded benötigt. Speziell bei der Methode Echo fällt auf, dass außerdem nun auch Daten mit an den Server gesendet werden müssen.

Um die veränderten Anforderungen abdecken zu können, erweitere ich das bestehende JavaScript um eine neue Funktion:

    1         function postAjaxRequest(url, data) {

    2             if (xmlHttp) {

    3                 xmlHttp.open("POST", url, true);

    4                 xmlHttp.onreadystatechange = requestFinished;

    5                 xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

    6                 xmlHttp.send(data);

    7             }

    8         }

 

Die neuen Funktion, postAjaxRequest ähnelt der vorherigen Funktion sendAjaxRequest. Die veränderten Anforderungen ließen sich relativ einfach integrieren. In Zeile 3 wurde aus einem GET ein POST. In Zeile 5 wird der zusätzlich benötigte Content-Type gesetzt. In Zeile 6 wird der Methode send nun nicht mehr fix null als Datenparameter übergeben. Stattdessen wird das neue Argument data weiter gereicht.

Der HTML Code musste auch nur unwesentlich erweitert werden. Er erhält zwei neue Links zum Aufruf der beiden Webmethoden:

    1             <a href="#" onclick="postAjaxRequest('AjaxDemoService.asmx/HelloWorld', null);">Hier

    2                 für Hello World WebService klicken </a>

    3             <br />

    4             <a href="#" onclick="postAjaxRequest('AjaxDemoService.asmx/Echo', 'number=1');">Hier

    5                 für Echo WebService klicken </a>

Das Ergebnis sieht anschließend wie folgt aus:

Abbildung zeigt den Rückgabewert des Hello World Webservices Abbildung zeigt die Rückgabe des Echo Web Services

Fazit

Ajax in eine Webanwendung zu integrieren ist aus technischer Sicht nicht sonderlich schwierig. Der vorliegende Blog Post hat gezeigt, dass sich selbst ohne "magische" Frameworks mit wenigen Zeilen JavaScript Ajax Funktionalitäten integrieren lassen. Natürlich ist die gezeigte Implementierung nur sehr rudimentär. So wurde das zurückgegeben XML des Webservices zum Beispiel noch nicht geparsed und es fand außerdem so gut wie keine Fehlerbehandlung statt. Dieses in das Beispiel zu integrieren hätte den Rahmen eines Blogposts jedoch vollständig gesprengt. Außerdem gibt es genau aus diesen Gründen auch bereits fertige Frameworks, wie ASP.NET AJAX oder jQuery, die einem genau diese Detailarbeit abnehmen.

Ehe man ein solches Framework einsetzt, sollte man jedoch die Grundlagen dessen kennen, was hinter den Kulissen geschieht. Und genau diese Grundlagen haben wir in diesem Blog Post geschaffen.

Das vollständige Beispiel werde ich übrigens am Wochenende zum Download bereit stellen.


Kick it on dotnet-kicks.de
 
1/29/2010 - 12:51 AM | Comments [4] | Categories: .NET | Ajax | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Ende November mietete ich mir einen Windows 2008 64 Bit VServer, auf dem ich einige Websites hosten wollte.

Einer der ersten Schritte nach der Freischaltung war die Installation des SQL Server 2008 Express inkl. Management Studio Express.

Nun ja, zumindest versuchte ich dies. Denn leider war weder eine manuelle Installation der SQL Server Express 64 Bit Variante erfolgreich, noch half der Weg über den Web Plattform Installer.

Statt dessen bekam ich folgende Fehlermeldung:

Der Befehlszeilenwert INSTANCESHAREDWOWDIR ist ungültig. Stellen Sie sicher, dass der angegebene Pfad gültig und nicht identisch mit dem Pfad für INSTANCESHAREDDIR ist.

Eine kurze Recherche im Netz ergab, dass dieses Problem auf IA64 Systemen auftritt, wenn man den SQL Server nicht im Standardpfad installiert. Als Abhilfe solle man das INSTALLSHAREDWOWDIR als Befehlszeilenparameter setzen.

Interessant, aber ich habe ein X64 System und wollte im Standardpfad installieren. Also scheint die Ursache doch eine andere zu sein.

Was anschließend folgte, waren endlose Nächte in denen ich versuchte den SQL Server zu installieren. Der Trick mit dem Befehlzeilenparameter brachte mich zwar über die Fehlermeldung, dafür konnte ich dann aber weder das SP1 noch das Management Studio Express installieren. Deinstallieren ging auch nicht mehr, denn auch dann kam die Meldung:

Der Befehlszeilenwert INSTANCESHAREDWOWDIR ist ungültig. Stellen Sie sicher, dass der angegebene Pfad gültig und nicht identisch mit dem Pfad für INSTANCESHAREDDIR ist.

Also hieß es nach jeder erfolglosen Installation erst mal: Windows 2008 Server neu installieren (lassen).

Mittlerweile war ich schon kurz davor aufzugeben. Glücklicherweise fand ich folgende Seite:

http://www.cannontrodder.net/2009/07/24/sql-server-2008-x64-on-windows-server-2008-x64-is-utterly-broken/

Hier hatte jemand das exakt gleiche Problem. Im Gegensatz zu mir hatte er jedoch eine Lösung gefunden, die die Installation des SP1 und des Managent Studios erlaubten.

Alles in allem sah meine Vorgehensweise zur Installation des SQL Servers auf einem nackten Windows 2008 System (außer vorinstalliertem PLESK und der VC++ 2005 Runtime war sonst nix drauf) wie folgt aus:

  1. .NET Framework 3.51 herunterladen und installieren
  2. Powershell als Feature im Server Management aktivieren
  3. SQL Server 2008 Express With Tool 64 Bit herunter laden (SQLEXPRWT_x64_DEU.exe)
  4. Starten des Setups, Verzeichnis in das das Setup entpackt wird notieren
  5. Setup Bildschirm geöffnet lassen, jedoch nichts klicken
  6. Öffnen eines Command-Prompts
  7. Navigation in das zuvor notierte Verzeichnis
  8. Eingeben der folgenden Befehlszeile: setup /action=install /INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server\\" /INSTALLSHAREDWOWDIR="C:\Program Files\Microsoft SQL Server Shared\\"
  9. Wizzard durchklicken
  10. Download des Sql Server 2008 SP 1 64 Bit (SQLServer2008SP1-KB968369-x64-DEU)
  11. Starten des SP 1 Setups (lief ohne Probleme)

Ich hoffe, dass ich anderen, die mit dem selben Problem kämpfen mit diesem Eintrag helfen kann!

An dieser Stelle auch noch mal vielen Dank an die Entwickler und Testabteilung des SQL Server 2008 Setups. Ohne euch hätte ich gar nicht gewusst, was ich an den vielen Abenden machen soll ...


Kick it on dotnet-kicks.de
 
1/22/2010 - 03:56 PM | Comments [0] | Categories: DotNetGerman Bloggers | SQL Server | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

vielen Missverständnissen. Eines dieser Missverständnisse ist die häufig anzutreffende Meinung, dass der ASP.NET ViewState für DropDownList Controls nicht abgeschaltet werden darf. Andernfalls würde die DropDownList den vom Anwender gewählten Wert beim Postback "verlieren".

Ein kleines Beispiel

Sehen wir uns zur Verdeutlichung ein kleines Beispiel an. Ein Entwickler - nennen wir ihn Herrn Brause -  hat die Aufgabe eine Webseite zu schreiben, auf der ein Anwender in einer DropDownListe auswählen kann, wie viele Zeilen Quellcode er heute schreiben kann. Nach der Bestätigung der Auswahl soll der entsprechende Wert auf der Webseite angezeigt werden.

Herr Brause macht sich schnell ans Werk und erstellt folgende Seite:

Screenshot der erstellen Webseite, bestehend aus Text und DropDownList 

Die DropDownListe füllt er mit folgendem Code.

    1 using System;

    2 using System.Web.UI.WebControls;

    3 

    4 public partial class _Default : System.Web.UI.Page

    5 {

    6     protected void Page_Load(object sender, EventArgs e)

    7     {

    8         if (!IsPostBack)

    9         {

   10             linesOfCodeDropDown.DataSource = GetLinesOfCodeListItems();

   11             linesOfCodeDropDown.DataBind();

   12         }

   13     }

   14 

   15     static ListItemCollection GetLinesOfCodeListItems()

   16     {

   17         var items = new ListItemCollection();

   18         for (int i = 0; i < 1000; i++)

   19         {

   20             items.Add(new ListItem(string.Format("{0} Codezeilen", i), i.ToString()));

   21         }

   22         return items;

   23     }

   24 

   25     protected void sendButton_Click(object sender, EventArgs e)

   26     {

   27         linesOfCodeLiteral.Text = linesOfCodeDropDown.SelectedItem.Text;

   28     }

   29 }

   30 

Ein erster kleiner Test auf Herrn Brauses Rechner ergibt, dass die Webseite zufriedenstellend läuft. Stolz übergibt er sein Arbeitsergebnis an seinen Chef.

Nicht so schnell!

Kurz nach der Veröffentlichung der Seite mehren sich die Beschwerden von Anwendern, die an Standorten mit schlechten Internetverbindungen arbeiten. Sie klagen über extrem lange Ladezeiten.

Herr Brause nimmt sich dem Problem an und findet schnell heraus, dass sich mehr als ein Drittel der übertragenen Daten aus dem ViewState der Seite zusammen setzt. Von den insgesamt knapp 107 kB der Seite verteilen sich 40 kB auf den ViewState.

Bild der Seiten- und Viewstategröße.

Diese 40 kB werden übrigens nicht nur zum Client heruntergeladen, sondern bei jedem PostBack auch wieder zurück zum Server geschickt. Sie belasten die Seite also doppelt!

Was war noch mal der ViewState?

Ehe wir den ViewState gleich eliminieren werden, möchte ich zuvor schnell erklären, was genau wir überhaupt versuchen los zu werden.

Der ViewState speichert Werte von ASP.NET Controls anhand ihres Namens. Er ist vergleichbar mit einer Hashtable. Diese Name/Wert Paare serialisiert er in ein verstecktes Formularfeld mit dem Namen __VIEWSTATE. Nach einem Postback wird das __VIEWSTATE Feld deserialisiert und der Viewstate somit wieder hergestellt.

In unserem Beispiel sieht das __VIEWSTATE Feld so aus:

Bild des Seitenquelltexts mit Viewstate. 

Der tatsächliche Viewstate der Seite ist um einiges größer! Die Abbildung zeigt nur einen Ausschnitt.

Der ViewState dient übrigens als Datenspeicher für die meisten Controleigenschaften. Die Eigenschaft "DataSource" der DropDownList könnte daher zum Beispiel wie folgt implementiert sein:

    1 public IEnumerable DataSource

    2 {

    3     get

    4     {

    5         return ViewState["DataSource"] as IEnumerable

    6     }

    7 

    8     set

    9     {

   10         ViewState["DataSource"] = value;

   11     }

   12 }

 

Also weg damit!

Um die übertragene Datenmenge seiner Seite zu reduzieren, schaltet Herr Brause also den ViewState der DropDownList ab. Da die DataSource der Liste nach einem PostBack nun nicht mehr automatisch gefüllt wird, entfernt er außerdem die Prüfung auf einen PostBack ehe er die DataSource der Liste füllt:

    1 protected void Page_Load(object sender, EventArgs e)

    2 {

    3     linesOfCodeDropDown.DataSource = GetLinesOfCodeListItems();

    4     linesOfCodeDropDown.DataBind();

    5 }

Die anschließende Prüfung ergibt, dass der ViewState nun nur noch 0,05 kB beträgt und die komplette Seite nun über 40 kB kleiner geworden ist.

Der ViewState beträgt nun nur noch 0.05 kB

Leider ergibt der zweite Test, dass die Seite nun nicht mehr funktioniert. Egal was der Anwender auswählt, die Seite gibt immer "0 Codezeilen" aus. Dies liegt daran, dass der "SelectedIndex" der Liste innerhalb des Button Click Events immer 0 ist.

Im folgenden Screenshot wurde zum Beispiel "5 Codezeilen" ausgewählt und anschließend auf Absenden geklickt.

Obwohl der Anwender den Wert 5 auswählte, wird nur eine 0 dargestellt.

Ist der ViewState also dafür zuständig, die Auswahl des Anwenders in das Servercontrol zu schreiben?

Nein!

Auch wenn diese Vermutung zunächst nahe liegt, ist sie vollkommen falsch. Um das Rätsel zu lösen, muss man sich kurz den ASP.NET Page Life Cycle vor Augen halten.

Dieser teilt sich in folgende Phasen auf:

  1. Page Request
  2. Start
  3. Page Initialization
  4. Load
  5. Validation
  6. Postback event handling
  7. Rendering
  8. Unload

Interessant für unsere Situation sind die Phasen PageInitialization und Load.

In der Phase PageInitialization werden die folgenden Methoden durchlaufen:

OnInit

Alle Controls der Seite sind bereits erstellt worden. Deklarativ in der ASPX Seite festgelegte Werte wurden zugewiesen. ViewState und durch den Anwender gepostete Werte wurden noch nicht in die Controleigenschaften geschrieben

OnInitComplete

Wird aufgerufen, nachdem das die Methode OnInit durchgelaufen ist.

OnPreLoad

Wird aufgerufen, ehe die Phase Load begonnen wird.

In der Phase Load werden unter anderem die folgenden Methoden durchlaufen

LoadViewState

In dieser Methode wird der ViewState zurück in die Seite und die Controls geschrieben. In unserem ursprünglichen Beispiel würde hier also die DataSource Eigenschaft der DropDownList gefüllt werden.

ProcessPostData

Diese Methode schreibt die durch den Benutzer geposteten Werte in die Controls. In unserem Beispiel wird hier also der selektierte Wert der DropDownList gesetzt.

PageLoad

Wenn diese Methode ausgeführt wird, ist die Seite vollständig initialisiert. In unserem Ursprünglichen Beispiel haben wir hier die DataSource der DropDownList gesetzt.

Die Events der einzelnen Controls, also zum Beispiel das Event Click unseres Buttons werden übrigens in einer späteren Phase, nämlich der Phase PostBack Event Handling ausgelöst.

Interessant, aber was hat es mit unserem Problem zu tun?

Auch wenn der kleine Exkurs in Richtung Page Life Cycle sicherlich (vielleicht ;-)) sehr interessant war, stellt sich die Frage, wo der Zusammenhang zu unserem Problem der fehlenden geposteten Daten besteht.

Nun, dies ist eigentlich ganz einfach. Ursprünglich lief unsere Seite wie folgt:

Erster Request:

Setzen der DataSource Eigenschaft in der Methode Page_Load.

PostBack

Automatisches Wiederherstellen der DataSource Eigenschaft in der Methode LoadViewState, anschließend automatisches Zuweisen des durch den Anwender ausgewählten Werts in ProcessPostData.

Durch unsere Änderungen, nämlich das Deaktivieren des ViewStates und der Entfernung der Prüfung auf einen PostBack in Page_Load haben wir nun folgendes Bewirkt:

Erster Request:

Setzen der DataSource Eigenschaft in der Methode Page_Load.

PostBack

Automatisches Zuweisen des durch den Anwender ausgewählten Werts in ProcessPostData. Da die DropDownListe jedoch noch keine DataSource hat, kann dies hier noch nicht funktionieren. Die DataSource wird erst anschließend in Page_Load gesetzt.

Und das bedeutet?

Das bedeutet, dass die "fehlenden" geposteten Werte eigentlich gar nichts mit dem deaktivierten ViewState zu tun hatten. Die Ursache ist einfach die Tatsache, dass wir die DataSource setzen, nachdem die Daten des Anwenders in das Control geschrieben wurden.

Und die Lösung?

Die Lösung ist recht einfach. Der Code, der die DataSource der DropDownList setzt muss einfach ausgeführt werden, ehe ProcessPostData durchlaufen wird. Typischerweise nimmt man dazu die Methode OnInit.

    1 protected override void OnInit(EventArgs e)

    2 {

    3     linesOfCodeDropDown.DataSource = GetLinesOfCodeListItems();

    4     linesOfCodeDropDown.DataBind();

    5 }

    6 

    7 protected void Page_Load(object sender, EventArgs e)

    8 {

    9 }

Fazit

Der ViewState einer DropDownList darf sehr wohl deaktiviert werden. Um das Problem der "fehlenden" geposteten Werte in den Griff zu bekommen, muss lediglich die Zuweisung der DataSource an einen früheren Zeitpunkt innerhalb des Page Life Cycle verschoben werden.

Wer mehr zu dem Thema wissen möchte, sollte sich übrigens in jedem Fall Dave Reeds Artikel Truly Understanding ViewState durchlesen. Eine umfangreichere und vor allem bessere Behandlung des Themas habe ich bisher an keiner anderen Stelle im Web gefunden.

Wer mehr über den ASP.NET Page Life Cycle wissen möchte, dem kann ich den Artikel Der Lebenszyklus einer ASP.NET 2.0 Seite empfehlen.

Tja und wer gar nichts mehr mit ViewState oder Page Life Cycle zu tun haben möchte, der sollte zum ASP.NET MVC Framework greifen;-)


Kick it on dotnet-kicks.de
 
1/22/2010 - 09:43 AM | Comments [3] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Jeder der in einer ASP.NET Webforms Anwendung ein oder mehrere "Ajax-Enabled" Controls eines Komponentenherstellers nutzt kennt das Problem: Die Anzahl der zum Client übertragenen Scripte steigt explosionsartig an.

Dies liegt daran, dass die meisten Komponentenhersteller je Control ein eigenes Script zum Client senden. Generell ist das auch durchaus sinnvoll, denn nur so kann gewährleistet werden, dass der Browser nur die minimal benötigte Anzahl an Scriptcode vom Server herunterladen muss.

Wie der folgende Screenshot zeigt, wird dieser Segen jedoch auch schnell zum Fluch. Die Abbildung zeigt einen Mitschnitt des Netzwerktraffics beim Besuch einer Seite eines aktuellen Projekts. Inhalt der Seite sind ein Scriptmanager, zwei Infragistics Controls, einige ESRI WebADF Controls sowie ein paar eigene Servercontrols, die auch eigene Scripts rendern.

1_Vorher

Die Anzahl zu übertragenden Scripts beträgt laut Screenshot also 37 Stück.

Interessant, aber wo liegt nun das Problem?

Wir wissen zwar nun, dass wir 37 Scripte zum Client übertragen müssen, aber was ist daran so schlimm und warum sollte es uns überhaupt interessieren?

Um das Problem an der Situation zu erkennen, sollten wir uns kurz vor Auge führen, dass die Scripte nicht gemeinsam innerhalb des selben HTTP Requests wie die eigentliche Website übertragen werden. Stattdessen wird je Script (und auch Bild, CSS Stylesheet, etc.) eine eigene Anfrage an den Webserver gestellt.

Jede dieser Anfragen hat eine gewisse "Rüstzeit" oder auch Latenz. Damit ist die Zeitspanne gemeint, die zwischen dem Absetzen des Befehls, eine Anfrage zu starten und dem tatsächlichen Start der Anfrage vergeht. Je nach Entfernung zwischen Client und Server sowie der Qualität der Internetanbindung der beiden variiert der tatsächliche Wert dieser Reaktionszeit.

Und was bedeutet dies nun konkret?

Schauen wir uns zur Verdeutlichung ein kleines Beispiel an. Gehen wir von jemandem mit einer extrem schlechten Anbindung und einer Latenz von 500 ms aus (zugegeben, der Wert ist recht hoch, lässt sich aber schön einfach rechnen ;-))

Um unsere 37 Scriptdateien herunterzuladen, hätten wir nun also

37 Anfragen * 500 ms Latenz + 37 Antworten * 500 ms Latenz = 37 Sekunden.

Diese 37 Sekunden sind reine Wartezeit und kommen zur eigentlichen Übertragungszeit der Scripte hinzu. Nun ist es natürlich nicht so, dass sämtliche Anfragen sequentiell abgearbeitet werden. Allerdings sieht die Standardkonfiguration vieler Browser eine Beschränkung von zwei gleichzeitigen Verbindungen zu einem Hostnamen vor. Diese Anzahl resultiert aus einer Empfehlung der HTTP 1.1 Spezifikation. Heisst, wir müssen unsere 37 Sekunden noch durch zwei Teilen, da zwei gleichzeitige Verbindungen möglich sind. Bleiben immer noch 18,5 Sekunden übrig.

Was können wir dagegen tun?

Um dieses Problem, was tückischerweise während der Entwicklung nur selten auffällt, da die Latenz zwischen lokalem Webserver und lokalem Webbrowser nicht sonderlich hoch sein dürfte ;-), in den Griff zu bekommen, bietet ASP.NET ab der Version 3.51 das Element CompositeScript als Kindelement des Scriptmanagers an.

Dieses Element erlaubt es, einzelne Scripts anzugeben, welche anschließend automatisch zu einem Script zusammengefasst werden.

Das Ganze sieht ungefähr so aus:

<asp:ScriptManager runat="server" ID="sm1">
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name="/Scripts/MeinScript1.js" />
            <asp:ScriptReference Name="/Scripts/MeinScript2.js" />
            <asp:ScriptReference Name="/Scrips/MeinScript3.js" />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>

Problematisch ist nur, dass man die URL der notwendigen Scripts nur für Dateibasierte Scripts kennt. Sobald ein Script aber als eingebettete Ressource über die Webresource.axd nach aussen gerendert wird, ist die URL unbekannt.

Um dieses Problem zu lösen, kann das auf Codeplex erhältliche Control ScriptReferenceProfiler genutzt werden. Wird es auf einer Seite eingesetzt, rendert es eine Liste aller genutzten Scripte heraus. Diese Liste kann 1:1 kopiert und in das Scripts Element des CompositeScript Elements eingefügt werden.

4_ScriptProfilerOutput

Die eingefügten Scripts werden anschließend serverseitig zu einem großen Script hinzugefügt und innerhalb einer einzigen Anfrage herunter geladen.

Nicht so schnell!

Der Versuch, alle 37 Scripte der zuvor genannten Seite in einem CompositeScript Element zusammen zu führen resultiert in folgendem Fehler:

5_ASP_Error

Die Ursache für diesen Fehler ist, dass der Scriptmanager jedes benötigte Script mit einem kryptischen Bezeichner an die URL anhängt. Dies sprengt schnell die maximale Begrenzung einer URL auf 1024 Zeichen.

Die Lösung

Um dieses Problem zu umgehen platziert man einfach mehrere CompositeScript Elemente auf der Seite und verteilt die Scripte auf diese. Da jedes CompositeScript Element einen eigenen Scriptmanager benötigt, nutzt man ab dem zweiten CompositeScript Element statt zusätzlichen Scriptmanagern, von denen es immer nur einen pro Seite geben darf einfach ScriptmanagerProxy Controls.

Die optimale Verteilung der Scripte auf die einzelnen CompositeScript Elemente ist schnell im Trial-and-Error-Verfahren gefunden. In meinem Beispiel sah sie übrigens wie folgt aus:

<asp:ScriptManagerProxy ID="ScriptManagerProxyAjax" runat="server">
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name="MicrosoftAjax.js" />
            <asp:ScriptReference Name="MicrosoftAjaxWebForms.js" />
            <asp:ScriptReference Name="AjaxControlToolkit.Common.Common.js" Assembly="AjaxControlToolkit, Version=3.0.30512.20315, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
            <asp:ScriptReference Name="AjaxControlToolkit.Compat.Timer.Timer.js" Assembly="AjaxControlToolkit, Version=3.0.30512.20315, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
            <asp:ScriptReference Name="AjaxControlToolkit.Compat.DragDrop.DragDropScripts.js"                 Assembly="AjaxControlToolkit, Version=3.0.30512.20315, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
            <asp:ScriptReference Name="AjaxControlToolkit.Animation.Animations.js" Assembly="AjaxControlToolkit, Version=3.0.30512.20315, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
        </Scripts>
    </CompositeScript>
</asp:ScriptManagerProxy>

<asp:ScriptManagerProxy ID="ScriptManagerProxyInfragisticsCommon" runat="server">
    <CompositeScript>
        <Scripts>

...
        </Scripts>
    </CompositeScript>
</asp:ScriptManagerProxy>

...

Gerade wenn man sich mit einem Element nahe an der 1024 Zeichen Grenze befindet, sollte man jedoch im Hinterkopf halten, dass die tatsächliche Url der Seite später meist nicht mehr http://localhost ist, sondern eher http://meinesubdomain.meinedomain.de. Durch die Änderung des Hostnamens während der Produktivsetzung ändert sich also eventuell noch einmal die Länge der URL. Daher im Zweifelsfall lieber ein Script weniger als in der Entwicklungsumgebung möglich in die CompositeScript Tags einfügen.

Es müssen übrigens nicht alle Scripts in CompositeScript Tags gepackt werden. Solche, die nicht verpackt wurden, werden wie gehabt ganz normal weiter als einzelner Script Tag zum Browser herausgerendert.

Und was bringts nun?

Tja, was bringt der ganze Aufwand nun? Da Bilder bekanntlich mehr als Worte sagen, hier ein paar Vorher/Nachher Screenshots der zuvor vorgestellten Seite:

Initial hat die Seite 37 Scripts heruntergeladen.

 1_Vorher

Yahoo's YSlow gab der Seite eine Gesamtnote von E.

 2_Vorher_YSlowGrade

Die Ladezeit betrug knapp 15 s, die Gesamtgröße 734 kb.

 3_Vorher_YSlowSize

Nach den Änderungen wurden nur noch 9 Scripte referenziert.

 6_Firebug_Nachher

Die YSlow Benotung stieg immerhin von E auf D.

 7_Nachher_YSlowGrade

Die Ladezeit reduzierte sich auf knapp 5 s, die Gesamtgröße auf 655 kb.

8_Nachher_YSlowSize


Kick it on dotnet-kicks.de
 
1/14/2010 - 10:16 AM | Comments [0] | Categories: .NET | ASP.NET | DotNetGerman Bloggers | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Als Consultant lebe ich davon, mich und mein Wissen an den Mann zu bringen. Neben stark ausgeprägten Soft Skills, sind tiefgehende technische Fertigkeiten somit unabdingbar. Da sich die IT Welt schneller zu drehen scheint als der Rest der Welt, stellt die Aufrechterhaltung des persönlichen technischen Know Hows eine nicht zu unterschätzende Herausforderung dar.

Diese Herausforderung kann man entweder ignorieren, unkoordiniert und zufällig oder aber geplant angehen. Für 2010 habe ich mich entschieden, die Sache geplant anzugehen.

Dabei bin ich zu folgender Strategie gekommen, die ich an allen Arbeitstagen durchhalten möchte werde. Als Zeitlimit werde ich ein bis zwei Stunden am Tag ansetzen.

Montags: Eine Code Kata durchführen

Jeder der eine Kampfkunst ausübt weiß, dass eine (annähernde) Perfektion nur durch stetige Wiederholung erreicht werden kann. Jede Bewegung wird tausendfach wiederholt und bis ins kleinste Detail perfektioniert.

Nun ist die Softwareentwicklung sicherlich keine Kampfkunst. Zumindest habe ich während der Ausübung meines Berufs bisher weder geblutet, geschweige denn irgendwelche schlimmeren Verletzungen davon getragen. Auch geschwitzt habe ich nur, wenn im Sommer die Klimaanlage abgeschaltet war.

Trotz allem ist auch in der Softwareentwicklung ständiges Üben hilfreich, um den Geist zu schärfen und die eigenen Fertigkeiten zu verbessern (danke an Mario, der mich meine Einstellung zu diesem Thema überdenken lies).

Deshalb ist Montag ab sofort Code Kata Tag. Diese Woche habe ich mich an der Calculator Kata versucht. Eine sehr interessante Erfahrung.

Dienstags: Eine Tekpub Episode ansehen

Was Rob und James hier auf die Beine gestellt haben ist einfach nur großartig. Es gibt wohl keinen effektiveren Weg, sich in ein Thema einzuarbeiten, als sich die Screencasts der beiden anzusehen.

Wer die Seite noch nicht kennt, sollte sofort dort hin gehen und sich die beiden freien Serien sowie die Previews der kostenpflichtigen Inhalte ansehen.

Für 2010 habe ich mir eine Jahresmitgliedschaft für die Tekpub Website gekauft. Seitdem ist Dienstags Tekpub Tag.

Mittwochs: Einen Blog Eintrag schreiben

Bloggen ist aus meiner Sicht aus mehreren Gründen äußerst sinnvoll:

  1. Man teilt sein Wissen mit der Community, wodurch es sich unweigerlich vermehrt
  2. Während man (oder zumindest ich) einen Blog Eintrag schreibt, reflektiert man selbst noch einmal über das Thema, recherchiert vielleicht auch noch ein wenig und lernt dadurch auch selbst dazu.
  3. Man trainiert die Fähigkeit komplizierte Sachverhalte aufzubereiten und einfach darzustellen.

Da ich in der Vergangenheit viel zu unregelmässig gebloggt habe, wird ab sofort jeden Mittwoch gebloggt!

Donnerstags: An einem privaten Übungsprojekten arbeiten

Code Katas und Trainingsvideos sind zwar sehr sinnvoll, einige Sachen lernt man allerdings nur, wenn man versucht, ein Ergebnis zu erzielen. Aus meiner Erfahrung geht dies besonders gut im privaten Umfeld. Generell bin ich bei diesen Projekten viel experimentierfreudiger, als bei einem Kundenprojekt.

Freitags: lesen

Bekanntermaßen bildet lesen. Deshalb sollte lesen auch in dieser Auflistung nicht fehlen. Freitags ist somit Lesetag. Der Lesestoff kann entweder ganz klassisch ein Buch, oder auch ein Fachmagazin sein. Alternativ kann ich mir natürlich auch vorstellen, Online Artikel zu lesen, oder einfach den Code erfolgreicher Open Source Projekte durchzuarbeiten, um daraus zu lernen.

Ist das alles überhaupt machbar?

Auf den ersten Blick sieht es nach einer Menge Arbeit aus. Natürlich stellt sich die Frage, ob neben Familie, Haus, Beruf, der eigenen User Group und nicht zuletzt auch Freizeitaktivitäten überhaupt genug Zeit bleibt, um all dies Woche für Woche zu erledigen.

Ich denke, dass es wahrscheinlich nicht jeden Tag funktionieren wird. Gerade wenn es im Projekt etwas heikler zugeht, werde ich solche Aktivitäten wohl zuerst gegen Überstunden eintauschen. Glücklicherweise bin ich jedoch in der Situation, dass Überstunden Ausnahmen darstellen. Daher bin ich guter Dinge, dass ich mein Vorhaben durchführen kann. Ob dem so ist, kann der aufmerksame Leser jeden Mittwoch Abend beziehungsweise Donnerstag Morgen selbst herausfinden ;-)

Und ihr?

Ich glaube nicht, dass ich der einzige bin, der seine Skills auf der Höhe der Zeit halten möchte. Daher interessiert mich natürlich brennend, wie ihr die Sache angeht. Lasst es mich einfach über die Kommentarfunktion meines Blogs wissen!
Kick it on dotnet-kicks.de
 
1/6/2010 - 10:10 PM | Comments [1] | Categories: .NET | DotNetGerman Bloggers | Vorsätze
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Bis vor kurzem hätte ich die Frage, ob Klassen, Methoden, Eigenschaften und Variablen innerhalb eines Quellcodes Deutsch oder Englisch benannt werden sollen ohne zu zögern mit "Natürlich Englisch" beantwortet.

Mittlerweile bin ich mir dessen nicht mehr so sicher. Seit einiger Zeit befasse ich mich nun mit Domain-Driven-Design und damit verwandten Themen. In der meist englischen Literatur bzw. Blog Beiträgen wird stets darauf hingewiesen, dass man im DDD komplett in die Domäne des Kundens eintauchen und dessen Sprache sprechen sollte.

So schreibt Gojko Adzic zum Beispiel in seinem Buch Bridging the Communication Gap, dass man als Entwickler nicht mit technischen Bezeichnungen arbeiten und diese für den Kunden übersetzen, sondern durch das ganze Projekt hindurch die (Geschäfts-)sprache des Kundens nutzen soll.

Gerade im englischsprachigen Raum kann ich mir dies auch problemlos vorstellen. Wenn bei meinen Kunden ein Artikel in einer Bestellung zum Beispiel OrderItem genannt wird, hätte ich auch kein Problem damit, eine Klasse OrderItem in meinem Code anzulegen. Auch Methoden wie GetOrderItemById, oder GetOrderItemByName würde ich ohne zu zögern in einem Repository unterbringen.

Was aber, wenn wir im deutschsprachigen Raum arbeiten? Der domänenspezifische Begriff des Kundens wäre zum Beispiel Bestellposition.

Heißt dies nun, dass ich eine Klasse Bestellposition anlegen sollte? und ein BestellpositionenRepository? Mit den Methoden GetBestellpositionById und GetBestellpositionByName?

Für mich persönlich klingt dies unheimlich grausam. Natürlich könnte ich die Methoden auch HoleBestellPositionAnhandDerId oder HoleBestellPositionAnhandDesNamens nennen. So würde die vermischte Schreibweise zumindest entfallen. Trotzdem würde ein solcher Code, der englische Schlüsselwörter mit deutschen Klassen und Membern vermischt, eigenartig und fremd auf mich wirken.

Auf der anderen Seite habe ich auch schon genug Situationen erlebt, in denen man das korrekte englische Wort zum Domänenbegriff des Kundens einfach nicht kannte. Also ging man schnell zu leo, um dort eine Übersetzung nachzuschlagen. Ergebnis war dann häufig, man entweder eine unpassende und eher komische Übersetzung benutzte, oder aber andere Entwickler gar nicht wussten, was dieser eigenartige englische Begriff bedeuten könnte. Im schlimmsten Fall ging ein anderer Entwickler also auch zu leo und suchte sich dort eine andere Übersetzung heraus. So habe ich zum Beispiel mehrere Jahre in einem Projekt gearbeitet, in dem für den Deutschen Begriff Artikel die englischen Begriffe Article, Part und Material genutzt wurden. Wie man sich vorstellen kann, trug das nicht gerade zur Lesbarkeit des Codes bei.

Von daher meine Frage an die Community: Wie geht ihr in deutschen Projekten vor? Deutsche Namen? Englische Namen? Beides wild gemischt?

Über zahlreiche Kommentare oder auch Blogantworten würde ich mich sehr freuen!


Kick it on dotnet-kicks.de
 
11/27/2009 - 07:35 PM | Comments [17] | Categories: .NET | DDD | DotNetGerman Bloggers | Feedback erwünscht
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

"Dieser Webpart kann nicht angezeigt werden. Öffnen Sie diese Webseite in einem Windows SharePoint Services-kompatiblen HTML-Editor, wie beispielsweise Microsoft Office SharePoint Designer, um dieses Problem zu behandeln. Falls das Problem weiterhin besteht, wenden Sie sich an Ihren Webserveradministrator."

Dies ist mit großem Abstand meine Sharepoint Lieblingsfehlermeldung. Dahinter kann sich so ziemlich alles verbergen. Kürzlich erhielt ich diese Meldung, nachdem ich einige Änderungen an einem Dataview Webpart (DVWP) bei einem meiner Kunden vorgenommen hatte.

Typischerweise kommt diese Meldung, wenn man ein DVWP in einer Sharepoint Umgebung exportiert und in eine andere Importiert. Ursache ist in solchen Fällen die fehlerhafte Liste GUID, die innerhalb der XSL Transformation sowie als Parameter in den Webpart Eigenschaften gespeichert wurde.

In meinem Fall konnte ich diese Ursache allerdings ausschließen. Interessanterweise wurde der Fehler nämlich nur anonymen Nutzern der Seite angezeigt. Als angemeldeter Nutzer (mit Admin-Rechten) wurde der Webpart fehlerfrei angezeigt.

Nach einer Stunde relativer Ratlosigkeit und sprichwörtlicher Suche der Nadel im Heuhaufen fand ich glücklicherweise die Quelle des Fehlers.

Während der Entwicklung hatte ich die XSL Transformation des Wepart nicht direkt innerhalb des Webparts gespeichert. Stattdessen hatte ich sie in die Style Library ausgelagert. Was ich allerdings vergessen hatte: Die XSL Datei war zwar wunderbar ausgelagert, jedoch nicht veröffentlicht. Somit hatte ich als authentifizierter Benutzer zwar keine Probleme den Webpart anzuzeigen, anonyme Anwender konnten das Herzstück des Webparts - nämlich die Transformation - jedoch nicht zugreifen und erhielten somit (zurecht) die oben genannte Fehlermeldung.


Kick it on dotnet-kicks.de
 
11/16/2009 - 04:01 PM | Comments [0] | Categories: .NET | DotNetGerman Bloggers | Sharepoint
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Während der Webpart Entwicklung kann es nützlich sein, zu wissen ob man sich gerade im Webpart Design Modus befindet. Folgender Codeausschnitt zeigt, wie man an diese Information kommt:
        protected bool IsDesignMode()
        {
            bool designMode = true;
            if (this.Page != null)
            {
                WebPartManager wp = WebPartManager.GetCurrentWebPartManager(this.Page);
                if (wp != null)
                {
                    designMode = (wp.DisplayMode != WebPartManager.BrowseDisplayMode);
                }
            }
            return designMode;
        }
Hilfreich ist dies zum Beispiel, um Validatoren während des Design Modus abzuschalten.


Kick it on dotnet-kicks.de
 
9/17/2009 - 09:29 AM | Comments [1] | Categories: .NET | DotNetGerman Bloggers | Sharepoint | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

In meinem Beitrag zur MSDN Blogparade zum Thema Entwicklungstools zählte ich WinDbg als einen meiner Favoriten auf. In einem Nebensatz erwähnte ich, dass ich bei Interesse gerne ein kleines Tutorial zu diesem Werkzeug schreiben könnte. Die Anzahl der Rückmeldungen auf diesen Beitrag führten zu zwei Schlussfolgerungen:

  1. Mein Blog lesen mehr Leute als ich dachte, und nicht wie vorher vermutet nur meine Frau und meine Mutter.
  2. Das Interesse an einem Tutorial ist definitiv vorhanden. Also werde ich mein Versprechen einhalten und ein kleines Tutorial schreiben.

Sicherlich stellt sich nun die Frage, warum überhaupt so lange gedauert hat, das Tutorial zu verfassen!

Nun, das liegt zum einen daran, dass ich zur Zeit nicht in Köln, sondern in Bonn arbeite. Somit verbringe ich viel weniger Zeit im Zug und habe somit auch viel weniger Zeit zum bloggen. Zum anderen liegt es daran, dass ich die knappe Zeit im Zug nicht zum schreiben, sondern zum Ansehen von Rob Conneries StoreFront Webcasts genutzt habe. An dieser Stelle möchte ich mich als absoluter Fan der Serie bekennen. Wer sich die Webcasts noch nicht angesehen hat, sollte dies unbedingt nachholen! Dann kam auch noch der Urlaub hinzu, so dass dieser Eintrag einfach ein wenig warten musste.

Jetzt aber zurück zum eigentlichen Thema!

WinDbg - Was ist das überhaupt?

Wie in meinem vorherigen Blog Post geschrieben, ist WinDbg ein unmanaged (native) Debugger mit grafischer Benutzeroberfläche. Dank der SOS Erweiterung kann man ihn jedoch wunderbar dazu verwenden, auch managed Code zu debuggen. WinDbg benötigt eigentlich keinerlei Installation auf dem zu debuggenden System und kann somit wunderbar als Geheimwaffe auf einem Schweizer Taschenmesser USB Stick mitgeführt werden.

Wo bekommt man ihn her und was muss bei der Installation beachtet werden?

Herunterladen kann man WinDbg in Form eines MSI Paketes auf der Download Seite der Microsoft Debugging Tools for Windows. Die Installation an sich verläuft recht geradlinig (Next -> Next -> I Agree -> Next -> Finish ;-)). Sind die Debugging Tools installiert, kann der komplette Inhalt des Verzeichnisses wie bereits erwähnt auf einen Stick kopiert werden und wäre auch von dort aus lauffähig.

Bevor WinDbg nun wirklich genutzt werden kann, ist jedoch noch eine kleine Vorarbeit - nämlich die Definition des Symbol-Pfads - notwendig. Andernfalls meldet WinDbg während der Debugging Aktivitäten stets, dass er keine Symbole (PDB-Dateien) findet, was die Übersichtlichkeit ein wenig leiden lässt.

Zur Angabe des Symbol Pfads legt man nun zunächst ein Verzeichnis auf seiner Festplatte/Stick an, zum Beispiel c:\symbols. Anschließend startet man WinDbg und wählt im Menü File den Eintrag Symbol File Path ... aus. Im sich anschließend öffnenden Dialog gibt man über folgenden Befehl an, dass man die Symbole gerne von Microsoft herunter laden und auf der Festplatte im Verzeichnis c:\symbols  speichern würde:

SRV*c:\symbols*http://msdl.microsoft.com/download/symbols

 

Genug der Vorarbeit. Los gehts!

Jetzt, nachdem WinDbg korrekt installiert und konfiguriert ist, möchte ich anhand eines kleinen Beispiels die Funktionsweise zeigen. Source Code sowie die kompilierte Version gibt es übrigens bald auf meiner Hompage zum Download.

Die Applikation um die es sich handelt ist eine kleine Windows Anwendung, die nur aus einem Login Dialog besteht. Gibt der Anwender die korrekten Zugangsdaten ein, erhält er Bestätigungsmeldung:

windbg1  

Sind die Benutzerdaten falsch, kommt eine Fehlermeldung:

windbg2

Das ganze läuft seit einer ganzen Weile recht gut beim Kunden im produktiven Einsatz. Seit kurzem ist jedoch kein Login mehr möglich. Der Kunde meldet, dass trotz 100%ig richtiger Zugangsdaten stets die Meldung "Ungültige Benutzername / Passwort Kombination" Meldung kommt. Eine Log Datei wird leider nicht erstellt, so dass die Ursache des Fehlers derzeit vollkommen offen ist. Ein Blick auf den Quellocde zeigt folgende Zeilen innerhalb des Login-Formulars

private void LoginButton_Click(object sender, EventArgs e)
{
    UserService service = new UserService();
    try
    {
        service.ValidateUser(UserNameTextBox.Text, PasswordTextBox.Text);
        MessageBox.Show("Login erfolgreich");
    }
    catch
    {
        MessageBox.Show("Ungültige Benutzername / Passwort Kombination");
    }
}

Wie man sieht, wird hier Logik über Exceptions gesteuert. Nicht schön, aber vorerst leider nicht zu ändern. Da der Code im Falle irgendeiner Exception die Meldung "Ungültige Benutzername / Passwort Kombination" bringt, liegt die Vermutung nahe, dass irgendein Fehler innerhalb der Methode ValidateUser auftritt, der zu einer Exception führt. Die Frage ist nur: Welche Exception und warum tritt diese überhaupt auf?

Die Ursache des Problems wäre mit einer kleinen Quellcodeänderung schnell gefunden. Eine schnelle Lösung ist zwar genau das, was unser Kunde braucht, jedoch möchte er uns weder zum Debuggen in sein Netzwerk, noch auf Gut Glück neue Versionsstände mit erweiterten Log Nachrichten einspielen lassen. Allerdings willigt er ein, dass wir mit einem USB Stick bewaffnet an einen der betroffenen PCs dürfen. Voraussetzung jedoch ist, dass wir keine Software installieren.

Showtime

Die beschriebene Situation ist ein typisches Einsatzszenario für WinDbg.

Wir starten also WinDbg von unserem USB Stick und wählen das Menü File->Attach to a process. Anschließend wählen wir unsere fehlerhafte Applikation aus der Prozessliste aus und drücken OK. WinDbg sollte nun ungefähr so aussehen:

windbg3

Als nächstes geben wir folgende Kommandos in die Eingabezeile ein:

sxe clr

.loadby sos mscorwks

Falls nun keine Fehlermeldung kommt, haben wir alles richtig gemacht ;-)

sxe clr sagt dem Debugger, dass er bei jeder CLR Exception anhalten soll. Der Befehl ".loadby sos mscorwks" dient dazu, die SOS Extension zu laden. Diese DLL ermöglicht die Untersuchung von Managed Code innerhalb von WinDbg, der ja eigentlich ein Debugger für unmanaged Code ist. Für jede Version der CLR gibt es eine eigene SOS.DLL. Um nun die zum Framework der fehlerhaften Anwendung passende SOS.DLL zu laden, kann man entweder den vollständigen Pfad angeben, oder man lädt die Extension einfach aus dem Pfad, aus dem auch die mscorwks geladen wurden. Die Datei mscorwks gehört zum .NET Framework.

Derzeit befindet sich das Programm immer noch im Haltemodus. Über die Eingabe von g (für Go) bzw. drücken von F5 können wir die Ausführung fortführen.

Als nächstes Klicken wir in unserer fehlerhaften Applikation erneut auf den Button Login, um den Fehler zu provozieren. Ein Wechsel zu WinDbg zeigt, dass die Ausführung aufgrund der Exception angehalten wurde. Außerdem werden folgende Zeilen ausgegeben:

(1144.1380): CLR exception - code e0434f4d (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012ecf0 ebx=e0434f4d ecx=00000000 edx=00000028 esi=0012ed7c edi=0015b718
eip=7c812a6b esp=0012ecec ebp=0012ed40 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\KERNEL32.dll -
KERNEL32!RaiseException+0x52:
7c812a6b 5e              pop     esi

Wir sehen also, dass eine CLR Exception aufgetreten ist. Leider sagt die aktuelle Ausgabe noch relativ wenig über die Ursache aus. Wie kommen wir also an die Details?

Dazu gibt es prinzipiell zwei Möglichkeiten.

Variante 1 ist, über den Befehl

!DumpStackObjects (oder kurz !dso) eine Liste aller Objekte, die aktuell auf dem Stack verwiesen werden, abzurufen.

Das Ergebnis sieht in meinem Beispiel wie folgt aus:

0:000> !dso
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll -
PDB symbol for mscorwks.dll not loaded
OS Thread Id: 0x1380 (0)
ESP/REG  Object   Name
0012ed5c 014c8974 System.Data.SqlServerCe.SqlCeException
0012eda8 014c8974 System.Data.SqlServerCe.SqlCeException
0012edec 014c8974 System.Data.SqlServerCe.SqlCeException
0012edf8 014c8974 System.Data.SqlServerCe.SqlCeException
0012ee20 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ee24 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ee50 014c8974 System.Data.SqlServerCe.SqlCeException
0012ee7c 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef00 014bf388 System.Windows.Forms.MouseEventArgs
0012ef04 014c0430 Codemurai.Tutorial.WinDbg.ExceptionHunting.UserService
0012ef08 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef14 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef18 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef2c 014b075c System.Object[]    (System.Object[])
0012ef30 014bf388 System.Windows.Forms.MouseEventArgs
0012ef34 014c0430 Codemurai.Tutorial.WinDbg.ExceptionHunting.UserService
0012ef50 014c048c System.Text.StringBuilder
0012ef5c 014c04a0 System.String    test
0012ef60 014a6e80 System.String    Data Source=CodemuraiDb2.sdf
0012ef64 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef68 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef6c 014a6b78 System.Configuration.ConnectionStringSettings
0012ef70 014a5f8c System.Configuration.ConnectionStringSettingsCollection
0012ef80 014c04d8 System.Data.SqlServerCe.SqlCeConnection
0012ef84 014a6b78 System.Configuration.ConnectionStringSettings
0012ef88 014a5f8c System.Configuration.ConnectionStringSettingsCollection
0012ef8c 014c045c System.String    wilhelm
0012ef98 014c0430 Codemurai.Tutorial.WinDbg.ExceptionHunting.UserService
0012efb0 014bf388 System.Windows.Forms.MouseEventArgs
0012efb4 013c8b28 System.EventHandler
0012efb8 013c6a04 System.Windows.Forms.Button
0012efc4 014c04a0 System.String    test
0012efc8 014c04a0 System.String    test
0012efcc 014c045c System.String    wilhelm
0012efd0 014c0430 Codemurai.Tutorial.WinDbg.ExceptionHunting.UserService
0012efd4 014c0430 Codemurai.Tutorial.WinDbg.ExceptionHunting.UserService
0012efd8 014c0430 Codemurai.Tutorial.WinDbg.ExceptionHunting.UserService

...

Wir sehen, dass ganz oben auf dem Stack eine SqlCeException liegt. Diese ist unter der Adresse 014c8974 auf dem Heap abgelegt. Details eines Objekts kann man sich über !DumpObj bzw. !do ansehen.

0:000> !do 014c8974
Name: System.Data.SqlServerCe.SqlCeException
MethodTable: 07d8255c
EEClass: 07d04f14
Size: 76(0x4c) bytes
(C:\WINDOWS\assembly\GAC_MSIL\System.Data.SqlServerCe\3.5.1.0__89845dcd8080cc91\System.Data.SqlServerCe.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79330a00  40000b5        4        System.String  0 instance 00000000 _className
7932fe74  40000b6        8 ...ection.MethodBase  0 instance 00000000 _exceptionMethod
79330a00  40000b7        c        System.String  0 instance 00000000 _exceptionMethodString
79330a00  40000b8       10        System.String  0 instance 014c8e0c _message
7932a35c  40000b9       14 ...tions.IDictionary  0 instance 00000000 _data
79330b94  40000ba       18     System.Exception  0 instance 00000000 _innerException
79330a00  40000bb       1c        System.String  0 instance 00000000 _helpURL
7933061c  40000bc       20        System.Object  0 instance 00000000 _stackTrace
79330a00  40000bd       24        System.String  0 instance 00000000 _stackTraceString
79330a00  40000be       28        System.String  0 instance 00000000 _remoteStackTraceString
79332c4c  40000bf       34         System.Int32  1 instance        0 _remoteStackIndex
7933061c  40000c0       2c        System.Object  0 instance 00000000 _dynamicMethods
79332c4c  40000c1       38         System.Int32  1 instance -2146233087 _HResult
79330a00  40000c2       30        System.String  0 instance 00000000 _source
793332c8  40000c3       3c        System.IntPtr  1 instance        0 _xptrs
79332c4c  40000c4       40         System.Int32  1 instance -532459699 _xcode
07d82660  400032a       44 ...CeErrorCollection  0 instance 014c8784 errors

In den Informationen über unser Exception Objekt sehen wir nun, dass es ein Feld _message gibt, dessen Inhalt sich an der Adresse 014c8e0c befindet. An die Details des Felds _message kommen wir wieder über den Befehl !do.

0:000> !do 014c8e0c
Name: System.String
MethodTable: 79330a00
EEClass: 790ed64c
Size: 282(0x11a) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: The database file cannot be found. Check the path to the database. [ Data Source = CodemuraiDb2.sdf ]
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79332c4c  4000096        4         System.Int32  1 instance      133 m_arrayLength
79332c4c  4000097        8         System.Int32  1 instance      101 m_stringLength
793316e0  4000098        c          System.Char  1 instance       54 m_firstChar
79330a00  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  00163700:013a1198 <<
79331630  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  00163700:013a18ec <<

Prima, diese Aussage hat doch gleich eine ganz andere Qualität. Benutzername/Passwort waren wirklich nicht falsch. Einzig die geschluckte Exception sorgte für den Eindruck. Statt dessen konnte die DB nicht gefunden werden. Ein kurzer Blick die App.Config zeigt folgenden Eintrag:

<connectionStrings>
    <add name="CodemuraiDb" connectionString="Data Source=CodemuraiDb2.sdf"
        providerName="Microsoft.SqlServerCe.Client.3.5" />

 

Die DB selbst heißt im Dateisystem jedoch: CodemuraiDb.sdf.

Einen kurzen Eintrag in der Datei Codemurai.Tutorial.WinDbg.ExceptionHunting.exe.config später läuft das Programm wieder wie gewünscht.

Geht das auch schneller?

Selbstverständlich. Sobald unser Code wegen einer Exception steht hätten wir statt !dso und mindestens Zwei mal !do auch einfach !PrintException, oder kurz !pe eingeben können.

0:000> !pe
Exception object: 014c8974
Exception type: System.Data.SqlServerCe.SqlCeException
Message: The database file cannot be found. Check the path to the database. [ Data Source = CodemuraiDb2.sdf ]
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131501

Aber das kann ja jeder ;-) Außerdem haben wir über den anderen Weg direkt noch ein paar Debugging Tipps gelernt.

Zusammenfassung

In diesem Eintrag wurden folgende Befehle besprochen.

Befehl Bedeutung
sxe clr Bei jeder CLR Exception anhalten
.loadby sos mscorwks SOS Extension passend zur .NET Framework Version laden
g Ausführung fortführen
!DumpStackObjects / !dso Auflistung aller Objekte, die auf dem Stack verwiesen werden
!DumpObj /!do <Adresse>  Details zu einem Objekt ansehen
!PrintException / !pe  Details zur aktuellen Exception ansehen

Wie gehts weiter?

Ziel dieses kleinen Beispiels war es, den Einstieg in WinDbg zu erleichtern. Natürlich gibt es noch weitaus mehr, was mit WinDbg angestellt werden kann. So ist der Debugger sehr hilfreich, um Speicherlecks, oder (vermeintliche) Deadlocks zu finden. Auch die Option, einen zuvor durch den Kunden generierten MemoryDump zu analysieren ist sehr interessant.

Sollte also Interesse an einer Fortsetzung bestehen, reicht es einen kurzen Kommentar zu diesem Beitrag zu hinterlassen. Kommen genug Kommentare zusammen, schreibe ich gerne weitere Teile - dieses Mal auch mit weniger Wartezeit ;-)


Kick it on dotnet-kicks.de
 
9/4/2009 - 04:43 PM | Comments [8] | Categories: .NET | DotNetGerman Bloggers | Tutorials | WinDbg
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Wer in den letzten Jahren auch nur am Rande etwas mit der Entwicklung von Webapplikationen zu tun hatte, sollte wissen, dass der Einsatz von Tabellen zur Gestaltung des Layouts nicht mehr wirklich State-Of-The-Art sind. Schön und gut, aber wie sieht es mit Dateneingabefomularen aus? Diese sind häufig zweispaltig und mehrzeilig aufgebaut, so dass der Einsatz von Tabellen nicht komplett abwegig wäre und zumindest noch halbwegs sinnvoll erscheint.

Doch leider weit gefehlt. Jeder, der sich ein solches Eingabeformular von einem Screenreader vorlesen lässt, merkt schnell, dass auch hier Tabellen nicht das Mittel der Wahl sein sollten. Wie sieht nun aber die Alternative aus? In der Praxis stolpert man häufig über wüst zusammen geschusterte DIV Tags mit fixen Positionierungen, deren einziger Zweck in der Emulation von Tabellenzellen besteht. Dies mag zwar bereits ein Schritt in die richtige Richtung sein, aber wenn man ehrlich ist, wurde das Problem nur verlagert.

Weitaus interessanter finde ich Cameron Adams Ansatz, den ich seit einiger Zeit auch erfolgreich in einigen Projekten zum Einsatz gebracht habe. Wer also Eingabeformulare fürs Web erstellen muss, und keine Lust auf Tabellen hat, sollte sich auf jeden Fall diesen Link inklusive der verschiedenen Beispiele und der Quellcodes ansehen.


Kick it on dotnet-kicks.de
 
6/19/2009 - 02:19 PM | Comments [0] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Von heute an veranstaltet die Universität Koblenz sieben Tage lang eine „Sommer-Uni“ mit Themen, die etwas außerhalb des normalen Lehrplans angesiedelt sind. Mit dabei ist die .NET User Group Koblenz, die am 24. Juni zwei Vorträge aus dem .NET-Umfeld beisteuert: „Moderne Anwendungsarchitekturen mit .NET “ und „User Interface Design mit der WPF“.

Was ich mich dabei nur frage: Wieso liegt .NET außerhalb des normalen Lehrplans ;-)


Kick it on dotnet-kicks.de
 
6/19/2009 - 01:45 PM | Comments [1] | Categories: .NET | Community | DNUG Koblenz | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Wer kennt solche Unterhaltungen nicht:

Ich: Der Build-Vorgang schlägt fehl. Wer hat als letztes eingecheckt?

Er: Ich wars

Ich: Herzlichen Glückwunsch! Du hast den Build kaputt gemacht.

Er: Bei mir läufts!

 

Was ist die Moral der Geschichte? Ob es auf dem eigenen Rechner kompiliert ist egal. Der Build Server (und nur der) ist wirklich wichtig!

 

Daher möchte ich aus gegebenem Anlass an dieser Stelle an folgende Zertifizierung erinnern:

worksonmymachine_logo_small 

Alternativ kann das Ganze übrigens auch in Form eines Awards vergeben werden:

works-on-my-machine-starburst

Happy coding ;-)


Kick it on dotnet-kicks.de
 
6/4/2009 - 10:11 PM | Comments [1] | Categories: .NET | DotNetGerman Bloggers | Fun
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Ich weiß, ich bin spät, aber zum Glück noch nicht zu spät. MSDN Deutschland hat zur Blog-Parade aufgerufen und fragt nach den drei Lieblings-Entwickler-Tools.

150x32_BlogParade_anim_thumb

Nachdem Mario, Alex, Jan und viele andere auch schon mit gemacht haben, möchte ich meinen Senf selbstverständlich auch zum Besten geben.

Das Visual Studio zur Grundausstattung eines Entwicklers gehört setze ich mal voraus und lasse es daher außen vor. Nun aber zu meinen Top 3.

  1. Microsoft WinDbg

    windbg

    Ich kann gar nicht aufzählen, wie oft mir dieses Tool schon in unangenehmen Situationen geholfen hat.
    Wer kennt die Situation nicht:
    • Man entwickelt eine Applikation
    • Auf dem Entwicklungs- und Testsystemen läuft Sie großartig
    • Beim Kunden stürzt sie ab
    • Man hat weder eine vernünftige Fehlermeldung, noch einen aussagekräftigen Eintrag in der Log-Datei
    • Der Fehler ist bei uns nicht nachvollziehbar
    • Wir dürfen mit unserem PC nicht ins Kundennetz und Visual Studio oder CoreDbg dürfen wir dort auch nicht installieren
    • „Pech gehabt, lieber Kunde“ oder wirres Code-Ändern ist keine valide Option ;-)

    Wenn es so weit ist, hilft nur noch ein ganz tiefer Griff in die Werkzeugkiste, nämlich der zu WinDbg. Für diejenigen, die ihn nicht kennen: WinDbg ist ein unmanaged (native) Debugger mit grafischer Benutzeroberfläche. Dank der SOS Erweiterung kann man ihn jedoch wunderbar dazu verwenden, auch managed Code zu debuggen. Zugegeben, der Komfort liegt etwas hinter dem des in Visual Studio integrierten Debuggers, aber in der Not nimmt man schließlich, was man bekommen kann. Das tolle an diesem Debugger ist nämlich, dass er nicht installiert werden muss, sondern direkt vom Stick gestartet werden kann. Außerdem "sieht" er etwas mehr als der in VS integrierte Debugger, denn schließlich ist er ja eigentlich ein unmanaged Debugger. Bei Interesse könnte ich mich übrigens dazu verleiten lassen, mal ein WinDbg Einsteiger Tutorial zu schreiben, oder zumindest mein "Cheat-Sheet" hier aufs Blog zu packen. Also einfach die Kommentarfunktion dieses Eintrags nutzen!

  2. Infragistics NetAdvantage

    infragistics 

    »Für den ersten Eindruck gibt es keine zweite Chance« lautet ein geflügeltes Wort. Dass Menschen einander innerhalb weniger Augenblicke beurteilen, ist allgemein bekannt. Da diese Beurteilung vor allem anhand optischer Gesichtspunkte statt findet, achten viele Menschen heutzutage in besonderem Maße auf ihr Äußeres. Nur so lassen sich wichtige erste Begegnungen, wie zum Beispiel ein Vorstellungsgespräch, erfolgreich meistern.

    Ähnlich wie das menschliche Äußere bei einem Vorstellungsgespräch, kann auch das Äußere einer Softwareapplikation – also die Benutzeroberfläche – im Zweifelsfall über Sieg oder Niederlage (kaufen oder nicht kaufen!) entscheiden. Der Grund dafür ist, dass viele Benutzer, vor allem solche, die technisch nicht sonderlich versiert sind, vom Eindruck der Benutzeroberfläche auf die Gesamtqualität der Software schließen. Komplexe, überladene Bildschirmmasken, deren Design an ein typisches Look and Feel von vor zehn Jahren erinnern, mindern potentiell also den kommerziellen Erfolg einer Anwendung. Dies gilt besonders dann, wenn die Konkurrenz ihre Hausaufgaben gemacht hat!

    Reichte es vor 15 Jahren noch vollkommen aus, einfach nur die Bedienung der Applikation mit der Maus zu unterstützen, erwarten Anwender heute doch einiges mehr. Angeregt von innovativen Oberflächenelementen in Applikationen großer Hersteller, wie zum Beispiel Apples Coverflow, das Ribbon in Microsoft Office 2007 oder interaktiv gruppierbarer Tabellen wie in Microsoft Outlook, erwarten Endanwender solche Features auch in anderen, also unseren Applikationen.

    Leider kommt man mit den Bordmitteln, die Microsoft Visual Studio bis zur Version 2008 an dieser Stelle liefert nicht immer ans Ziel. So sind zum Beispiel weder Ribbons, noch interaktiv gruppierbare Tabellen oder Excel ähnliche Diagramme „out-of-the-box“ möglich.

    Möchte man die Anforderungen an eine konkurrenzfähige Oberfläche also erfüllen, kommt man (wirtschaftlich gesehen) kaum um den Einsatz einer 3rd Party UI Komponentensuite herum.

    Die Suite meiner Wahl ist Infragistics NetAdvantage. Die Komponenten sehen nicht nur top aus, sondern bieten auch alle Features, die das Entwicklerherz begehrt.

  3. StyleCop

    Konsistente Quellcode Formatierungen in einem Projekt mit mehreren Entwicklern. müssen dank StyleCop kein Wunschtraum mehr bleiben. Jedem, der im Team entwickelt kann ich dieses Tool nur wärmstens ans Herz legen!

Kick it on dotnet-kicks.de
 
6/4/2009 - 09:51 PM | Comments [7] | Categories: .NET | DotNetGerman Bloggers | Infragistics | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Kürzlich versuchte ich via stsadm -o restore das Sharepoint Backup eines Kundens auf meinem Entwicklungsrechner einzuspielen. Obwohl die Wiederherstellung problemlos lief, kam beim anschließenden Aufrufen der Seite stets nur die statische Meldung: HTTP 500, interner Serverfehler. Sowohl das Eventlog, als auch die Sharepoint-Log Dateien gaben keinen Aufschluss zur Ursache des Fehlers. Auch Google und Co waren wenig hilfreich.

Nach stundenlangem Probieren konnte ich die Ursache glücklicherweise doch finden: Sowohl der Kunde, als auch ich hatten einen englischen MOSS auf einem englischen Windows 2003 Server installiert. Im Gegensatz zu mir hatte der Kunde jedoch zusätzlich die deutschen Sprachpakete für MOSS und WSS 3.0 inkl. aktuellem Servicepack installiert.

Mein Fazit der Geschichte:

In Zukunft werde ich beim Aufsetzen einer Sharepoint Entwicklungsmaschine sofort die Language Packs installieren.


Kick it on dotnet-kicks.de
 
5/18/2009 - 10:44 PM | Comments [0] | Categories: DotNetGerman Bloggers | Sharepoint | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Wer den RSS Feed seines Blogs via Feedburner publiziert, kann mit Hilfe eines FeedFlares sehr einfach einen dynamischen Dotnet-kicks.de Link unter jeden Eintrag setzen.

Dazu ist wie folgt vorzugehen:

  1. Einloggen bei feedburner
  2. Feed zur Verwaltung auswählen
  3. image
  4. Registrierkarte Optimize anklicken.
  5. Links im Menü den Punkt FeedFlare wählen
    image
  6. Neuen Flare hinzufügen
    image
    Eine passende Flare Datei, die von jedem Benutzt werden darf, befindet sich auf meiner Homepage unter http://www.codemurai.de/downloads/kick_it-deflare.xml
  7. Speichern und Aktivieren
    image
  8. Das Ergebnis genießen ;-)
    image

Kick it on dotnet-kicks.de
 
5/18/2009 - 10:25 PM | Comments [0] | Categories: .NET | Community | DotNetGerman Bloggers | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Seit über drei Jahren ist ASP.NET 2.0 nun schon in der Final Version auf dem Markt und bis Mitte letzter Woche habe ich sie übersehen: Die Eigenschaft AssociatedControlID des ASP.NET Label Controls. Bisher habe ich auf den Einsatz dieses Controls möglichst verzichtet, da es in meinen Augen "nur" einen unnötigen Span-Tag um meinen Text setzt.

Habe ich in meiner aspx Seite also folgenden Code:

<asp:Label ID="Label1" runat="server" Text="Ich bin ein Label"></asp:Label>
<asp:TextBox ID="TextBox1" runat="server">Ich bin eine Textbox</asp:TextBox><br />

wird dies zu folgendem Markup:

<span id="Label1">Ich bin ein Label</span>
<input name="TextBox1" type="text" value="Ich bin eine Textbox" id="TextBox1" /><br />

Alles in allem ist dies wenig spektakulär.

Erweitere ich meinen Code nun jedoch um die Eigenschaft AssociatedControlID

<asp:Label ID="Label2" runat="server" AssociatedControlID="TextBox2" Text="Ich bin auch ein Label"></asp:Label>
<asp:TextBox ID="TextBox2" runat="server">Ich bin auch eine Textbox</asp:TextBox></div>

wird das ASP.NET Label nicht mehr als Span-Tag, sondern als HTML Label-Tag gerendert:

<label for="TextBox2" id="Label2">Ich bin auch ein Label</label>
<input name="TextBox2" type="text" value="Ich bin auch eine Textbox" id="TextBox2" />

Schön und gut, aber wo liegt der Vorteil?

Zum einen schafft der HTML Code durch den Zusatz "for=<anderes Control>" eine logische Verknüpfung zwischen Eingabefeld und Beschriftung. Dies kann von Screenreadern ausgelesen werden und verbessert somit die Barrierefreiheit.

Dies ist jedoch nicht alles. Für den Standardbenutzer ergibt sich der Vorteil, dass er nun innerhalb des Browsers mit der Maus einfach auf das Label klicken kann und anschließend automatisch das assozierte Steuerlement den Fokus erhält.

Mein Fazit: ein tolles Feature, was sowohl Usability, als auch Barrierefreiheit mit sehr wenig Aufwand erhöht.

Näheres findet sich übrigens auch bei selfhtml.

Die Frage ist nur: Warum habe ich es so lange übersehen? Vielleicht liegt es daran, dass es unter ASP.NET 1.0 noch nicht existierte und die anderen Neuerungen einfach so viel aufregender waren?


Kick it on dotnet-kicks.de
 
4/30/2009 - 08:58 AM | Comments [0] | Categories: .NET | ASP.NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Dienstag Abend fand ich folgende E-Mail in meinem Postfach:

Dear Andre,
Congratulations! We are pleased to present you with the 2008 Infragistics MVP Award! Your actions in the community have not only promoted the spirit of community but have also helped other professionals in the industry to be more successful each and every day. This award is our way of thanking you for your contributions.

[...]

Thank you again,
Tony Lombardo
Anthony Lombardo
Lead Technical Evangelist
Infragistics, Inc.
Worldwide Evangelism Group

Bis zu diesem Augenblick wusste ich noch nicht einmal, dass Infragistics MVPs auszeichnet. Des Rätsels Lösung: Die Auszeichnung gibt es - laut Tonys Blog - erst seit letztem Dienstag. Demnach bin ich einer der ersten Infragistics MVPs.

Die Auszeichnung kam sehr überraschen für mich und selbstverständlich habe ich mich sehr gefreut und bin extrem stolz. Sie ist in meinen Augen ein toller Weg, um Community Engagement zu wertschätzen.

Mein Dank gilt neben dem Infragistics Team vor allem Nic Goetz!

IgMvpLogoRotatedReflected_43836EBF


Kick it on dotnet-kicks.de
 
4/3/2009 - 03:03 PM | Comments [1] | Categories: .NET | Community | DotNetGerman Bloggers | Infragistics
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Genau ein Jahr und einen Monat ist es nun her, dass Eric Berres, Markus Renning, Thomas Naunheim und ich die .NET User Group Koblenz ins Leben riefen. Ein Jahr und einen Monat? Ist das nicht ein wenig spät für eine Gratulation? Normalerweise schon, aber aufgrund meiner anderen Aktivitäten (Familie, Job, Hausbau, Basta Vortrag, ...) lag mein Blog in den letzten Wochen ein wenig brach, so dass ich erst jetzt dazu komme, der DNUG Koblenz zu gratulieren.

Im Januar 2008, als Eric, Markus, Thomas und ich zum ersten Mal (außerhalb eines Fast-Food-Restaurants ;-)) zur Vorbesprechung zusammen saßen, waren wir uns sehr unsicher, ob das ganze überhaupt funktionieren würde. Wir waren uns vollkommen im unklaren darüber, wie viele Teilnehmer wohl zu den treffen kommen würden. Unsere schlimmste Befürchtung war, dass wir nur zu viert sein könnten und sich alles relativ schnell wieder im Sande verlieren würde. Auch waren wir unsicher, ob wir die Organisation überhaupt geregelt bekommen, da niemand von uns zuvor auf einem Treffen einer anderen DNUG waren. So richtig wusste also keiner von uns, was wir auf unseren Treffen also wirklich machen müssen. Auch hatten wir keine Idee, wie wir an andere Sprecher, intern oder extern, abgesehen von uns selbst, heran kommen könnten.

Glücklicherweise waren unsere Bedenken weitestgehend unnötig. Im Schnitt haben wir ca. 20 Teilnehmer pro Treffen. Sicherlich sind es auch mal nur 10, oder auch mal über 30, normalerweise sollten es jedoch um die 20 sein.

Auch die Sprecher gingen uns nie aus. So konnten wir neben einigen Referenten aus den eigenen Reihen auch - INETA Germany und einiger Community Kontakte sei dank - bekannte Sprecher aus der Community für einen Vortrag bei uns gewinnen. Eines der absoluten Highlights in diesem Zusammenhang war sicherlich der Vortrag von Tony Lombardo,  ASP.NET MVP und Worldwide Lead Evangelist der Firma Infragistics, der während seines Europa Aufenthalts zur TechEd Europe einen Zwischenstopp in Koblenz machte, um uns etwas zum Thema Silverlight zu erzählen.

An dieser Stelle möchte jedoch nicht nur Tony hervorheben, sondern mich auch bei allen anderen Sprechern für ihre tollen Vorträge bedanken.

Im einzelnen wären da zu nennen:

  • Eric Berres: Einführung in DNN, Einführung in die WCF
  • Jan Meinecken: Einführung in die Windows Sharepoint Services
  • Lars Keller: VSTO
  • Markus Kissling: Oracle & .NET  
  • David Kortmann: Eigener Membershipprovider in 12 1/2 Minuten
  • André Krämer: Jetzt lerne ich C#3.0 in 12 1/2 Minuten, SOS nicht nur für Schiffbrüchige, Schätzen statt raten, Design By Contract 
  • Thomas Naunheim: TFS 2008
  • Frank Pommering: Reflection
  • Markus Rennings: Neuerungen in Visual Studio 2008 für ASP.NET, Einführung in das ADO.NET Entity Framework, .NET Framework Debugging, Custom Images für VS Commands 
  • Alexander Rippert: Unit Testing
  • Mario Röcher: Das Decorator Pattern
  • Jens K. Süßmeyer: Einführung in den SQL Server 2008
  • Torsten Weber: Einführung in das Mobile Computing, Keine Zeit für Herzrasen
  • Roland Weigelt: GUI Design für nicht Designer

Außerdem gilt natürlich unseren Sponsoren ein herzlicher Dank!

Außerdem vielen Dank an:

  • Roland Weigelt: Für die tolle "Anleitung" zur Gründung einer User Group
  • Torsten Weber: Für die hilfreichen Tips zur Gründung einer User Group

Wow, die vielen Danksagungen erinnern schon fast an die Oscar Verleihung. Den haben wir zwar (noch :-)) nicht erhalten, dafür hat Microsoft uns jedoch ein tolles Geschenk gemacht. Am Abend des Geburtstagstreffens erhielten wir nämlich die Nachricht, dass wir ins Microsoft CLIP Programm aufgenommen wurden! Vielen Dank (:-))


Kick it on dotnet-kicks.de
 
3/18/2009 - 11:25 PM | Comments [1] | Categories: .NET | DNUG Koblenz | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Heute versuchte ich mein erstes Outlook 2007 Add-In unter Visual Studio 2008 zu entwickeln. Nachdem ich bereits einiges an Code geschrieben hatte, wagte ich den ersten Kompiliervorgang.

Das Ergebnis war allerdings wenig erfreulich, denn anstatt mir ein das Ergebnis meines Kompiliervorgangs anzuzeigen, ging Visual Studio einfach kommentarlos zu.

Um auszuschließen, dass es an meinem Code lag, stellte ich dasselbe zusätzlich noch mit einem leeren Outlook 2007 Add-In nach. (Glücklicherweise) war das Resultat gleich :-)

Nach ein wenig googeln wurde ich darauf aufmerksam, dass die Visual Studio PowerCommands die Ursache sein sollten. Diese habe ich jedoch gar nicht installiert. Also ging die Suche weiter.

Die Lösung fand ich schließlich in folgendem Eintrag:

MSDN Forum - VS 2008 closes at build, Outlook 2007 Add-In

Kurz zusammengefasst: Es ist ein Fehler innerhalb der CLR, der auftritt, wenn Assemblies in einer bestimmten Reihenfolge geladen werden.

Als Workarround kann man das VSTO Native Image (nicht VSTO selbst ;-)) löschen. Dazu tippt man in einem Visual Studio Command Promt einfach folgende Zeile ein:

ngen /delete Microsoft.VisualStudio.Tools.Office.Runtime.v9.0.dll

Viel Erfolg ;-)


Kick it on dotnet-kicks.de
 
1/29/2009 - 10:04 PM | Comments [0] | Categories: DotNetGerman Bloggers | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

(English version below)
30 begeisterte Fans des manged Codes fanden gestern Abend Ihren Weg nach Koblenz, um bei unserer .NET User Group Tony Hasselhoff Lombardo, ASP.NET MVP und Lead-Evangelist bei Infragistics, zu hören.

Tony teilnehmer

Tony erzählte in einem sehr spannenden Vortrag, was mit Microsoft Silverlight 2.0 fernab von sinnfreien animierten Buttons und TextBoxen machen können. Ganz Konkret ging es um Geschäftsapplikationen, mit denen unsere Kunden sogar arbeiten können ;-)

Unter anderem Sprach Tony über:

  • Authentifizierung und Authorisierung
  • Das Speichern von Benutzereinstellungen
  • Datenzugriff und -bindung
  • sowie das MVVM Pattern.

presentation

Entgegen meiner ersten Befürchtung war die Gruppe trotz des ersten fremdsprachlichen Vortrages recht aktiv und stellte viele Fragen. Außerdem freute ich mich sehr darüber, so viele neue Gesichter zu sehen. Hoffentlich findet der ein oder andere zu zukünftigen Treffen seinen Weg zu uns.

Als User Group freuen wir uns natürlich sehr darüber, dass wir Tony als einzige Gruppe in Europa exklusiv zu Gast haben durften. Vielen Dank an dieser Stelle noch mal an Ihn und das ganze restliche Team von Infragistics, die diesen Vortrag möglich machten!

Als kleines Bonbon sponsorte Infragistics zum Abschluss des Vortrags sogar noch eine MSDN Premium Subscription sowie eine vollständige NetAdvantage Suite, die wir innerhalb der Gruppe verlosen durften.

ziehung

Als Fazit nehme ich mit:

  • Silverlight eröffnet eine Reihe neuer Möglichkeiten im Web
  • 5 Erwachsene passen in einen Peugeot 106, bequem ist es aber nur für den Fahrer
  • Man sollte auch grauen Text lesen
  • Jemand der kein deutsches Bier gewohnt ist, verwechselt leicht den "Kleiner als" Operator "<" mit einem öffnenden Generic

English version:

Yesterday evening, 30 excited fans of managed code found their way to Koblenz in order to listen  to Tony Hasselhoff Lombardo, ASP.NET MVP and Infragistics lead evangangelist, at our local .NET user group.

Tony teilnehmer

Tony did a great talk on the possibilities of Microsoft Silverlight 2.0 beyond rotating buttons or text boxes. Actually the whole thing was about Line of Business Applications, which our customers could eventually use to get their job done ;-)

During his talk Tony covered:

  • Authentication and authorization
  • Preserving user settings
  • data access and binding
  • as well as the MVVM pattern

presentation

Despite my initial worries, the group was very active and asked a lot of question. So nobody seemed to be shy at our first talk in a foreign language. In addition, I've been very happy to see so many new faces during the meeting. Hopefully some of them will find their way to future meetings, too.

As a user group we were of course very happy to be the only group to present Tony in Europe. Many thanks again to him and the whole Infragisitcs team for making this talk possible.

As a small bonbon, Infragistics sponsored our meeting by raffling a MSDN Premium Subscription as well as a full NetAdvantage Suite.

ziehung

My resume of the meeting is:

  • Silverlight gives you a whole set of new possibilities for web applications
  • 5 persons do fit into a Peugeot 106, but it's only comfortable for the driver
  • You should read the grey text, too
  • Somebody who's not used to German beer may easily mix up a "lower then"-operator (<) with an opening generic

Kick it on dotnet-kicks.de
 
11/7/2008 - 09:04 PM | Comments [0] | Categories: .NET | DNUG Koblenz | DotNetGerman Bloggers | english posts | Infragistics | Silverlight
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Möchte man als Entwickler einen Einstieg in die Entwicklung mit den Microsoft Windows Sharepoint Services (WSS) 3.0 oder den Microsoft Office Sharepoint Server (MOSS) 2007 finden, steht man zu Beginn vor einer eher ungewöhnlichen Frage:

"Welche Systemumgebung brauche ich, um überhaupt los legen zu können?"

Reicht für andere Einsatzzwecke normalerweise ein Windows Client Betriebssystem inklusive Visual Studio nebst dem ein oder anderen Tool aus, muss es für die Sharepoint Entwicklung ein vollständiges Server Betriebssystem sein.

Da die wenigsten Entwickler wohl einen Server als "Hauptbetriebssystem" nutzen, liegt der Griff zur einer Virtuellen Maschine nahe. Leider ist auch diese nicht ganz so einfach installiert. Zumindest nicht, wenn man nicht regelmäßig Windows Server installiert und konfiguriert.

Genau aus diesem Grund hat Tony Zink eine 20-Teilige Blog Serie veröffentlicht, die durch den Dschungel der Windows Server 2003 + SQL Server 2005 + Moss 2007 Installation und Konfiguration führt.

Ein deutsches Pendant in der Kombination Windows Server 2008, SQL Server 2008 und MOSS 2007 findet sich übrigens in Fabians Blog.

Was in beiden Beiträgen fehlt ist die Installation von Visual Studio. Dies sollte für uns als Entwickler jedoch kein Problem sein :-)


Kick it on dotnet-kicks.de
 
11/5/2008 - 04:45 PM | Comments [0] | Categories: DotNetGerman Bloggers | Sharepoint
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Er ist ASP.NET MVP, er ist Co-Leader der Central-Jersey .NET User Group, für Microsoft hält Web-Casts direkt aus Redmond. Außerdem ist er der Leiter von Infragistics weltweiter Evangelism Gruppe. Er kommt direkt aus den USA und im November ist er bei uns!

Die .NET User Group Koblenz ist stolz, Anfang November mit Tony Lombardo, Lead Evangelist von Infragistics aus den USA, den ersten internationalen Sprecher zu Gast zu haben.
Tony wird über Line of Business Applications mit Silverlight 2.0 sprechen. Während des Vortrags wird er unter anderem auf alltägliche Aufgabenstellungen wie Datenbindung, Authentifizierung oder der Speicherung von Benutzereinstellungen zu sprechen kommen.

Über den Sprecher:

Tony Lombardo ist der leitende Evangelist von Infragistics Welt-Weiter Evangelisten Gruppe. Sein Spezialgebiet ist ASP.NET. Sein tiefer technischer ASP.NET Hintergrund sowie sein Community Engagement verschafften ihm die Auszeichnung zum Microsoft MVP für ASP.NET.
Tony ist Co-Leader der Central-Jersey .NET User Group. Zusätzlich schreibt er regelmäßig für online und print Medien wie zum Beispiel ASP.NET Pro, Dr. Dobb’s Journal, .NET Developers Journal und die Redmond Developer News.
Tony arbeitet seit fast einem Jahrzehnt für Infragistics. Diese Zeit startete er zunächst als Entwickler für ASP.NET, wurde anschließend Produkt Manager für die Infragistics ASP.NET Komponenten ehe er seine aktuelle Rolle als Lead-Evangelist einnahm. Bedingt durch seinen Werdegang verfügt er über "real-world" ASP.NET Erfahrung, die er stets mit freue teilt.
Der Vortrag wird ausnahmsweise in englischer Sprache abgehalten werden.

Anmeldung unter:
https://www.xing.com/app/events?op=detail;id=267741

English Version:

LOB 2.0 with Silverlight 2

This talk will cover building a next generation web application using Silverlight 2. In this talk we will discuss how to do some common tasks including databinding, authentication, and persistence of user defined settings.

Speaker: Tony Lombardo

In November we have Tony Lombardo visiting Europe from the USA. Tony is the lead Technical Evangelist in Infragistics' Worldwide Evangelism Group at Infragistics.
Tony has a deep technical background in ASP.NET and his community participation has earned him the ASP.NET MVP Award from Microsoft. Tony co-runs the Central Jersey .NET Users Group, and often writes for both online and printed publications, including ASP.NET Pro, Dr. Dobb’s Journal, .NET Developers Journal and Redmond Developer News.
In addition, Tony has been working for Infragistics for almost a decade, and in that time he spent his days working as a Developer on our ASP.NET product before becoming a Product Manager and eventually moving the role he has today. As such, Tony has first-hand experience in “real-world” issues and he will be very pleased to advise in a technical capacity on use of Infragistics controls.

Register at:
https://www.xing.com/app/events?op=detail;id=267741


Kick it on dotnet-kicks.de
 
10/27/2008 - 09:17 PM | Comments [0] | Categories: .NET | ASP.NET | DNUG Koblenz | DotNetGerman Bloggers | Infragistics | Silverlight
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Abgesehen von "Können Sie mir hier bitte einen Stempel drauf machen?" oder "Darf ich so einen Kuli haben?" war  die dritthäufigste Frage an unserem Infragistics-Stand während der diesjährigen Herbst-Basta sicherlich "Wann kommen die Infragistics Silverlight 2.0 Controls raus?"

Nun, während der Messe lautete unsere Antwort stets: "So lange Microsoft selbst nicht weiss, wann Silverlight 2.0 heraus kommt, können wir hier leider keine Antwort geben" ;-)

Tja, wie heute in diversen Blogs zu lesen war ([1], [2], [3]) hat Microsoft die Katze aus dem Sack gelassen und das Silverlight 2.0 RTW für morgen angekündigt.

Infragistics war allerdings ein wenig schneller - zumindest was die Ankündigungen angeht -  und gabin Tony's Blog letzten Mittwoch die Infragistics NetAdvantage Silverlight Controls for Line of Business (LOB) Applications Roadmap bekannt.

Zusammengefasst sieht diese wie folgt aus:

Meilenstein Datum
Roadmap angekündigt Oktober 2008
Erstes CTP Ende 2008
Zweites CTP März 2009
2009 Vol 1 Release (inkl. Silverlight LOB Controls) Quartal 2 2009

Die ersten Screenshots sehen schon recht vielversprechend aus

xamWebChart xamOutlookbar

bleibt nun also abzuwarten, was tatsächlich daraus wird.

PIC-0031

Eine Aussage zum NetAdvantage Silverlight Release Datum konnten wir während der Basta leider nicht treffen. Spaß auf dem "Bastoberfest" hatten wir aber trotzdem ;-)


Kick it on dotnet-kicks.de
 
10/13/2008 - 11:01 PM | Comments [1] | Categories: .NET | DotNetGerman Bloggers | Infragistics | Silverlight
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Die .NET User Group Koblenz freut sich, am 23. Oktober, mit Torsten Weber einen weiteren Top Speaker zu Gast zu haben. Torsten ist Microsoft MVP, Buchautor, Basta-Speaker und Leiter der .NET User Group Leipzig.

Inhalt des Vortrags:

Mobile Computing ist schwer, denn dazu benötigt man einerseits komplexes Fachwissen und andererseits mindestens ein mobiles Gerät. Falsch; Mobile Computing ist nicht so schwer, wie man vielleicht denkt. Viel schwerer ist es, die Angst davor zu verlieren. Genau das bietet Torsten Weber in seinem „Wo und wie beginne ich"-Vortrag. Da die Begriffe AJAX und Web Irgendetwas.Null derzeit auf Entwickler viel magische Anziehungskraft besitzen und manche Entwickler lieber bei den "guten alten" Webanwendungen bleiben als bei Windows Forms, gibt es für alle Schmackes oben drauf: Die Unterstützung vom Internet Explorer Mobile als auch von Windows Forms für AJAX & Co. Abgerundet wird alles mit Best Practices bei mobilen Anwendungen.

Über den Sprecher:

Torsten Weber hat sich seit seinem ersten mobilen Gerät vor über einem Jahrzehnt in die Mobilität verliebt und schwört mit Beginn der ersten Version des .NET Compact Frameworks auf genau diese Plattform. Als Wirtschaftsinformatiker, gelernter Bankkaufmann, Technologieberater und Querdenker hat er langjährige Berufserfahrung in der Softwareentwicklung, insbesondere wenn es um betriebswirtschaftliche Gesamtzusammenhänge geht. Ob bei seiner Tätigkeit bei Microsoft als Project Manager, ob als Buchautor oder in seinem Engagement in der Leipziger .NET User Group mit Alexander Groß bis hin zu seinem MVP für Device Application Development – es geht ihm immer ein wenig um "so wird das Ganze dann mobil!".

Anmeldungen werden unter: https://www.xing.com/app/events?op=detail;id=259135; angenommen. Näheres zur .NET Developer Group Koblenz kann unter http://www.dnug-koblenz.de nachgelesen werden.


Kick it on dotnet-kicks.de
 
10/7/2008 - 10:22 PM | Comments [0] | Categories: .NET | DNUG Koblenz | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Kürzlich installierte ich Visual Studio 2008 Service Pack 1. Nach der Installation konnte ich keine neuen Solutions basierend auf der SCSF - April 2008 mehr anlegen. Der Sourcecode wurde zwar generiert, beim Versuch das Projekt zu starten kam es jedoch zu einer ModuleLoadException. Außerdem wurde auch das "Add View (With Presenter)" Recipe nicht mehr angezeigt.

image

Glücklicherweise war das Problem bereits bekannt. Abhilfe in Form einer Anleitung zum Patchen des Sourcecodes gibt es hier.


Kick it on dotnet-kicks.de
 
9/7/2008 - 09:37 PM | Comments [0] | Categories: .NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

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 ;-)


Kick it on dotnet-kicks.de
 
7/27/2008 - 07:48 PM | Comments [2] | Categories: .NET | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Nachdem ich die letzten Jahre meine Software fast ausschließlich unter C# entwickelte, musste ich kürzlich für einen Kunden an einem VB.NET Projekt arbeiten.

Die Umgewöhnung in die Sprache klappte überraschend schnell und reibungslos. Allerdings übersah ich, dass VB.NET von Haus aus keine Escape Sequenzen, wie z. B. \n für neue Zeile oder \t für Tab unterstützt. Entsprechend sahen auf der Oberfläche auch meine ausgegebenen Strings aus.

Auf der Suche nach einer Lösung bei der ich nicht sämtliche zuvor definierten Strings anpacken musste, stolperte ich fast ausnahmslos auf Ansätze, die Über String.Replace() die Escape Sequenzen gegen VB.NET Control Characters ersetzten.

Eleganter fand ich allerdings folgende Lösung, die auf eine .NET Framework Standardklasse setzt:

myString = System.Text.RegularExpressions.Regex.Unescape(myString)

... Ist doch irgendwie eleganter, oder? :-)


Kick it on dotnet-kicks.de
 
7/9/2008 - 08:32 AM | Comments [0] | Categories: .NET | DotNetGerman Bloggers | Tips und Tricks | VB.NET
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Bei meinen Streifzügen durchs Web bin ich über folgendes Video gestolpert. Es zeigt Rick Barraza, User Experience Architect bei Cynergy Systems, der aus einer Wii-Remote, zwei Handschuhen, einer Funkmaus und WPF innerhalb von acht Tagen ein Minority Report ähnliches User Interface gebaut hat.

minority

Hat ein wenig etwas von MacGyver, oder :-)

Technorati tags:

Kick it on dotnet-kicks.de
 
4/7/2008 - 04:41 PM | Comments [0] | Categories: .NET | DotNetGerman Bloggers | WPF
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Wirft man einen Blick auf die Vielzahl der im Internet vorhandenen Silverlight 1.0 Demo Applikationen, stellt man in den meisten Fällen eine Gemeinsamkeit fest:

  • Die Demo zeigt verblüffende grafische Effekte / Animationen im Web
  • Abgesehen von diesen Effekten hat die Demo keinerlei Nährwert :-)

Infragistics möchte die Lücke der wertvollen Silverlight Demos nun füllen.

Zu diesem Zweck wird unter http://www.faceoutlive.com/ ein Silverlight Dashboard für Vertriebsmitarbeiter gezeigt.

faceout

Registrierte Benutzer erhalten unter  http://www.infragistics.com/hot/silverlight.aspx#InfragisticsandSilverlight zusätzlich den kompletten Source Code sowie ein 12seitiges Dokument, in dem Infragistics einen kurzen Überblick über die Applikationsarchitektur sowie die "lessons-learned" gibt. Gerade der "lessons-learned" Bereich ist sehr interessant.

In diesem Sinne: Viel Spaß mit Silverlight ;-)

Technorati tags: ,

Kick it on dotnet-kicks.de
 
4/7/2008 - 04:20 PM | Comments [0] | Categories: .NET | DotNetGerman Bloggers | Infragistics | Silverlight
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Vorbereitend auf den Visual Studio 2008 Launch in Frankfurt hatte ich die Möglichkeit, einen Artikel für Zoschkes Devdorado.de (01/2008) über die Infragistics Excel Engine zu schreiben.

André Wiese, Geschäftsführer der Zoschke Data GmbH, gab mir freundlicherweise die Genehmigung, den Artikel im PDF-Format auf meiner Homepage zum Download anzubieten.

Anbei ein kleiner Vorgeschmack:

"Optisch ansprechende Excel-Dateien zu erzeugen, erfordert normalerweise fehleranfälligen Automatisierungscode. Devdorado.de zeigt Ihnen, wie Sie mit Hilfe der Infragistics Excel Engine fehlerfrei zu eindrucksvollen Ergebnissen kommen."

Den kompletten Artikel gibt's hier. (Link wurde am 7. April 2008 gefixt.)

Technorati tags:

Kick it on dotnet-kicks.de
 
4/4/2008 - 09:50 AM | Comments [1] | Categories: .NET | DotNetGerman Bloggers | Infragistics | Veröffentlichungen
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Als technologiebegeisteter Mensch lud ich mir kürzlich, wie wahrscheinlich außer mir noch 100te andere, Infragistics NetAdvantage for WPF 2007 Vol 2 runter, um Infragistics neueste Technologie zu evaluieren.

Leider kam bereits beim öffnen der Beispielsapplikation "xamFeatureBrowser" die erste Enttäuschung. Die Applikation zeigte nämlich nicht Infragistics neueste Features, sondern präsentierte statt dessen den relativ langweiligen Windows-Fehlerreportingdialog:

3ndmessage

Eine kurze Rückfrage bei meinem Kontakt zu Infragistics ergab, dass das Problem anscheinend nur unter "nicht englischen Betriebssystemen" auftritt.

Um der Sache nun auf den Grund zu gehen, öffnete ich die xamFeatureBrowser Solution unter Visual Studio 2005 und warf den Debugger an. Sofort nach dem Start wurde mir folgende Meldung mit anschließender Exception um die Ohren geworfen:

1stmessage

System.IO.IOException was unhandled

Message="Cannot locate resource  \"app.xaml\"."

2ndmessage

Anschließend stürzte die Applikation wieder ab. Eine kurze Recherche im Internet ergab, dass das Problem mit .NET 3.0 Solutions zusammen hängt, die unter Visual Studio 2008 Beta 1 gespeichert und später unter Visual Studio 2005 wieder geöffnet wurden. Einen Einfluss auf die von Infragistics ausgelieferte kompilierte Version der Anwendung sollte diese Situation jedoch nicht haben.

Um dem Fehler also auf die Spur zu kommen, warf ich als nächstes die Windows Debugging Tools an. Hier bekam ich die recht interessante Meldung, dass er für die angegebene Kultur keine Ressourcen finden kann. Dieses Verhalten würde auch zum Phänomen passen, dass der xamFeatureBrowser nur unter englischen Betriebssystemen läuft, da für die englische Kultur Ressourcen vorhanden sind.

0:000> .loadby sos mscorwks

0:000> !pe

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll -

PDB symbol for mscorwks.dll not loaded

Exception object: 01429598

Exception type: System.Resources.MissingManifestResourceException

Message: Für die angegebene Kultur oder die neutrale Kultur konnten keine Ressourcen gefunden werden.

Stellen Sie sicher, dass xamFeatureBrowser.g.resources beim Kompilieren richtig in die Assembly

xamFeatureBrowser eingebettet wurde, oder dass die erforderlichen Satellitenassemblys geladen werden

können und vollständig signiert sind.

Die Lösung

Auf meinem System konnte ich das Problem wie folgt lösen:

  1. Öffnen der Solution. Diese lag bei mir unter:
    "C:\Users\Public\Documents\Infragistics\NetAdvantage for WPF 2007 Vol. 2\Samples\xamFeatureBrowser"
  2. Umschalten der Solution Konfiguration von "Debug - Full Trust" auf "Release". Dieser Schritt ist eventuell nur unter VS 2005 notwendig. Zumindest kommt dann keine Meldung mehr, dass die App.xaml fehlt.
    projectconfiguration
  3. Öffnen der App.xaml.cs und hinzufügen des folgenden Konstruktors:

            public App()

            {

                CultureInfo objCI = new CultureInfo("en-US");

                Thread.CurrentThread.CurrentCulture = objCI;

                Thread.CurrentThread.CurrentUICulture = objCI;

            }


    Diesen Tipp bekam ich übrigens von heroic im Infragics Forum
  4. Ändern der Eigenschaften für die Datei "TableOfContents.xml". Die Build Action muss auf Content und die Copy to Output Directory auf Copy always gesetzt werden.
    toc
  5. Starten des Projekts :-)
    xambrowser 

Bleibt nun zu hoffen, dass Infragistics den Bug in der nächsten Version behebt ...


Kick it on dotnet-kicks.de
 
4/2/2008 - 02:52 PM | Comments [0] | Categories: .NET | DotNetGerman Bloggers | Infragistics | WPF
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

Eigentlich sollte der Titel meines Vortrags während des gestrigen Treffens der .NET Developer Group Koblenz "Neues in Visual Studio 2008, .NET Framework 3.5 und C# 3.0" lauten. Da ich jedoch am Vormittag des Treffens bei einem Kunden in Bayern war und kurz vor Würzburg 20 km Stau auf uns warteten, traf ich statt wie geplant um 19:00 Uhr erst um 20:45, also 15 Minuten vor offiziellem Ende der Veranstaltung in Koblenz ein. Um der Situation nun kurzerhand gerecht zu werden benannte ich den Vortrag spontan in "Jetzt lerne ich C# 3.0 in 12 1/2 Minuten" um und gab mein bestes, die knappe Zeitvorgabe zu halten.

Tja, wer mich kennt, der weiß, dass es vollkommen gegen meine Natur ist, mich kurz zu fassen :-) Aus 12 1/2 wurden also gut 45 Minuten. Da keiner der Teilnehmer vorzeitig ging oder einschlief, werte ich den Vortrag allerdings trotzdem als erfolgreich :-)

Die Folien und den Beispielcode meines Vortrags können entweder hier, oder im Downloadbereich der .NET Developer Group Koblenz herunter geladen werden.

An dieser Stelle noch mal vielen Dank an Markus Rennings fürs Vorziehen seines Vortrags zum Entity Framework sowie an alle geduldigen Teilnehmer!!!

Download: Vortrag
Download: Beispielcode

Edit (15. April 2008): Habe die Links mal gesetzt.

Technorati tags:

Kick it on dotnet-kicks.de
 
3/27/2008 - 08:04 PM | Comments [3] | Categories: DNUG Koblenz | DotNetGerman Bloggers
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

In meinem beruflichen Alltag erstelle ich häufig virtuelle Maschinen, auf denen ich z. B. meine Software teste, oder auch meine komplette Entwicklungsumgebung virtualisiere. Gerade die virtualisierten Entwicklungsumgebungen haben sich alles gutes Mittel herausgestellt, um neuen Kollegen schnell eine vollständig vorkonfiguriertes Entwicklungssystem an die geben Hand zu können.

Benötigte ein Kollege nun eine solche virtuelle Maschine, kopierte ich bisher einfach die virtuelle Festplatte auf seinen Rechner und lies anschließend das Tool NewSID über die Installation laufen, um sowohl den Rechnernamen, als auch dessen Security ID zu ändern.

Leider funktionierte dies unter Vista bei mir nicht mehr. NewSID lies sich zwar starten, schien aber in einer Endlosschleife zu laufen.

Lösen konnte ich das Problem durch den Einsatz von Sysprep statt NewSID. Im Gegensatz zu früheren Windows-Betriebssystemen muss dies unter Vista nicht mehr nachinstalliert werden. Statt dessen findet man es direkt unter:

%WINDIR%\System32\Sysprep

Im folgenden Dialog ist es wichtig, die Option "Verallgemeinern" anzuwählen. Andernfalls wird keine neue SID erzeugt :-)

sysprep

Technorati tags: , , ,

Kick it on dotnet-kicks.de
 
3/19/2008 - 02:33 PM | Comments [0] | Categories: DotNetGerman Bloggers | Tips und Tricks | VPC
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)

In den letzten Tagen installierte ich das .NET 3.0 und 3.5 SDK unter Vista. Nach der Installation entschied sich Visual Studio 2005 leider, keine Vorlagen mehr anzuzeigen. Der Klick auf "Add New Item" öffnete einen Dialog, in dem nur noch das WCF Template vorhanden war.

Bob hatte zum Glück die Lösung.

  1. VS 2005 schließen
  2. VS 2005 Command Prompt öffnen
  3. devenv /InstallVSTemplates ausführen
  4. VS 2005 erneut öffnen.

edit:

Kürzlich hatte ich das Problem wieder, doch leider zeigte devenv /InstallVSTemplates dieses Mal zunächst keinen Erfolg. Nachdem ich allerdings in den Sicherheitseinstellungen für die Ordner "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ItemTemplatesCache" und "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ItemTemplates" der Gruppe "ERSTELLER-BESITZER" Vollzugriff gab, lief es beim zweiten Anlauf wie geschmiert. Ich bin mir unsicher, ob dies reiner Zufall war, oder ob es wirklich einen Zusammenhang gibt. Falls jemand ähnliche Erfahrungen gemacht hat, kann hier er ja einfach mal einen Kommentar posten :-)

edit 2:

Ein weiterer Anhaltspunkt könnte sein, dass VS 2005 die Spracheinstellung auf "Wie Microsoft Windows" stehen hatte (in meinem Fall war das Deutsch). Mein Studio ist nun aber eigentlich ein Englisches. Da ich die Umstellung auf Englisch zeitgleich mit den Berechtigungen gemacht habe, kann ich leider nicht nachvollziehen, welches von beidem zum Erfolg führte.


Kick it on dotnet-kicks.de
 
3/18/2008 - 10:24 PM | Comments [0] | Categories: .NET | DotNetGerman Bloggers | Tips und Tricks
© Andre Kraemer | RSS/Subscribe Feed your aggregator (RSS 2.0)