Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute...

173

Transcript of Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute...

Page 1: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

.Net Framework und C#

SS 2010

Dr. Ute Blechschmidt-Trapp

Darmstadt, 20. März 2010

Page 2: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Inhaltsverzeichnis

1 Einleitung 41.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.2 Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.3 Aufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.4 Schnellstart Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Windows Forms 7

3 C# für Entwicklerinnen 93.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3.1.1 Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.1.2 Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.1.3 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.2 Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.3 Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.3.1 Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.3.2 Generika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.3.3 Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.3.4 Enumerationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.4 Sprachbesonderheiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.4.1 Log/Trace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.4.2 Ausnahmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.4.3 Indexer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.4.4 Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.5 Delegaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.5.1 Mulitcastdelegaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.5.2 Schnittstelle vs. Delegate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.5.3 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3.6 Attribute und Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.6.1 Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.6.2 Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.6.3 Zugri� auf Attribute mit Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

3.7 Lambda-Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.8 IDisposable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.9 using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323.10 Serialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333.11 XML-Dokumentationskommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343.12 Namenskonventionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343.13 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

4 Komponenten 374.1 Eigene GUI Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394.2 Managed Extensibility Framework (MEF) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394.3 Managed Add-in Framework (MAF) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

4.3.1 Laden eines Add-Ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414.3.2 Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

5 Windows Presentation Foundation 455.1 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465.2 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485.3 WinForm und WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

6 Datenbankanbindung 496.0.1 .NET Framework-Datenanbieter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

2

Page 3: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Inhaltsverzeichnis

6.0.2 DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516.0.3 DataSet oder DataReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

6.1 Entity Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526.1.1 Mapping von Objekten zu Daten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536.1.2 Zugreifen auf und Ändern von Entitätsdaten . . . . . . . . . . . . . . . . . . . . . . . . . 536.1.3 Datenmodellierung im Entity Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

6.2 LINQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546.3 Datenbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

6.3.1 Datenbindung in WinForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566.3.2 Datenbindung in WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576.3.3 Datenbindung in Asp.Net . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

6.4 Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

7 Webentwicklung 597.1 Asp.Net . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

7.1.1 Masterseiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607.1.2 Lebenszyklus einer Seite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607.1.3 Ereignismodell für ASP.NET-Webserversteuerelemente . . . . . . . . . . . . . . . . . . . . 617.1.4 Zustandsverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627.1.5 Kon�guration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637.1.6 Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

7.2 Asp.Net MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

8 Windows Communication Foundation 678.1 Problembeispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678.2 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

8.2.1 Dienstlaufzeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698.2.2 Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698.2.3 Hosting und Aktivierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

9 Windows Work�ow Foundation 719.1 Statuscomputerwork�ows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719.2 Sequenzielle Work�ows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

10 .Net Framework 7310.1 Vokabeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7410.2 Assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7810.3 Anwendungsdomäne (AppDomain) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8010.4 Basisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8110.5 Typsystem von C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

3 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 4: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

1 Einleitung

1.1 Motivation

Warum biete ich dieses Wahlfach an? Ich habe 10 Jahre bei einem zerti�zierten Partner von Microsoft gearbeitetund dabei die Microsoft Entwicklungsumgebungen und Möglichkeiten kennen und auch zu schätzen gelernt. Ne-ben Java ist C# die Programmiersprache, die in Stellenanzeigen und Artikeln am Häu�gsten genannt wird. AufClient-Maschinen ist Windows immer noch das meist verbreitete Betriebssystem (93stellt Microsoft immerhin24% der Server - Apache 54%. Sicher, Java und plattformunabhängige Technologien sind primär zu erlernen.Der Markt wünscht aber auch Entwicklerinnen, die sich im Microsoftumfeld auskennen. Dies zeigt eine Statistikdes Freelancerportals Gulp1 oder auch ein Artikel bei heise2.

Die verschiedenen aktuellen Technologien von Microsoft sind mächtig und spannend und es macht Spaÿ, miteiner komfortablen IDE zu arbeiten. Wer sich mit seiner geplanten Anwendung im Microsoftumfeld bewegt,sollte die Möglichkeiten von .Net nicht verzichten. Selbst, wer plattformübergreifend denkt und arbeitet, kannvon .Net pro�tieren, denn es gibt zunehmend mehr Projekte, in denen Java und C# zusammen eingesetztwerden.

Leider oder zum Glück gibt es im .Net Umfeld ca. alle 2 Jahre eine neue Version. Glück für die Entwicklerinnen,die stumpfsinniges Arbeiten durch mächtige Werkzeuge und Bibliotheken weiter abgenommen bekommen undleider, da sich die Prinzipien dadurch auch grundlegend ändern können. Was ist also das bleibende Wissen,das in einer Hochschulveranstaltung vermittelt werden soll? Für Entwicklerinnen, die bereits eine OO-Sprachebeherrschen, kann dies nicht das Erlernen der Sprache C# sein. Das Erlernen einer weiteren OO-Sprache istkeine Herausforderung. C# und Java sind sehr ähnlich. In dieser Veranstaltung werden wir uns also auf dieUnterschiede konzentrieren. Der Fokus dieser Vorlesung liegt auf dem Arbeiten mit .Net, wo bekomme ich Hilfe,was sind momentan die empfohlenen Vorgehensweisen und wesentlichen Technologien?

Dies ist also kein Anfängerprogrammierkurs für C#, sondern ich versuche, Hochsprachkonzepte am Beispiel vonC# darzustellen, z.B. was ist Delegation? Sie erarbeiten durch geeignete Aufgaben die konzeptuellen Unter-schiede zu Java.

Jedes Kapitel enthält eine Fülle von Links, die weiterführende Informationen enthalten. Das Skript ist eineSammlung vieler Quellen, vorwiegend aus Microsofts Feder � die Er�nder können am Besten ausdrücken, wassie womit meinen und wollen. Marketing-Sprüche habe ich weitestgehend entfernt. .

Zeitreise: 1980 startete Microsoft mit 40 Mitarbeitern. In den 80ern und 90ern verfolgte das Unternehmen eineabschottende Politik, d.h. Betriebssystem und Anwendungen waren gegenüber Erweiterungen und Austauschvon Anwendungsdaten auf Microsoft-Produkte beschränkt. In dieser Zeit entwickelte sich insbesondere in denHochschulen und einer aktiven Entwicklergemeinde ein relativ schlechtes Image von Microsoft. Das Unterneh-men hat seine Strategie geändert, Quellcodes werden verö�entlicht, Microsoft arbeitet in vielen Gremien anStandards mit, das .Net Framework kann von beliebigen Sprachen genutzt werden, sofern ein entsprechenderVorcompiler existiert und auch der Datenaustausch zwischen O�ceanwendungen und Drittanbietern ist DankXML möglich.

1.2 Literatur

Es gibt viele dicke Bücher zu C#, Asp.Net und anderen Themen dieser Vorlesung. Es gibt meiner Meinungnach wenige gute Bücher, die für Quereinsteiger von anderen OO-Sprachen geschrieben werden, so dass Sie vielGeld für Gedrucktes, dass Sie aus anderen Veranstaltungen kennen, ausgeben. Dies, die Schnelllebigkeit vonMicrosoft-Technologien und die guten Online-Materialien führen zu folgender Linkliste:

• Microsoft

� MSDN � Das Microsoft Developer Network http://msdn.microsoft.com

1Marktstudie: Begehrte Programmiersprachen2Experten langfristig gefragt

4

Page 5: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

1.3 Aufbau

� Channel9 � Videos und anderes Lernmatieral http://channel9.msdn.com/learn/

� patterns & practices: http://msdn.microsoft.com/en-us/practices/default.aspx

� Microsoft Application Architecture Guide, 2nd Edition http://msdn.microsoft.com/en-us/

library/dd673617.aspx

� Magazin http://msdn.microsoft.com/en-us/magazine/default.aspx

� Visual Studio 2010 and .NET Framework 4 Training Kit http://www.microsoft.com/downloads/details.aspx?familyid=752CB725-969B-4732-A383-ED5740F02E93&displaylang=en

• Developer Blogs

� http://blogs.msdn.com/

� http://weblogs.asp.net/

• Andere

� Das war das .NET-Jahr 2009 - ein Rückblick http://www.heise.de/developer/artikel/

Das-war-das-NET-Jahr-2009-ein-Rueckblick-908768.html

� Visual Studio Magazine http://visualstudiomagazine.com

� BlogBook http://www.dotnet-blogbook.com/Download.aspx

� Vorlesung in die Tiefen von .Net http://svn.ipd.uka.de/lehre/vorlesung/NET/download/ (mitanonymous anmelden)

• Code

� Codeproject http://www.codeproject.com/

� Codeplex http://cfx.codeplex.com

� Design Patterns http://www.dofactory.com

1.3 Aufbau

Ein Kurzeinblick in die Arbeitsweise des Frameworks im folgenden Abschnitt gibt Ihnen den nötigen Hintergrundund die Basis zum Verständnis der folgenden Kapitel. Mit WinForm wurden früher Client-GUI-Applikationenentwickelt. Diese Technologie wird zunehmend von Windows Presentation Foundation abgelöst. Dennoch star-ten wir in Kapitel 2 mit WinForm, so dass die weiteren Applikationen im Praktikum nicht nur auf der Consolelaufen müssen. In Kapitel 3 lernen Sie die grundlegende Syntax und die Besonderheiten von C# kennen. DasFramework zeichnet sich durch das Zusammenspiel von verschiedenen Komponenten aus. Wie Sie selbst kom-ponentenbasierte und durch Komponenten zur Laufzeit erweiterbare Applikationen entwickeln können, erfahrenSie in Kapitel 4. Die Basis ansprechender Benutzerober�ächen in .Net bildet Windows Presentation Founda-tion. In diese Technologie führt Kapitel 5 ein. Kapitel 6 beschäftigt sich mit allen Fragen der Anbindung anDatenbanken. Die Grundlagen der Webentwicklung stellt Kapitel 7 vor. In Kapitel 8 erhalten Sie eine kurzeEinführung in Windows Communication Foundation und in Kapitel 9 eine kurze Einführung in Windows Work-�ow Foundation. Abschlieÿend schauen wir uns das .Net Framework in Kapitel 10 noch einmal genauer an �wobei ich aufgrund des Umfangs der Vorlesung Sicherheitsfragen leider nicht genauer betrachten kann.

Dies ist eine Alpha-Version des Skripts. Informieren Sie mich bitte über Fehler, Unklarheiten, Lücken, machenSie Vorschläge für eine bessere Struktur, verständlichere Formulierungen, Übungsaufgaben, Schaubilder. Ichfreue mich über jede konstruktive Kritik.

Obwohl ich viele Jahre im Microsoftumfeld entwickelt habe, sind mir einige der hier vorgestellten Technologienauch nur aus der Theorie oder in älterer Version bekannt. In der Produktentwicklung hat man immer auch mitAltlasten zu kämpfen, Kundenserver werden nicht leichtfertig auf neue Versionen umgestellt, so dass aktuelleTechnologien nur bedingt zeitnah in den Entwicklungsabteilungen ankommen. Wenn Sie Erfahrungen mit .Nethaben, bringen Sie diese bitte ein!

5 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 6: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

1.4 Schnellstart Framework

Abbildung 1.1: Ausführungsmodell

1.4 Schnellstart Framework

In Abschnitt 10 auf Seite 73 schauen wir uns das .Net Framework genauer an. Für das Verständnis einigerEigenschaften von C# und die ersten Aufgaben im Praktikum, ist jedoch ein grobes Verständnis hilfreich. .NetFramework hat ähnlich wie Java eine Laufzeitumgebung, die vorcompilierten Code ausführt.

Mit einem Vorcompiler wird Programmcode in die Common Intermediate Language (CIL) (teilweise auch nurIntermediate Language (IL) genannt) übersetzt. CIL ist eine objektorientierte Assemblersprache. Das physikali-sche Ergebnis dieses Compilevorgangs sind die sogenannten Assemblies (siehe Abschnitt 10.2 auf Seite 78). DerJust in time compiler (JIT) erzeugt daraus sogenannten Managed Code, der in der Common Language Runtime(CLR) für das jeweilige Betriebssystem umgesetzt wird. Managed Code ist per De�nition Code, der von derCLR interpretiert wird. Jeder andere Code ist unmanaged.

Abbildung 1.2: Common Type System

In .Net sind alle Sprachen gleichwertig. Ein einheitliches Typsystem ermöglicht das direkte Zusammenspiel ein-zelner Assemblies, die in unterschiedlichen Sprachen implementiert sein können. Dabei ist zu beachten, dassalles ein Objekt ist. Werttypen landen auf dem Stack und alle anderen Typen auf dem Heap, der vom GarbageCollector der CLR gegebenenfalls aufgeräumt wird. Entwickler müssen sich im Allgemeinen nicht um die Spei-cherverwaltung kümmern, sollten aber im Hinterkopf behalten, dass alles auf dem Heap mit Adressen zu tunhat.

6 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 7: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

2 Windows Forms

Abbildung 2.1: Windowsentwicklung

Microsoft startete mit einem erfolgreichen Betriebssystem und Software (O�ce) für Client-Rechner. Daher habensich Entwicklerinnen, die für Microsoft-Betriebssysteme entwickelt haben, auf Client-Entwicklung fokusiert.Hierin liegt der Ursprung und die Stärke von VB6, schnell Produkte oder Ergänzungen (damals als ActiveX)zur O�ce-Palette zu entwickeln, die Aufgaben von Anwendern automatisieren oder unterstützen.

1Windows Forms (WinForm) war lange Zeit die Basis zur Erstellung von fensterbasierten Windows-Desktop-Anwendungen. War, denn mit Windows Presentation Foundation (WPF) ist Microsoft den nächsten Schrittgegangen. Das Ergebnis sehen Sie auch in der IDE VS2010, die komplett auf WPF basiert. Mehr dazu inAbschnitt 5 auf Seite 45.

Dennoch möchte ich mit WinForm beginnen. WinForm ist eine ereignisbasierte Technologie für Fensterober�ä-chen, wie viele andere GUI-Technologien auch.

Die Erstellung der Ober�äche gestaltet sich sehr einfach, mit dem Designer im VS die entsprechenden Elementeauf die Ober�äche ziehen, possitionieren, Eigenschaften setzen und implementieren, wie auf welche Eigenschaftenreagiert werden soll. Es gibt also immer eine Design-Ansicht und eine Implementationssicht - die Designansichtlässt sich auch im Programmcode ansehen und ggf. verändern. WinForms sind prinzipiell pixelbasierte, absolutpossitionierte Fenster.

Windows Forms enthält eine Vielzahl von Steuerelementen, die Sie Formularen hinzufügen können: Steuer-elemente zur Anzeige von Textfeldern, Schalt�ächen, Dropdownfeldern, Optionsfeldern und sogar Webseiten.Wenn ein vorhandenes Steuerelement Ihre Anforderungen nicht erfüllt, unterstützen Windows Forms auch dieErstellung eigener benutzerde�nierter Steuerelemente mithilfe der UserControl-Klasse.

Ein Doppelklick im Forms-Designer bewirkt, dass das Visual Studio eine Methode für das Standardereignis derangeklickten Komponente erstellt. Um abweichend vom Standardereignis ein anderes zu behandeln, können Siedas Eigenschaftsfenster einsetzen, nachdem Sie in der Symbolleiste auf die Ereignisliste umgeschaltet haben.Wenn Sie auf ein Ereignis in der Ereignisliste, das Sie behandeln wollen, doppelklicken, wird automatisch einEreignishandler erstellt, dessen Bezeichner sich aus dem Objektnamen und dem Ereignis zusammensetzt. DieBindung des Ereignishandlers �nden Sie in InitializeComponent. Sie können dem Ereignishandler auch eineBezeichnung geben, die von der üblichen Vorgabe abweicht. Dazu tragen Sie nur den von Ihnen bevorzugtenMethodennamen in der Wertespalte neben dem Ereignis manuell ein.

Möchten Sie einen Ereignishandler gleichzeitig für mehrere Ereignisse registrieren, ist das auch sehr einfachzu lösen. Aktivieren Sie die Wertespalte zu einem Ereignis in der Ereignisliste, sehen Sie eine Schalt�äche miteinem Pfeilsymbol. Sie können darüber eine Liste ö�nen, in der alle Ereignishandler aufgeführt sind, die im

1Übersicht über Windows Forms

7

Page 8: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

2 Windows Forms

Args-Parameter denselben Typ haben. Wählen Sie daraus den Ereignishandler aus, der das markierte Ereignisverarbeiten soll.

Wenn Sie Fenster innerhalb anderer Fenster anordnen möchten, dann setzten sie die Eigenschaft IsMdiContainerauf wahr.

Aufgabe 2.1 Analysieren Sie die vorhandenen Steuerelemente und beschreiben Sie in einem Satz, wofür wel-ches Steuerelement verwendet wird.

8 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 9: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3 C# für Entwicklerinnen

Die folgenden Beispiele zeigen die Syntax von C# exemplarisch. In den Unterabschnitten werden Besonder-heiten der Sprache dargestellt. Der dazugehörige Text und die Beispiele stammen vorwiegend aus dem C#-Programmierhandbuch von Microsoft. Die Besonderheiten von C# im Vergleich zu Java listet Microsoft selbstauf: C# für Java-Entwickler

3.1 Syntax

Listing 3.1: C# Grundlagen

//Klasse − Konstruktoren

using System;

/∗ Blockkommentar ∗/abstract class Shape

{

public const double pi = Math.PI;

protected double x, y, z;

public Shape (double x, double y, double z)

{

this.x = x;

this.y = y;

this.z = z;

}

public abstract double Area();

}

class Circle: Shape

{

public Circle(): this(100) //default radius is 100

{

}

public Circle(double radius): base(radius,0,0)

{

}

public override double Area()

{

return pi*x*x;

}

}

class Cylinder: Circle

{

public Cylinder(double radius, double height): base(radius)

{

y = height;

}

public override double Area()

{

return 2*(base.Area()) + 2*pi*x*y;

}

}

class TestClass

{

public static void Main()

{

double radius = 2.5, height = 3.0;

Circle myCircle = new Circle(radius);

Cylinder myCylinder = new Cylinder(radius, height);

Console.WriteLine("Area of the circle = {0:F2}",

myCircle.Area());

9

Page 10: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.1 Syntax

Console.WriteLine("Area of the cylinder = {0:F2}",

myCylinder.Area());

}

}

// versiegelte Klassen = �nal in Java

public sealed class D

{

// Class members here.

}

//statische Klassen

public static class TemperatureConverter

{

public static double CelsiusToFahrenheit(string temperatureCelsius)

{

...

}

}

double F, C = 0;

F = TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine());

//Schnittstellen

interface IEquatable<T>

{

bool Equals(T obj);

}

public class Car : IEquatable<Car> //weitere implementierte Interfaces durch , abtrennen

{

private string make;

private string model;

public string Make

{

get {return make;}

set {make = value;}

}

public string Model

{

get {return model;}

set {model = value;}

}

// Implementation of IEquatable<T> interface

public bool Equals(Car car)

{

if (this.Make == car.Make && this.Model == car.Model)

{

return true;

}

else

return false;

}

}

//If Statement

if (condition)

{

/∗ code ∗/}

else if (otherCondition)

{

/∗ code ∗/}

else

{

/∗ code ∗/}

//switch − geht auch mit String

int cost = 0;

switch(n)

{

case 1:

cost += 25;

10 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 11: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.1 Syntax

break;

case 2:

cost += 25;

goto case 1;

case 3:

cost += 50;

goto case 1;

default:

Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");

break;

}

//For Schleife

int[] myArray;

for (int i = 0; myArray.Length; i++)

{

Console.WriteLine(myArray[i]);

}

//Foreach Schleife

int[] myArray;

foreach (int element in myArray)

{

Console.WriteLine(element);

}

//Using Statement

using (StreamWriter sw = new StreamWriter(@"C:\MyFile.txt")

{

sw.Write("Hello, File");

}

//Arrays

// Declare a single−dimensional arrayint[] array1 = new int[5];

// Declare and set array element values

int[] array2 = new int[] { 1, 3, 5, 7, 9 };

// Alternative syntax

int[] array3 = { 1, 2, 3, 4, 5, 6 };

// Declare a two dimensional array

int[,] multiDimensionalArray1 = new int[2, 3];

// Declare and set array element values

int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } };

// Declare a jagged array

int[][] jaggedArray = new int[6][];

// Set the values of the �rst array in the jagged array structure

jaggedArray[0] = new int[4] { 1, 2, 3, 4 };

Gleichheit von Objekten:Zwei Objekte sind gleich, wenn deren Inhalte gleich sind Zwei Objekte sind identisch, wenn sie die gleicheInstanz referenzieren Gleichheit de�niert sich über die virtuelle Methode System.Object.Equals identisch: Sys-tem.Object.Equals = true gleich: System.Object.Equals.Value = true.

Ähnlich wie in anderen OO-Sprachen gibt es die Zugri�smodi�zierer private, public und protected. C# unter-stützt auch internal.1

public Auf den Typ oder Member kann von jedem Code in der gleichen Assembly siehe Abschnitt 10.2 auf Seite78 oder einer anderen Assembly, die darauf verweist, zugegri�en werden.

private Auf den Typ oder Member kann nur von Code in der gleichen Klasse oder Struktur zugegri�en werden.

protected Auf den Typ oder Member kann nur von Code in der gleichen Klasse oder Struktur oder in einerabgeleiteten Klasse zugegri�en werden.

1Zugri�smodi�zierer

11 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 12: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.1 Syntax

internal Auf den Typ oder Member kann von jedem Code in der gleichen Assembly zugegri�en werden, jedochnicht von Code in einer anderen Assembly.

protected internal Auf den Typ oder Member kann von jedem Code in der gleichen Assembly oder von jederabgeleiteten Klasse in einer anderen Assembly zugegri�en werden.

Methoden und Konstruktoren können überladen werden, ebenso können die meisten Operatoren überladenwerden: Arithmetische, Vergleichs-, Logik- und Bedingungsoperatoren. Ausnahmen sind Zuweisungsoperatoren,Spezialoperatoren wie sizeof, new, is und typeof.

3.1.1 Operatoren

In Bezug auf Operatoren möchte ich auch noch auf den tertiären Operator ? mit : hinweisen, den es auchin anderen Programmiersprachen wie z.B. PHP gibt, der jedoch seltener als wünschenswert eingesetzt wird.Ähnlich dazu, gibt es den ?? Operator. Dieser ist äuÿerst hilfreich, wenn Objekte auf null hin überprüft werdensollen. Es wird die Linke Seite evaluiert. Ist diese nicht null, wird der linke Wert zurückgegeben, andernfalls derrechte.

Listing 3.2: Operatoren

//Variante mit Bedingung?Wahrfall:Falschfall;

string r = ( v == null ) ? "null" : v;

//Variante mit dem ??−Operator:string r = v ?? "null";

//+ einmal anders

class Matrix {

...

Matrix operator +(Matrix l, Matrix r)

{

Matrix e = new Matrix();

e = l mit r verknüpfen

return e;

}

}

...

Matrix a = new Matrix(...), b = new Matrix(...);

Matrix c = a + b;

3.1.2 Partial

Die De�nitionen partieller Typen ermöglichen es, die De�nition einer Klasse, einer Struktur oder einer Schnitt-stelle auf mehrere Dateien aufzuteilen. Einen Klassen-, Struktur- oder Schnittstellentyp auf mehrere Dateienaufzuteilen kann hilfreich sein, wenn Sie mit groÿen Projekten oder mit automatisch generiertem Code wiez.B. von Der Windows Forms-Designer bereitgestellt. Verwenden Sie hierfür das Schlüsselwort partial, also z.B.partial class A in zwei Dateien.

3.1.3 Strings

Eine Zeichenfolge ist ein Objekt vom Typ String, dessen Wert Text ist. Intern wird der Text als schreibge-schützte Au�istung von Char-Objekten gespeichert, von denen jedes ein in UTF-16 codiertes Unicode-Zeichendarstellt. Eine C#-Zeichenfolge wird nicht mit einem NULL-Zeichen am Ende terminiert (im Gegensatz zu Cund C++). Deshalb kann eine C#-Zeichenfolge eine beliebige Anzahl eingebetteter NULL-Zeichen ('0') enthalten. Die Länge einer Zeichenfolge stellt die Anzahl der Zeichen unabhängig davon dar, ob die Zei-chen aus Unicode-Ersatzzeichenpaaren gebildet werden oder nicht. Um in einer Zeichenfolge auf die einzelnenUnicode-Codepunkte zuzugreifen, verwenden Sie das StringInfo-Objekt.

In C# ist das string-Schlüsselwort ein Alias für String. Deshalb entsprechen sich String und string, und Sie kön-nen eine beliebige Namenskonvention verwenden. Die String-Klasse stellt viele Methoden zum sicheren Erstellen,Bearbeiten und Vergleichen von Zeichenfolgen bereit.

Initialisieren Sie eine Zeichenfolge mit dem konstanten Empty-Wert, um ein neues String-Objekt zu erstellen,dessen Zeichenfolge die Länge NULL aufweist. Durch die Initialisierung von Zeichenfolgen mit dem Wert Empty

12 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 13: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.2 Performance

anstelle von null können Sie die Chancen einer NullReferenceException reduzieren. Verwenden Sie die statischeIsNullOrEmpty(String)-Methode, um den Wert einer Zeichenfolge zu prüfen, bevor Sie versuchen, darauf zuzu-greifen.

Da eine Zeichenfolgenänderung einer neuen Zeichenfolgenerstellung gleichkommt, müssen Sie beim Erstellen vonVerweisen auf Zeichenfolgen vorsichtig sein. Wenn Sie einen Verweis auf eine Zeichenfolge erstellen und danndie ursprüngliche Zeichenfolge ändern, zeigt der Verweis weiterhin auf das ursprüngliche Objekt und nicht aufdas neue Objekt, das beim Ändern der Zeichenfolge erstellt wurde. Der folgende Code veranschaulicht diesesVerhalten:

Listing 3.3: Stringreferenz

string s1 = "Hello ";

string s2 = s1;

s1 += "World";

System.Console.WriteLine(s2);

//Output: Hello

Listing 3.4: Stringreferenz

string s1 = "Hello ";

string s2 = s1;

s1 = "Hello World";

System.Console.WriteLine(s2);

Aufgabe 3.1 Was wird in Listing 3.4 ausgegeben?

Längere Strings oder Pfade können Sie in C# mit einem vorangestellten @ de�nieren.

Listing 3.5: Strings mit @ - ähnlich Heredoc von PHP

string path = @"c:\Docs\Source\a.txt";

string quote = @"Her name was ""Sara.""";

string miniTemplate = @"Hello {0},

Your friend {1} sent you this message:

{2}

That's all!";

string populatedTemplate = String.Format(miniTemplate, "Fred", "Jack", "HelloWorld!");

System.Console.WriteLine(populatedTemplate);

3.2 Performance

Zeichenfolgenobjekte sind unveränderlich. Das bedeutet, dass sie nicht mehr geändert werden können, nachdemsie erstellt wurden. Alle String-Methoden und C#-Operatoren, mit denen eine Zeichenfolge geändert werdenkönnte, geben diese Ergebnisse in einem neuen Zeichenfolgenobjekt zurück. Im folgenden Beispiel bleiben beimVerketten des Inhalts von s1 und s2 zu einer einzelnen Zeichenfolge die beiden ursprünglichen Zeichenfolgenunverändert. Der Operator += erstellt eine neue Zeichenfolge, die die kombinierten Inhalte enthält. Dieses neueObjekt wird der Variablen s1 zugewiesen. Das ursprüngliche Objekt, das s1 zugewiesen wurde, wird für dieGarbage Collection freigegeben, da keine andere Variable einen Verweis darauf enthält.

Mit der Klasse StringBuilder können Sie ohne das Zerstören und Neuerstellen von Objekten speichere�zientZeichenketten zusammenführen. Die Klasse enthält auch weitere nützliche Methoden.

Listing 3.6: StringBuilder zur Verkettung von Strings

int x = 4;

StringBuilder sb = new StringBuilder();

sb.append(x.toString());

sb.AppendFormat("GHI{0}{1}", 'J', 'k');

13 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 14: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.3 Typen

sb.append(" und ");

//Einen Text am Anfang einfügen

sb.Insert(0, "Alphabet: ");

sb.Replace('k', 'K');

Console.WriteLine("{0} chars: {1}", sb.Length, sb.ToString());

Noch eine Performanceverbesserung gibt es beim Prüfen auf Leerstrings: Der Vergleich von Zeichenfolgen mitder System.String.Length-Eigenschaft oder der System.String.IsNullOrEmpty(System.String)-Methode ist be-deutend schneller als ein Vergleich mit Equals.

Listing 3.7: String auf leer prüfenstring s1 = "test";

//nicht performant if (s1 == "")

if ( !String.IsNullOrEmpty(s1) )

{

Console.WriteLine("s1 != null and s1.Length != 0.");

}

Neben StringBuilder und dem Prüfen auf Leerstrings gibt es weitere Möglichkeiten, Code performanter zuschreiben. Wenn Sie ein Feld als static und readonly deklarieren und mit einem Wert initialisieren, dann solltenSie stattdessen const verwenden. Der Wert eines const-Felds wird zur Kompilierungszeit berechnet und inden Metadaten gespeichert. Dadurch wird � im Vergleich zu einem static readonly-Feld � die Laufzeitleistunggesteigert. Wenn mit dem is-Operator von C# getestet wird, ob die Umwandlung erfolgreich durchgeführtwerden kann, bevor die eigentliche Umwandlung erfolgt, sollten Sie erwägen, stattdessen das Ergebnis desas-Operators zu testen. Dieses Vorgehen bietet die gleiche Funktionalität ohne die implizite vom is-Operatorausgeführte Umwandlungsoperation.

Listing 3.8: is/as performant einsetzen// The 'is ' statement performs a cast operation.

if(obj is Control)

{

// The 'as' statement performs a duplicate cast operation.

Control aControl = obj as Control;

// Use aControl.

}

//besser:

Control aControl = obj as Control;

if(aControl != null)

{

// Use aControl.

}

Zur Analyse Ihres Codes, können Sie eine hilfreiche Eigentschaft Ihres Projekts setzen: Option Codeanalyse ak-tivieren. Weitere Empfehlungen zur Erstellung von hochwertigem Code �nden Sie unter Verfassen von qualitativhochwertigem Quellcode und darin insbesondere Leistungswarnungen.

3.3 Typen

3.3.1 Strukturen

Klassen und Strukturen sind zwei der grundlegenden Konstrukte des allgemeinen Typsystems in .NET Frame-work. Bei beiden handelt es sich um eine Datenstruktur, die einen als logische Einheit zusammengehörendenSatz von Daten und Verhalten kapselt. Die Daten und Verhalten sind die Member der Klasse oder Struktur.Diese enthalten deren Methoden, Eigenschaften, Ereignisse usw. Eine Struktur ist ein Werttyp (siehe Abschnitt10.5 auf Seite 1). Wenn eine Struktur erstellt wird, enthält die Variable, der die Struktur zugewiesen wird,die eigentlichen Daten der Struktur. Wenn die Struktur einer neuen Variable zugewiesen wird, werden diesekopiert. Die neue Variable und die ursprüngliche Variable enthalten daher zwei separate Kopien der gleichenDaten. Änderungen an einer Kopie wirken sich nicht auf die andere Kopie aus.

Im Allgemeinen werden Klassen zum Modellieren von komplexerem Verhalten oder von Daten verwendet, diedafür vorgesehen sind, nach der Erstellung eines Klassenobjekts geändert zu werden. Strukturen sind am bestenfür kleine Datenstrukturen geeignet, die überwiegend Daten enthalten, deren Änderung nach der Erstellung derStruktur nicht vorgesehen ist.

14 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 15: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.3 Typen

Besonderheiten von Strukturen im Vergleich zu Klassen:

• Strukturen haben fast die gleiche Syntax wie Klassen, unterliegen jedoch mehr Beschränkungen als Klas-sen:

• Innerhalb einer Strukturdeklaration können Felder nur dann initialisiert werden, wenn sie als konstantoder statisch deklariert werden.

• Eine Struktur kann keinen Standardkonstruktor (ein Konstruktor ohne Parameter) und keinen Destruktordeklarieren.

• Strukturen können nicht von Klassen oder anderen Strukturen erben.

• Strukturen werden durch Zuweisung kopiert. Wenn eine Struktur einer neuen Variablen zugewiesen wird,werden alle Daten kopiert. Änderungen an der neuen Kopie wirken sich nicht auf die Daten in der ur-sprünglichen Kopie aus.

• Strukturen sind Werttypen, und Klassen sind Referenztypen.

• Strukturen können im Gegensatz zu Klassen ohne einen Operator new instanziiert werden.

• Strukturen können Konstruktoren deklarieren, die über Parameter verfügen.

• Eine Struktur ist nicht in der Lage, von einer anderen Struktur oder Klasse zu erben, und sie kannauch nicht die Basis einer Klasse sein. Alle Strukturen erben direkt von System.ValueType, welcher vonSystem.Object erbt.

• Eine Struktur kann Schnittstellen implementieren.

Lapidar gesagt, Strukturen sind gut für die Darstellung zusammengehörender Daten, für die Modellierung vonVerhalten und wenn Vererbung benötigt wird, sollten Klassen verwendet werden.

Listing 3.9: Beispiel für Strukturen

public struct CoOrds

{

public int x, y;

public CoOrds(int p1, int p2)

{

x = p1;

y = p2;

}

}

struct/Objekt Parameterübergabe:Da Strukturen Werttypen sind und Objekte Referenztypen sind, gilt: Wenn struct an eine Methode übergebenwird, wird eine Kopie der Struktur übergeben. Bei der Übergabe einer class-Instanz wird jedoch ein Verweisübergeben.

3.3.2 Generika

Manchmal möchte man eine Klasse oder Parameter einer Methode so allgemein halten, dass sie mit Objektenverschiedener Typen arbeiten können. Dies könnte man durch den allgemeinsten Datentyp Object erreichen,damit können Typen jedoch gemischt werden. Möchte man auf Typsicherheit nicht verzichten, so kann manGenerika einsetzen. Häu�g verwendet man generische Listen, um z.B. Stringlisten, Int-Listen oder Listen vonObjekten einer Klasse zu verarbeiten.

Listing 3.10: Generika

public class GenericList<T>

{

void Add(T input) { }

}

class TestGenericList

{

private class ExampleClass { }

static void Main()

15 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 16: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.3 Typen

{

GenericList<int> list1 = new GenericList<int>();

GenericList<string> list2 = new GenericList<string>();

GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();

}

}

3.3.3 Listen

Abbildung 3.1: Listen

.NET Framework stellt spezialisierte Klassen für das Speichern und Abrufen von Daten bereit. Diese Klassenbieten Unterstützung für Stapel, Warteschlangen, Listen und Hashtabellen. Die meisten Au�istungsklassenimplementieren dieselben Schnittstellen. Diese Schnittstellen können geerbt werden, um neue, auf speziellereDatenspeicherungsanforderungen zugeschnittene Au�istungsklassen zu erstellen.

Hashtable stellt eine Au�istung von Schlüssel-Wert-Paaren dar, die nach dem Hashcode des Schlüssels organisiertsind.

Die generische Dictionary<(Of <(TKey, TValue>)>)-Klasse stellt eine Zuordnung von einem Satz von Schlüs-seln zu einem Satz von Werten bereit. Jede Hinzufügung zum Wörterbuch besteht aus einem Wert und dem zu-geordneten Schlüssel. Ein Wert kann anhand des zugehörigen Schlüssels sehr schnell abgerufen werden (beinaheein O(1)-Vorgang), da die Dictionary<(Of <(TKey, TValue>)>)-Klasse in Form einer Hashtabelle implemen-tiert ist. Wenn ein Objekt im Dictionary<(Of <(TKey, TValue>)>) als Schlüssel verwendet wird, darf es nichtauf eine Weise geändert werden, die sich auf seinen Hashwert auswirkt. Jeder Schlüssel in einem Dictionary<(Of<(TKey, TValue>)>) muss für den Gleichheitsvergleich des Wörterbuches eindeutig sein.

ArrayList implementiert die IList-Schnittstelle unter Verwendung eines Arrays, das nach Bedarf dynamischvergröÿert wird. Es wird nicht sichergestellt, dass die ArrayList sortiert ist. Sie müssen die ArrayList sortieren,bevor Sie Vorgänge wie BinarySearch durchführen, die eine sortierte ArrayList voraussetzen.

Die List<(Of <(T>)>)-Klasse stellt die generische Entsprechung der ArrayList-Klasse dar. Sie implementiertdie generische IList<(Of <(T>)>)-Schnittstelle unter Verwendung eines Arrays, das nach Bedarf dynamischvergröÿert wird.

Queue stellt eine FIFO-Au�istung (First-In-First-Out) von Objekten dar.

Stack stellt eine LIFO (Last-In-First-Out)-Au�istung variabler Gröÿe von Instanzen desselben beliebigen Typsdar.

Listing 3.11: ListenHashtable openWith = new Hashtable();

openWith.Add("txt", "notepad.exe");

Dictionary<string, string> openWith2 =

new Dictionary<string, string>();

16 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 17: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.3 Typen

openWith2.Add("txt", "notepad.exe");

ArrayList myAL = new ArrayList();

myAL.Add("Hello");

List<string> dinosaurs = new List<string>();

dinosaurs.Add("Tyrannosaurus");

Queue<string> numbers = new Queue<string>();

numbers.Enqueue("one");

Stack<string> numbers = new Stack<string>();

numbers.Push("one");

Wählen Sie Ihre System.Collections-Klasse sorgfältig aus. Wenn der falsche Typ ausgewählt wird, kann dies dieVerwendung der Au�istung einschränken.

Beachten Sie Folgendes:

• Benötigen Sie eine sequenzielle Liste, bei der ein Element normalerweise verworfen werden kann, wennsein Wert abgerufen wurde?

• Benötigen Sie Zugri� auf Elemente in einer bestimmten Reihenfolge, z. B. FIFO-, LIFO- oder beliebigerReihenfolge?

• Möchten Sie auf die einzelnen Elemente mithilfe eines Indizes zugreifen? ArrayList, List, Hashtable, Sor-tedList, ListDictionary und StringDictionary

• Wird jedes Element einen Wert enthalten, eine Kombination aus einem Schlüssel und einem Wert odereine Kombination aus einem Schlüssel und mehreren Werten?

• Müssen die Elemente unabhängig von der Reihenfolge ihrer Eingabe sortiert werden?

• Ist schnelles Suchen und Abrufen von Informationen erforderlich?

• Werden Au�istungen gebraucht, die nur Zeichenfolgen annehmen?

3.3.4 Enumerationen

Ein Enumerationstyp (auch als Enumeration bezeichnet) bietet eine e�ziente Möglichkeit, einen Satz benannterintegraler Konstanten zu de�nieren, die einer Variablen zugewiesen werden können. Angenommen, Sie müsseneine Variable de�nieren, deren Wert einen Wochentag darstellt. Es gibt nur sieben sinnvolle Werte, die dieseVariable speichern kann. Um diese Werte zu de�nieren, können Sie einen Enumerationstyp verwenden, der durchdie Verwendung des enum-Schlüsselworts deklariert wird. Der zugrunde liegende Standardtyp aller Elemente inder Enumeration lautet int.

Listing 3.12: Enumerationen

enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

enum Months : byte { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

Days meetingDay = Days.Monday;

enum MachineState

{

PowerOff = 0,

Running = 5,

Sleeping = 10,

Hibernating = Sleeping + 5

}

Sie können einen Enumerationstyp verwenden, um Bit�ags zu de�nieren. Dadurch kann eine Instanz des Enu-merationstyps eine Kombination aus den Werten speichern, die in der Enumeratorliste de�niert sind.

Im folgenden Beispiel wird eine andere Version des Days-Enumerators, der als Days2 bezeichnet ist, de�niert.Days2 weist das Flags-Attribut auf, und jedem Wert wird die nächste höhere Potenz von 2 zugewiesen. Sokönnen Sie eine Days2-Variable erstellen, deren Wert Days2.Tuesday und Days2.Thursday lautet. Um ein Flageines Enumerators festzulegen, verwenden Sie den logischen OR-Operator. Um zu ermitteln, ob ein bestimmtesFlag festgelegt ist, verwenden Sie eine logische AND-Operation.

17 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 18: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.4 Sprachbesonderheiten

Listing 3.13: Enumeration als Flags

[Flags]

enum Days2

{

None = 0x0,

Sunday = 0x1,

Monday = 0x2,

Tuesday = 0x4,

Wednesday = 0x8,

Thursday = 0x10,

Friday = 0x20,

Saturday = 0x40

}

class MyClass

{

Days2 meetingDays = Days2.Tuesday | Days2.Thursday;

private void pleassure() {

// Initialize with two �ags using bitwise OR.

meetingDays = Days2.Tuesday | Days2.Thursday;

// Set an additional �ag using bitwise OR.

meetingDays = meetingDays | Days2.Friday;

Console.WriteLine("Meeting days are {0}", meetingDays);

// Output: Meeting days are Tuesday, Thursday, Friday

// Remove a �ag using bitwise XOR.

meetingDays = meetingDays ^ Days2.Tuesday;

Console.WriteLine("Meeting days are {0}", meetingDays);

// Output: Meeting days are Thursday, Friday

// Test value of �ags using bitwise AND.

bool test = (meetingDays & Days2.Thursday) == Days2.Thursday;

Console.WriteLine("Thursday {0} a meeting day.", test == true ? "is" : "is not");

// Output: Thursday is a meeting day.

3.4 Sprachbesonderheiten

3.4.1 Log/Trace

Mit der Trace-Klasse lassen sich Anwendungen instrumentieren. Von einer laufenden Anwendung können Siedamit Informationsmeldungen erhalten, die beim Diagnostizieren von Problemen oder Analysieren der Leistunghilfreich sind.

Listing 3.14: Trace

Trace.WriteLine("Entering Main");

Mächtiger und besser zu kon�gurieren ist log4net.

3.4.2 Ausnahmen

2Die Features zur Ausnahmebehandlung in C# helfen Ihnen, wenn bei der Ausführung eines Programms uner-wartete oder auÿergewöhnliche Situationen auftreten. Bei der Ausnahmebehandlung wird mithilfe der Schlüs-selwörter try, catch und �nally versucht, Aktionen auszuführen, die möglicherweise fehlschlagen, um Fehlerzu behandeln und anschlieÿend die Ressourcen zu bereinigen. Ausnahmen können von der Common LanguageRuntime (CLR � ähnlich zur Virtual Maschine von Java), durch .NET Framework oder Bibliotheken von Drit-tanbietern oder durch den Anwendungscode generiert werden. Ausnahmen werden mit dem throw-Schlüsselworterstellt. Es gibt jedoch im Gegensatz zu Java keine Syntax, die kennzeichnet, welche Klassen welche Ausnahmenwerfen können � throws wird in C# also nur zum tatsächlichen Erzeugen/werfen von Ausnahmen verwendet.

In vielen Fällen wird eine Ausnahme nicht von einer Methode ausgelöst, die direkt durch den Code aufgerufenwurde, sondern von einer anderen Methode, die sich weiter unten in der Aufru�iste be�ndet. In diesem Fall

2Ausnahmen und Ausnahmebehandlung

18 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 19: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.4 Sprachbesonderheiten

Abbildung 3.2: Au�nden einer passenden catch-De�nition

entlädt die CLR die Aufru�iste, sucht eine Methode mit einem catch-Block für den spezi�schen Ausnahmetypund führt den ersten catch-Block aus, der gefunden wird. Falls kein entsprechender catch-Block in der Aufru�istegefunden wird, wird der Prozess beendet und eine Meldung für den Benutzer angezeigt.

Ausnahmen sind Typen, die letztlich alle von System.Exception abgeleitet werden. Fangen Sie keine Ausnahmeab, es sei denn, Sie können sie bearbeiten, und belassen Sie die Anwendung in einem bekannten Zustand. WennSie System.Exception abfangen, lösen Sie es erneut mit dem throw-Schlüsselwort am Ende des catch-Blocks aus.Ausnahmeobjekte enthalten ausführliche Informationen zum Fehler, z. B. den Zustand der Aufru�iste und eineTextbeschreibung des Fehlers. Nicht abgefangene Ausnahmen werden von einem generischen Ausnahmehandlerdes Systems behandelt, der ein Dialogfeld anzeigt. Im Gegensatz zu Java, gibt es keine Checked Exceptions. Eine

Abbildung 3.3: CheckedException Java vs C#

Checked Exception ist eine Ausnahme, bei der der Compiler prüft, ob alle Stellen, wo sie auftreten kann, durchCode zum Abfangen der Ausnahme abgedeckt sind. Der Code zum Abfangen kann dabei innerhalb derselbenMethode stehen, in der die Ausnahme auftreten kann, oder auch in aufrufenden Methoden. Der Compiler wirdbei Java durch das Schlüsselwort throws unterstüzt. Bei C# ist dies nicht so umgesetzt. Fängt niemand eineException, so bricht das Programm mit dem generischen Fehlerdialog ab. Per Design ist dies vermutlich einNachteil. Da jedoch viele Java-Programmierer einfach mit

Listing 3.15: Schlechter Code � Fehler schluckencatch (E e) {}

Fehler schlucken, führt dies weningstens nicht zu nicht-erklärbaren Verhalten, sondern der Fehler bleibt �erhal-ten�.

19 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 20: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.4 Sprachbesonderheiten

3.4.3 Indexer

3 einer Klasse oder Struktur auf dieselbe Weise wie Arrays. Abgesehen davon, dass ihre Accessoren Parameterakzeptieren, sind Indexer mit Eigenschaften vergleichbar.

Im folgenden Beispiel wird eine generische Klasse de�niert, die mit einfachen get-Accessormethoden und set-Accessormethoden ausgestattet ist. Mit diesen Methoden werdenWerte zugewiesen und abgerufen. Die Program-Klasse erstellt eine Instanz dieser Klasse für das Speichern von Zeichenfolgen.

Listing 3.16: Indexer

class SampleCollection<T>

{

private T[] arr = new T[100];

public T this[int i]

{

get

{

return arr[i];

}

set

{

arr[i] = value;

}

}

}

// This class shows how client code uses the indexer

class Program

{

static void Main(string[] args)

{

SampleCollection<string> stringCollection = new SampleCollection<string>();

stringCollection[0] = "Hello, World";

System.Console.WriteLine(stringCollection[0]);

}

}

Übersicht über Indexer

• Durch Indexer können Objekte auf ähnliche Weise wie Arrays indiziert werden.

• Ein get-Accessor gibt einen Wert zurück. Ein set-Accessor weist einen Wert zu.

• Das this-Schlüsselwort wird zum De�nieren der Indexer verwendet.

• Mithilfe des value-Schlüsselworts wird der Wert de�niert, der vom set-Indexer zugewiesen wird.

• Indexer müssen nicht mit ganzzahligen Werten indiziert werden. Sie können frei wählen, wie Sie denbestimmten Suchmechanismus de�nieren.

• Indexer können überladen werden.

• Indexer können mehr als einen formalen Parameter haben, z. B. wenn auf ein zweidimensionales Arrayzugegri�en wird.

3.4.4 Iteratoren

Möchte man eine Klasse mit foreach durchlaufen, so benötigt man die Methode GetEnumerator.

Listing 3.17: Iteratoren

public class DaysOfTheWeek : System.Collections.IEnumerable

{

string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

public System.Collections.IEnumerator GetEnumerator()

{

for (int i = 0; i < days.Length; i++)

{

3Indexer

20 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 21: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

yield return days[i];

}

}

}

class TestDaysOfTheWeek

{

static void Main()

{

// Create an instance of the collection class

DaysOfTheWeek week = new DaysOfTheWeek();

// Iterate with foreach

foreach (string day in week)

{

System.Console.Write(day + " ");

}

}

}

3.5 Delegaten

Ein Delegate ist ein Objekt, welches auf eine Methode zeigt, die zu einem späteren Zeitpunkt aufgerufen werdenkann.

Delegaten verfügen über folgende Eigenschaften:

• Delegaten ähneln C++-Funktionszeigern, sind aber typsicher.

• Delegaten ermöglichen es, Methoden als Parameter zu übergeben.

• Delegaten können zum De�nieren von Rückrufmethoden verwendet werden.

• Delegaten können miteinander verkettet werden. So können beispielsweise mehrere Methoden für eineinziges Ereignis aufgerufen werden.

Delegate:Delegate folgen dem Design Pattern Publish/Subscribe.

Das folgende Listing zeigt, wie ein Delegate de�niert wird.

Listing 3.18: Delegatenpublic delegate void Del<T>(T item);

public void Notify(int i) { }

Del<int> d1 = new Del<int>(Notify);

//gleichwertig zu

Del<int> d2 = Notify;

Dieses Beispiel zeigt ein Delegate mit Verweis auf eine statische Funktion und mit einer nicht-statischen Funk-tion

Listing 3.19: Delegaten, Beispiel1 namespace Bookstore

2 {

3 using System.Collections;

4 public struct Book

5 {

6 public string Title; // Title of the book.

7 public string Author; // Author of the book.

8 public decimal Price; // Price of the book.

9 public bool Paperback; // Is it paperback?

1011 public Book(string title, string author, decimal price, bool paperBack)

12 {

13 Title = title;

14 Author = author;

15 Price = price;

16 Paperback = paperBack;

21 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 22: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

Abbildung 3.4: Delegaten

17 }

18 }

19 public delegate void ProcessBookDelegate(Book book);

20 public class BookDB

21 {

22 ArrayList list = new ArrayList();

23 public void AddBook(string title, string author, decimal price, bool paperBack)

24 {

25 list.Add(new Book(title, author, price, paperBack));

26 }

27 public void ProcessPaperbackBooks(ProcessBookDelegate processBook)

28 {

29 foreach (Book b in list)

30 {

22 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 23: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

31 if (b.Paperback)

32 // Calling the delegate:

33 processBook(b);

34 }

35 }

36 }

37 }

38 namespace BookTestClient

39 {

40 using Bookstore;

41 class PriceTotaller

42 {

43 int countBooks = 0;

44 decimal priceBooks = 0.0m;

4546 internal void AddBookToTotal(Book book)

47 {

48 countBooks += 1;

49 priceBooks += book.Price;

50 }

5152 internal decimal AveragePrice()

53 {

54 return priceBooks / countBooks;

55 }

56 }

57 class TestBookDB

58 {

59 static void PrintTitle(Book b)

60 {

61 System.Console.WriteLine(" {0}", b.Title);

62 }

63 static void Main()

64 {

65 BookDB bookDB = new BookDB();

66 AddBooks(bookDB);

67 System.Console.WriteLine("Paperback Book Titles:");

68 bookDB.ProcessPaperbackBooks(PrintTitle);

69 PriceTotaller totaller = new PriceTotaller();

70 bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);

71 System.Console.WriteLine("Average Paperback Book Price: ${0:#.##}",

72 totaller.AveragePrice());

73 }

74 static void AddBooks(BookDB bookDB)

75 {

76 bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);

77 bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true);

78 bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false);

79 bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true);

80 }

81 }

82 }

Aufgabe 3.2 Kommentieren Sie das Programm ausführlich. Beschreiben Sie den Ablauf in Worten oder malenSie ein geeignetes Bild. Was gibt das Programm aus?

Ein Delegat kann entweder (wie in diesem Beispiel) synchron oder mithilfe der BeginInvoke-Methode und derEndInvoke-Methode asynchron aufgerufen werden.

3.5.1 Mulitcastdelegaten

Eine nützliche Eigenschaft von delegate-Objekten besteht darin, dass sie mithilfe des Operators + einer Dele-gatinstanz als Multicast zugewiesen werden können. Ein zusammengesetzter Delegat ruft die beiden Delegatenauf, aus denen er besteht. Es können ausschlieÿlich Delegaten mit demselben Typ kombiniert werden.

Mithilfe des Operators - kann ein Komponentendelegat aus einem zusammengesetzten Delegaten entfernt wer-den. Das folgende Beispiel zeigt die Verwendung dieser Operatoren.

Listing 3.20: Multicastdelegaten

23 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 24: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

delegate void Del(string s);

class TestClass

{

static void Hello(string s)

{

System.Console.WriteLine(" Hello, {0}!", s);

}

static void Goodbye(string s)

{

System.Console.WriteLine(" Goodbye, {0}!", s);

}

static void Main()

{

Del a, b, c, d;

a = Hello;

b = Goodbye;

c = a + b;

d = c - a;

System.Console.WriteLine("Invoking delegate a:");

a("A");

System.Console.WriteLine("Invoking delegate b:");

b("B");

System.Console.WriteLine("Invoking delegate c:");

c("C");

System.Console.WriteLine("Invoking delegate d:");

d("D");

}

}

Aufgabe 3.3 Was gibt das Programm Multicastdelegaten aus?

3.5.2 Schnittstelle vs. Delegate

4Sowohl Delegaten als auch Schnittstellen ermöglichen es einem Klassendesigner, Typdeklarationen und Imple-mentierung voneinander zu trennen. Eine bestimmte Schnittstelle kann von jeder Klasse oder Struktur geerbtund implementiert werden. Ein Delegat kann für eine Methode jeder beliebigen Klasse erstellt werden, solan-ge die Methode mit der Methodensignatur des Delegaten übereinstimmt. Ein Schnittstellenverweis oder einDelegat kann von jedem Objekt verwendet werden, ohne über Informationen zu der Klasse zu verfügen, diedie Schnittstelle oder die Delegatmethode implementiert. Wann sollte ein Klassendesigner in Anbetracht dieserGemeinsamkeiten einen Delegaten verwenden, und wann sollte eine Schnittstelle verwendet werden?

Verwenden Sie Delegaten, wenn

• ein Ereignisentwurfsmuster verwendet wird.

• eine statische Methode gekapselt werden soll.

• der Aufrufer keinen Zugri� auf weitere Eigenschaften, Methoden oder Schnittstellen des Objekts benötigt,das die Methode implementiert.

• die einfache Verknüpfung von Delegaten gewünscht ist.

• eine Klasse möglicherweise mehr als eine Implementierung der Methode benötigt.

Verwenden Sie Schnittstellen, wenn

• es eine Gruppe verwandter Methoden gibt, die möglicherweise aufgerufen werden.

• eine Klasse nur eine Implementierung der Methode benötigt.

• die Klasse, die die Schnittstelle verwendet, eine Umwandlung dieser Schnittstelle in andere Schnittstellenoder Klassentypen durchführt.

• die Methode, die implementiert wird, mit dem Typ oder der Identität der Klasse verknüpft ist, wie zumBeispiel bei Vergleichsmethoden.

4Wann sind Delegaten Schnittstellen vorzuziehen?

24 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 25: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

Ein gutes Beispiel für die Verwendung einer Schnittstelle mit nur einer Methode anstatt eines Delegaten istIComparable oder die generische Version IComparable<(Of <(T>)>). IComparable deklariert die CompareTo-Methode, die eine ganze Zahl zurückgibt, die ein Kleiner-als-, Gleich- oder Gröÿer-als-Verhältnis zwischen zweiObjekten desselben Typs angibt. IComparable kann als Grundlage für einen Sortieralgorithmus verwendet wer-den. Die Verwendung einer Delegatenvergleichsmethode als Grundlage für den Sortieralgorithmus ist zwar mög-lich, aber nicht optimal. Da die Vergleichsfunktionalität zu einer Klasse gehört und der Vergleichsalgorithmuszur Laufzeit nicht geändert wird, stellt eine Schnittstelle mit nur einer Methode die ideale Lösung dar.

3.5.3 Ereignisse

Ereignisse aktivieren eine Klasse oder ein Objekt, um Informationen über Aktionen von Interesse an andereKlassen oder Objekte zu übermitteln. Die Klasse, die das Ereignis sendet (oder auslöst), wird als Herausgeberbezeichnet und die Klassen, die das Ereignis empfangen (oder behandeln), werden als Abonnenten bezeichnet.

In einer typischen C#Windows Forms- oder -Webanwendung abonnieren Sie Ereignisse, die von Steuerelementenwie Schalt�ächen und Listenfeldern ausgelöst wurden. In der integrierten Entwicklungsumgebung (IDE) vonVisual C# können Sie die Ereignisse durchsuchen, die ein Steuerelement verö�entlicht, und diejenigen auswählen,die Sie behandeln möchten. Die IDE fügt automatisch eine leere Ereignishandlermethode und den Code zumAbonnieren des Ereignisses hinzu.

Ereignisse verfügen über folgende Eigenschaften:

• Der Herausgeber bestimmt, wann ein Ereignis ausgelöst wird; die Abonnenten bestimmen, welche Maÿ-nahme als Reaktion auf das Ereignis ergri�en wird.

• Ein Ereignis kann mehrere Abonnenten haben. Ein Abonnent kann mehrere Ereignisse von mehrerenHerausgebern behandeln.

• Ereignisse, die keine Abonnenten haben, werden nie ausgeführt.

• Ereignisse dienen normalerweise zur Signalisierung von Benutzeraktionen wie das Klicken auf Schalt�ächenoder Auswählen von Menüs in der gra�schen Benutzerober�äche.

• Ereignisse können verwendet werden, um Threads zu synchronisieren.

• In der .NET Framework-Klassenbibliothek basieren Ereignisse auf dem EventHandler-Delegaten und derEventArgs-Basisklasse

Listing 3.21: Ereignisse

1 /∗ De�nition in System−Namensraum2 public delegate void EventHandler<TEventArgs>(

3 Object sender,

4 TEventArgs e

5 )

6 ∗/7 public class MyEventArgs : EventArgs

8 {

9 privatestring msg;

1011 public MyEventArgs( string messageData ) {

12 msg = messageData;

13 }

14 publicstring Message {

15 get { return msg; }

16 set { msg = value; }

17 }

18 }

19 public class HasEvent

20 {

21 public void DemoEvent(string val)

22 {

23 // Copy to a temporary variable to be thread−safe.24 EventHandler<MyEventArgs> temp = SampleEvent;

25 if (temp != null)

26 temp(this, new MyEventArgs(val));

27 }

28 }

29 public class Sample

30 {

31 publics tatic void Main()

25 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 26: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

32 {

33 HasEvent he = new HasEvent();

34 he.SampleEvent +=

35 new EventHandler<MyEventArgs>(SampleEventHandler);

36 he.DemoEvent("Hey there, Bruce!");

37 he.DemoEvent("How are you today?");

3839 }

40 private static void SampleEventHandler(object src, MyEventArgs mea)

41 {

42 Console.WriteLine(mea.Message);

43 }

44 }

Aufgabe 3.4 Kommentieren Sie das Programm ausführlich. Beschreiben Sie den Ablauf in Worten oder malenSie ein geeignetes Bild. Was gibt das Programm aus?

Aufgabe 3.5 Arbeiten Sie das Zusammenspiel von Delegaten und Events anhand des Artikels im Anhang ??heraus.

Aufgabe 3.6 Kommentieren Sie das Programm ausführlich. Beschreiben Sie den Ablauf in Worten oder malenSie ein geeignetes Bild.

Listing 3.22: Asynchrone Delegate1 using System;

2 using System.Threading;

3 using System.Runtime.Remoting.Messaging;

4 //Quelle: http://msdn.microsoft.com/de−de/library/h80ttd5f%28en−us,VS.100%29.aspx5 namespace Examples.AdvancedProgramming.AsynchronousOperations

6 {

7 // Create a class that factors a number.

8 public class PrimeFactorFinder

9 {

10 public static bool Factorize(

11 int number,

12 ref int primefactor1,

13 ref int primefactor2)

14 {

15 primefactor1 = 1;

16 primefactor2 = number;

1718 // Factorize using a low−tech approach.

19 for (int i=2;i<number;i++)

20 {

21 if (0 == (number % i))

22 {

23 primefactor1 = i;

24 primefactor2 = number / i;

25 break;

26 }

27 }

28 if (1 == primefactor1 )

29 return false;

30 else

31 return true ;

32 }

33 }

3435 // Create an asynchronous delegate that matches the Factorize method.

36 public delegate bool AsyncFactorCaller (

37 int number,

38 ref int primefactor1,

39 ref int primefactor2);

4041 public class DemonstrateAsyncPattern

42 {

43 // The waiter object used to keep the main application thread

44 // from terminating before the callback method completes.

45 ManualResetEvent waiter;

4647 // De�ne the method that receives a callback when the results are available .

26 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 27: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.5 Delegaten

48 public void FactorizedResults(IAsyncResult result)

49 {

50 int factor1=0;

51 int factor2=0;

5253 // Extract the delegate from the

54 // System.Runtime.Remoting.Messaging.AsyncResult.

55 AsyncFactorCaller factorDelegate = (AsyncFactorCaller)((AsyncResult)result).AsyncDelegate;

56 int number = (int) result.AsyncState;

57 // Obtain the result .

58 bool answer = factorDelegate.EndInvoke(ref factor1, ref factor2, result);

59 // Output the results.

60 Console.WriteLine("On CallBack: Factors of {0} : {1} {2} - {3}",

61 number, factor1, factor2, answer);

62 waiter.Set();

63 }

6465 // The following method demonstrates the asynchronous pattern using a callback method.

66 public void FactorizeNumberUsingCallback()

67 {

68 AsyncFactorCaller factorDelegate = new AsyncFactorCaller (PrimeFactorFinder.Factorize);

69 int number = 1000589023;

70 int temp=0;

71 // Waiter will keep the main application thread from

72 // ending before the callback completes because

73 // the main thread blocks until the waiter is signaled

74 // in the callback .

75 waiter = new ManualResetEvent(false);

7677 // De�ne the AsyncCallback delegate.

78 AsyncCallback callBack = new AsyncCallback(this.FactorizedResults);

7980 // Asynchronously invoke the Factorize method.

81 IAsyncResult result = factorDelegate.BeginInvoke(

82 number,

83 ref temp,

84 ref temp,

85 callBack,

86 number);

8788 // Do some other useful work while

89 // waiting for the asynchronous operation to complete.

9091 // When no more work can be done, wait.

92 waiter.WaitOne();

93 }

9495 // The following method demonstrates the asynchronous pattern

96 // using a BeginInvoke, followed by waiting with a time−out.97 public void FactorizeNumberAndWait()

98 {

99 AsyncFactorCaller factorDelegate = new AsyncFactorCaller (PrimeFactorFinder.Factorize);

100101 int number = 1000589023;

102 int temp=0;

103104 // Asynchronously invoke the Factorize method.

105 IAsyncResult result = factorDelegate.BeginInvoke(

106 number,

107 ref temp,

108 ref temp,

109 null,

110 null);

111112 while (!result.IsCompleted)

113 {

114 // Do any work you can do before waiting.

115 result.AsyncWaitHandle.WaitOne(10000, false);

116 }

117 // The asynchronous operation has completed.

118 int factor1=0;

119 int factor2=0;

120121 // Obtain the result .

122 bool answer = factorDelegate.EndInvoke(ref factor1, ref factor2, result);

123

27 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 28: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.6 Attribute und Re�ektion

124 // Output the results.

125 Console.WriteLine("Sequential : Factors of {0} : {1} {2} - {3}",

126 number, factor1, factor2, answer);

127 }

128129 public static void Main()

130 {

131 DemonstrateAsyncPattern demonstrator = new DemonstrateAsyncPattern();

132 demonstrator.FactorizeNumberUsingCallback();

133 demonstrator.FactorizeNumberAndWait();

134 }

135 }

136 }

Delegate:Ein Delegate de�niert einen neuen Typ, der Referencen auf Methoden enthält.

Ereignisse nutzen Delegate, um Objekte über das Eintreten des Ereignisses zu informieren. Alle Abonnenten(Subscriber) des Ereignisses werden informiert.

Ein Callback5 de�niert eine Beziehung zwischen zwei Klassen. Ein Callback ist ein Pattern. Ein Callback wirdnicht in die Welt rauspossaunt, sondern bleibt für sich. Die Beziehung sorgt dafür, dass ein Objekt automatischauf ein anderes Objekt reagiert.

3.6 Attribute und Re�ektion

Abbildung 3.5: Re�ektion und Attribute

Attribute stellen eine e�ziente Methode dar, um Deklarationsinformationen mit C#-Code (Typen, Methoden,Eigenschaften usw.) zu verknüpfen. Sobald das Attribut einer Programmentität zugeordnet ist, kann es zurLaufzeit mithilfe eines Verfahrens abgefragt werden, das als Re�ektion bezeichnet wird.

3.6.1 Attribute

Attribute fügen dem Programm Metadaten hinzu. Metadaten sind Informationen zu den in einem Programmde�nierten Typen. Alle .NET-Assemblys enthalten einen angegebenen Satz von Metadaten, die die in der As-sembly de�nierten Typen und Typmember beschreiben. Sie können benutzerde�nierte Attribute hinzufügen,um bei Bedarf zusätzliche Informationen anzugeben.

5Der Begri� Callback (Rückruf) wird in verschiedene Programmiermodellen verwendet als Begri� für die Möglichkeit, dass aufgeru-fener Programmcode seinerseits einen Aufruf zu dem Aufrufer startet. Ein Callback ist eine asynchrone Operation im Gegensatzzu einem Rückgabewert auf einen Aufruf.

28 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 29: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.6 Attribute und Re�ektion

Sie können eigene benutzerde�nierte Attribute erstellen, indem Sie eine Attributklasse de�nieren. Dies ist eineKlasse, die direkt oder indirekt Elemente von Attribute ableitet, wodurch die schnelle und einfache Identi�kationvon Attributde�nitionen in Metadaten ermöglicht wird. Angenommen, Sie möchten Klassen und Strukturen mitdem Namen des Programmierers kennzeichnen, der sie geschrieben hat. Sie könnten beispielsweise eine benutzer-de�nierte Author-Attributklasse de�nieren: Der Klassenname entspricht dem Attributnamen, also Author. Daer von System.Attribute abgeleitet ist, handelt es sich um eine benutzerde�nierte Attributklasse. Die Parameterdes Konstruktors sind die positionellen Parameter des benutzerde�nierten Attributs (in diesem Fall name), undalle ö�entlichen Felder oder Eigenschaften, die Lese- und Schreibzugri� unterstützen, sind benannte Parameter(in diesem Fall ist version der einzige benannte Parameter). Beachten Sie, dass das AttributeUsage-Attributverwendet wird, um die Gültigkeit des Author-Attributs auf class- und struct-Deklarationen zu beschränken.

3.6.2 Re�ektion

Metadaten eines Moduls können zur Laufzeit ausgelesen und geändert werden Diesen Vorgang nennt manRe�ektion .NET Framework stellt entsprechende Klassen über den Namespace System.Re�ection bereit

6Bei der Re�ektion werden Objekte (vom Typ Type) bereitgestellt, die Assemblys, Module und Typen beschrei-ben. Mithilfe von Re�ektion können Instanzen von Typen dynamisch erzeugt, Typen an ein vorhandenes Objektgebunden und Typinformationen von vorhandenen Objekten abgefragt werden. Ebenso können die Methodenvorhandener Objekte aufgerufen werden, und es kann auf ihre Felder und Eigenschaften zugegri�en werden.Wenn Sie Attribute im Code verwenden, können Sie mithilfe von Re�ektion auf diese Attribute zugreifen.Re�ektion ist in folgenden Situationen nützlich:

• Wenn Sie auf Attribute in den Metadaten des Programms zugreifen müssen.

• Für das Überprüfen und das Instanziieren von Typen in einer Assembly.

• Für das Erstellen neuer Typen zur Laufzeit. Verwenden Sie die Klassen in System.Re�ection.Emit.

• Für das Ausführen von spätem Binden und für den Zugri� auf Methoden von zur Laufzeit erstelltenTypen.

3.6.3 Zugri� auf Attribute mit Re�ektion

Listing 3.23: Benutzerde�niertes Attribute und Re�ektion

//De�nition

[System.AttributeUsage(System.AttributeTargets.Class |

System.AttributeTargets.Struct,

AllowMultiple = true) // multiuse attribute

]

public class Author : System.Attribute

{

string name;

public double version;

public Author(string name)

{

this.name = name;

version = 1.0; // Default value

}

public string GetName()

{

return name;

}

}

[Author("H. Ackerman")]

private class FirstClass

{

// ...

}

// No Author attribute

private class SecondClass

6Re�ektion

29 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 30: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.7 Lambda-Ausdrücke

{

// ...

}

[Author("H. Ackerman"), Author("M. Knott", version = 2.0)]

private class ThirdClass

{

// ...

}

class TestAuthorAttribute

{

static void Main()

{

PrintAuthorInfo(typeof(FirstClass));

PrintAuthorInfo(typeof(SecondClass));

PrintAuthorInfo(typeof(ThirdClass));

}

private static void PrintAuthorInfo(System.Type t)

{

System.Console.WriteLine("Author information for {0}", t);

System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t); // re�ection

foreach (System.Attribute attr in attrs)

{

if (attr is Author)

{

Author a = (Author)attr;

System.Console.WriteLine(" {0}, version {1:f}", a.GetName(), a.version);

}

}

}

}

Aufgabe 3.7 Welche Ausgabe erzeugt das Programm?

3.7 Lambda-Ausdrücke

Bei einem Lambda-Ausdruck handelt es sich um eine anonyme Funktion, die Ausdrücke und Anweisungenenthalten und für die Erstellung von Delegaten oder Ausdrucksbaumstrukturen verwendet werden kann.

Alle Lambda-Ausdrücke verwenden den Operator Lambda =>, der so viel bedeutet wie wechselt zu. Auf derlinken Seite des Operators Lambda werden die Eingabeparameter angegeben (falls vorhanden), und auf derrechten Seite be�ndet sich der Ausdruck oder Anweisungsblock. Der Lambda-Ausdruck x => x * x bedeutet xwechselt x Mal zu x. Dieser Ausdruck kann wie folgt einem Delegattyp zugewiesen werden:

Listing 3.24: Ausdruckslambdas

(input parameters) => expression

//Beispiele

(x, y) => x == y

n => n<5

Listing 3.25: Anweisungslambdas

(input parameters) => {statement;}

//Beispiel :

n => { string s = n + " " + "World"; Console.WriteLine(s); }

3.8 IDisposable

IDisposable de�niert eine Methode zur Freigabe von reservierten Ressourcen. Über diese Schnittstelle werdenhauptsächlich nicht verwaltete Ressourcen freigegeben. Der Garbage Collector gibt automatisch den für ein

30 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 31: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.8 IDisposable

verwaltetes Objekt reservierten Speicher frei, wenn dieses Objekt nicht mehr verwendet wird. Es kann allerdingsnicht vorausgesagt werden, wann die Garbage Collection statt�ndet. Darüber hinaus hat der Garbage Collectorkeine Kenntnis von nicht verwalteten Ressourcen wie Fensterhandles oder o�enen Dateien und Streams.

Mit der Dispose-Methode dieser Schnittstelle können nicht verwaltete Ressourcen in Verbindung mit dem Gar-bage Collector explizit freigegeben werden. Der Consumer eines Objekts kann diese Methode aufrufen, wenn dasObjekt nicht mehr benötigt wird. Verwenden Sie für den Aufruf einer Klasse, die die IDisposable-Schnittstelleimplementiert, einen try-�nally-Block, um sicherzustellen, dass nicht verwaltete Ressourcen auch dann freige-geben werden, wenn die Anwendung aufgrund einer Ausnahme beendet wird.

Das Muster zum Verwerfen eines Objekts, Dispose-Muster genannt, legt eine Ordnung für die Lebensdauer vonObjekten fest.

Die Dispose-Methode eines Typs sollte alle Ressourcen freigeben, die dieser besitzt. Sie sollte auÿerdem alle Res-sourcen freigeben, deren Eigentümer die Basistypen der Methode sind, indem die Dispose-Methode des überge-ordneten Typs aufgerufen wird. Die Dispose-Methode des übergeordneten Typs sollte alle Ressourcen freigeben,die dieser besitzt, und dann die Dispose-Methode seines übergeordneten Typs aufrufen. Dieses Muster wirddurch die Hierarchie der Basistypen weitergegeben. Um sicherzustellen, dass Ressourcen immer entsprechendbereinigt werden, sollte eine Dispose-Methode auch mehrmals aufgerufen werden können, ohne eine Ausnahmeauszulösen.

Durch das Implementieren der Dispose-Methode für Typen, die nur verwaltete Ressourcen (z. B. Arrays) ver-wenden, werden keine Leistungsvorteile erzielt, da diese automatisch vom Garbage Collector freigegeben werden.Verwenden Sie die Dispose-Methode primär für verwaltete Objekte, die systemeigene Ressourcen verwenden, undfür COM-Objekte, die für .NET Framework verfügbar sind. Verwaltete Objekte, die systemeigene Ressourcen(z. B. die FileStream-Klasse) verwenden, implementieren die IDisposable-Schnittstelle.

Eine Dispose-Methode sollte die SuppressFinalize-Methode für das Objekt aufrufen, das es freigibt. Wenn sichdas Objekt gegenwärtig in der Finalisierungswarteschlange be�ndet, verhindert SuppressFinalize, dass dessenFinalize-Methode aufgerufen wird. Beachten Sie, dass das Ausführen einer Finalize-Methode hohen Leistungs-aufwand erfordert. Wenn Sie das Objekt bereits mit der Dispose-Methode bereinigt haben, muss die Finalize-Methode des Objekts nicht mehr vom Garbage Collector aufgerufen werden.

Im folgenden Codebeispiel wird das empfohlene Entwurfsmuster für das Implementieren einer Dispose-Methodefür Klassen veranschaulicht, die nicht verwaltete Ressourcen kapseln.

Ressourcenklassen werden i. d.R. von komplexen systemeigenen Klassen oder APIs abgeleitet und müssen ent-sprechend angepasst werden. Verwenden Sie dieses Codemuster als Anfangspunkt zum Erstellen einer Ressour-cenklasse, und stellen Sie die erforderliche Anpassung basierend auf den Ressourcen, die Sie kapseln, bereit.

Listing 3.26: Dispose-Pattern

//Klasse, die IDisposable implementiert.

public class DisposableResource : IDisposable

{

private Stream resource;

private bool disposed;

// The stream passed to the constructor

// must be readable and not null.

public DisposableResource(Stream stream)

{

if (stream == null)

throw new ArgumentNullException("Stream in null.");

if (!stream.CanRead)

throw new ArgumentException("Stream must be readable.");

resource = stream;

disposed = false;

}

// Demonstrates using the resource.

// It must not be already disposed.

public void DoSomethingWithResource() {

if (disposed)

throw new ObjectDisposedException("Resource was disposed.");

// Show the number of bytes.

31 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 32: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.9 using

int numBytes = (int) resource.Length;

Console.WriteLine("Number of bytes: {0}", numBytes.ToString());

}

public void Dispose()

{

Dispose(true);

// Use SupressFinalize in case a subclass

// of this type implements a �nalizer .

GC.SuppressFinalize(this);

}

protected virtual void Dispose(bool disposing)

{

// If you need thread safety, use a lock around these

// operations, as well as in your methods that use the resource.

if (!disposed)

{

if (disposing) {

if (resource != null)

resource.Dispose();

Console.WriteLine("Object disposed.");

}

// Indicate that the instance has been disposed.

resource = null;

disposed = true;

}

}

}

//Aufrufende Klasse

class Program

{

static void Main()

{

try

{

// Initialize a Stream resource to pass

// to the DisposableResource class.

Console.Write("Enter filename and its path: ");

string fileSpec = Console.ReadLine();

FileStream fs = File.OpenRead(fileSpec);

DisposableResource TestObj = new DisposableResource(fs);

// Use the resource.

TestObj.DoSomethingWithResource();

// Dispose the resource.

TestObj.Dispose();

}

catch (FileNotFoundException e)

{

Console.WriteLine(e.Message);

}

}

}

3.9 using

using 7stellt eine intuitive Syntax bereit, die die richtige Verwendung von IDisposable-Objekten sicherstellt.Wenn Sie ein IDisposable-Objekt verwenden, sollten Sie es grundsätzlich deklarieren und in einer using-Anweisung instanziieren. Die using-Anweisung ruft die Dispose-Methode für das Objekt auf die richtige Weiseauf und (bei Verwendung wie oben gezeigt) verursacht, dass das Objekt seinen Gültigkeitsbereich verlässt,

7using-Anweisung

32 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 33: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.10 Serialisierung

sobald Dispose aufgerufen wird. Innerhalb des using-Blocks ist das Objekt schreibgeschützt und kann nichtgeändert oder neu zugewiesen werden.

Mit der using-Anweisung wird sichergestellt, dass Dispose auch aufgerufen wird, wenn ein Ausnahmefehler auf-tritt, während Sie Methoden für das Objekt aufrufen. Sie erzielen dasselbe Ergebnis, indem Sie das Objekt ineinen try-Block einfügen und Dispose in einem �nally-Block aufrufen. Auf diese Weise wird die using-Anweisungvom Compiler übersetzt. Der Code im oben gezeigten Beispiel wird bei der Kompilierung auf folgenden Code er-weitert (beachten Sie die zusätzlichen geschweiften Klammern zur Erstellung des begrenzten Gültigkeitsbereichsfür das Objekt):

Listing 3.27: usingusing (System.IO.StreamReader sr = new System.IO.StreamReader(@"C:\Users\Public\Documents\test.txt"))

{

string s = null;

while((s = sr.ReadLine()) != null)

{

Console.WriteLine(s);

}

}

Aufgabe 3.8 Fügen Sie in Listing 3.27 das Werfen einer Ausnahme innerhalb des using-Blocks ein. Waspassiert? Schauen Sie sich den Ablauf im Debugger an.

3.10 Serialisierung

8Serialisierung beschreibt den Vorgang des Konvertierens eines Objekts in eine Form, die problemlos transpor-tiert werden kann. So können Sie beispielsweise ein Objekt serialisieren und es über das Internet per HTTPzwischen einem Client und einem Server transportieren. Am anderen Ende wird das Objekt durch Deserialisie-rung aus dem Stream wiederhergestellt.

Bei der XML-Serialisierung werden nur die ö�entlichen Felder und Eigenschaftenwerte eines Objekts in einenXML-Stream serialisiert. Typinformationen werden bei der XML-Serialisierung nicht berücksichtigt. Wenn Siez. B. über ein Book-Objekt im Library-Namespace verfügen, wird es nicht in jedem Fall in ein Objekt desselbenTyps deserialisiert.

Wenn Sie ein Objekt serialisieren möchten, erstellen Sie zuerst das zu serialisierende Objekt, und legen Sie danndessen ö�entliche Eigenschaften und Felder fest. Dazu müssen Sie das Transportformat angeben, in dem derXML-Stream gespeichert werden soll: als ein Stream oder als eine Datei. Wenn der XML-Stream beispielsweisein einer permanenten Form gespeichert werden muss, erstellen Sie ein FileStream-Objekt.

Listing 3.28: Attribut Serializable//Zustand ablegen

MySerializableClass myObject = new MySerializableClass();

// Insert code to set properties and �elds of the object.

XmlSerializer mySerializer = new

XmlSerializer(typeof(MySerializableClass));

// To write to a �le , create a StreamWriter object.

StreamWriter myWriter = new StreamWriter("myFileName.xml");

mySerializer.Serialize(myWriter, myObject);

myWriter.Close();

//Zustand wieder herstellen

MySerializableClass myObject;

// Construct an instance of the XmlSerializer with the type

// of object that is being deserialized .

XmlSerializer mySerializer =

new XmlSerializer(typeof(MySerializableClass));

// To read the �le , create a FileStream.

FileStream myFileStream =

new FileStream("myFileName.xml", FileMode.Open);

// Call the Deserialize method and cast to the object type.

myObject = (MySerializableClass)

mySerializer.Deserialize(myFileStream)

Weitere Beispiele �nden Sie unter Beispiele für die XML-Serialisierung.8Einführung in die XML-Serialisierung

33 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 34: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.11 XML-Dokumentationskommentare

3.11 XML-Dokumentationskommentare

In Visual C# lässt sich eine Dokumentation des Codes erstellen. Dazu werden XML-Tags in spezielle Kom-mentarfelder des Quellcodes unmittelbar vor dem Codeblock eingefügt, auf den sie sich beziehen. Wenn Sie mit/doc kompilieren, sucht der Compiler alle XML-Tags im Quellcode und erstellt eine XML-Dokumentationsdatei.XML-Dokumentkommentare sind keine Metadaten und sind nicht in der kompilierten Assembly enthalten. Folg-lich ist der Zugri� auf sie über Re�ektion nicht möglich. Wenn Sie in der IDE /// vor einem Codeblock eingeben,erzeugt Ihnen VS automatisch einen passenden Kommentar. Möchten Sie zusammenhängende Methoden, z.B.ctor (Konstruktoren), private Methoden o.ä. einfach ein- und ausklappen können, so verwenden Sie #regionctor . . .#endregion.

3.12 Namenskonventionen

Nach dem Naming Guideline von Microsoft sollen Parameter, Klassen etc. wie folgt benannt werden, Basis istPascalCase und camelCase:

• Nur Parameter und private Members sind camelCase

• Namespace:CompanyName.TechnologyName[.Feature][.Design], Beispiel: Microsoft.Media.Design

• Klassen: Nomen, Beispiel: class FileStream

• Methoden: Verben, Beispiel: RemoveAll()

• Interfaces: Nomen oder Nomenausdrücke, Beispiel: IServiceProvider, IFormatable

Diese Namensrichtlinien können Sie mit StyleCop validieren. Auf Assembly-Ebene analysiert das WerkzeugFxCop vorwiegend Designkonventionen für Entwicklerinnen von Klassenbibliotheken (Design Guidelines forClass Library Developers), aber auch einige Namenskonventionen.

Sie können natürlich auch einer anderen Richtlinie wie Zend für PHP folgen. Machen Sie dies in der Dokumen-tation Ihres Codes deutlich.

3.13 Aufgaben

Die Aufgaben stammen von .Net AT JKU.

Aufgabe 3.9 Was sind die Vorteile eines Enumerationstyps (z.B. enum Color red, blue, green) gegenüber int-Konstanten (z.B. const int red = 0, blue = 1, green = 2;)?

Aufgabe 3.10 Studieren Sie die Klasse Enum aus der Online-Dokumentation des .NET-SDK und schreibenSie zwei Methoden zur Ein- und Ausgabe von Werten eines von Ihnen gewählten Enumerationstyps in textuellerForm. Können Sie die Methoden so allgemein halten, dass Sie damit Werte eines beliebigen Enumerationstypslesen und schreiben können?

Aufgabe 3.11 Schreiben Sie eine Methode double[,] MatrixMult(double[,] a, double[,] b) die zwei Matrizen aund b multipliziert und das Ergebnis als Funktionswert liefert. Wenn sich a und b auf Grund ihrer Dimensionennicht multiplizieren lassen, soll die Methode eine Ausnahme liefern. allgemein halten, dass Sie damit Werteeines beliebigen Enumerationstyps lesen und schreiben können?

Aufgabe 3.12 Schreiben Sie eine Methode, die aus einem Pfadnamen (z.B. c:dotnetsamplesmyFile.cs) den Dateinamen ohne Verzeichnisse und Dateiendung herausschält (z.B. myFile).

Aufgabe 3.13 Schreiben Sie eine switch-Anweisung, die zu einem gegebenen Monat die Anzahl seiner Tageberechnet (ohne Schaltjahre zu berücksichtigen). Implementieren Sie eine Version, bei der das Monat durch eineZahl gegeben ist und eine andere, bei der es durch einen String gegeben ist. Vergleichen Sie den erzeugten Codemittels ildasm.

34 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 35: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.13 Aufgaben

Aufgabe 3.14 Erzeugen Sie eine Tabelle der Quadrate und Wurzeln der ersten n natürlichen Zahlen. Verwen-den Sie dazu formatierte Ausgabe. Zur Berechnung der Wurzel können Sie die Funktion Math.Sqrt verwenden.Übergeben Sie n als Kommandozeilenparameter.

Aufgabe 3.15 Für welche Aufgaben würden Sie Klassen verwenden, für welche Structs? Nennen Sie Beispiele.

Aufgabe 3.16 Implementieren Sie einen Struct Complex zum Rechnen mit komplexen Zahlen. Sehen Sie ge-eignete Konstruktoren vor sowie Properties, um auf dem reellen und imaginären Teil einer komplexen Zahlzugreifen zu können. Implementieren Sie mittels Operator Overloading die Addition, Multiplikation und denVergleich komplexer Zahlen. Sehen Sie auch einen impliziten und einen expliziten Konversionsoperator vor, mitdem man zwischen komplexen Zahlen und double konvertieren kann. Überschreiben Sie schlieÿlich auch die vonObject geerbten Methoden Equals, ToString und GetHashCode.

Aufgabe 3.17 Implementieren Sie eine Klasse GrowableArray, die ein dynamisch wachsendes Array beliebigerObjekte darstellt. Ein Indexer soll den Zugri� auf die einzelnen Elemente erlauben. Wird über einen Indexzugegri�en, der gröÿer als die bisherige Arraylänge ist, so soll das Array dynamisch erweitert, d.h. durch einlängeres ersetzt werden, wobei die Werte des alten Arrays den Anfang des neuen Arrays bilden sollen.

Aufgabe 3.18 Erweitern Sie Ihre Klasse GrowableArray so, dass die Elemente im Array mittels einer foreach-Schleife durchlaufen werden können. Dazu muss GrowableArray das Interface IEnumerable implementieren.Studieren Sie dieses Interface in der Online-Dokumentation des .NET-SDK.

Aufgabe 3.19 Schreiben Sie eine Klasse In mit Methoden zum Lesen von Zahlen, Wörtern, Strings, Zeichenund Booleschen Werten von der Tastatur. Bauen Sie dabei auf die Klasse Console auf.

Aufgabe 3.20 Erweitern Sie Ihre Klasse In so, dass man mit ihr wahlweise von der Tastatur, von einer Dateioder von einer Zeichenkette lesen kann.

Aufgabe 3.21 Nehmen Sie an, man möchte wissen, wieviele Objekte einer bestimmten Klasse C im Laufe einesProgramms erzeugt wurden. Implementieren Sie eine solche Klasse und versehen Sie sie mit einer statischenZählervariablen sowie mit einer statischen Methode, mit der die Zählervariable zurückgesetzt werden kann. Womuss die Zählervariable erhöht werden?

Aufgabe 3.22 Implementieren Sie ein Struct Time zur Verwaltung von Uhrzeiten. Intern sollen Uhrzeiten inSekunden gespeichert werden. Es soll aber drei Properties geben, die den Stunden-, Minuten-, und Sekundenanteileiner Uhrzeit liefern. Sehen Sie auch überladene Operatoren und Konstruktoren vor, um mit Uhrzeiten bequemrechnen zu können.

Aufgabe 3.23 Diskutieren Sie Vor- und Nachteile von Ref-Parametern, Out-Parametern und Parametern, dieals Funktionsergebnisse zurückgegeben werden.

Aufgabe 3.24 Implementieren Sie eine Methode Clear, mit der eine beliebige Anzahl von Elementen einesbeliebigen int-Arrays auf 0 gesetzt werden kann (z.B. Clear(arr, 3, 4, 9, 12);).

Aufgabe 3.25 Was ist der Unterschied zwischen einer abstrakten Klasse und einem Interface?

Aufgabe 3.26 Gegeben sei ein Array von Objekten der gleichen Art (z.B. Strings, Bruchzahlen oder sonstigeObjekte). Schreiben Sie eine Methode Sort(arr, compare), die das Array arr sortiert und dazu eine Vergleichsme-thode compare(x, y) verwendet, die als Delegate übergeben wird und zwei Objekte x und y des Arrays miteinandervergleicht, d.h. feststellt, ob x gröÿer, kleiner oder gleich y ist.

Aufgabe 3.27 Implementieren Sie eine Zählerklasse Counter mit einer Methode Add(x) zum Erhöhen desZählerwerts und einer Methode Clear() zum Löschen des Zählerwerts. Wenn der Zählerwert eine bestimmte Be-dingung erfüllt (z.B. Zählerwert > n) sollen eine oder mehrere Aktionen ausgelöst werden. Sowohl die Bedingungals auch die Aktionen sollen als Delegates an die Zählerklasse übergeben werden können.

Aufgabe 3.28 Schreiben Sie eine Methode static void Plot (Function f) ... der man eine Funktion eineDelegate-Typs delegate double Function(double x); übergeben kann und die ein zweidimensionales Diagrammdieser Funktion zeichnet. Verwenden Sie der Einfachheit halber als �Gra�k� eine zweidimensionale char-Matrix.

35 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 36: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

3.13 Aufgaben

Aufgabe 3.29 Die Methode Convert.ToInt32(s) konvertiert eine im String s gespeicherte Zahlenfolge in eineint-Zahl. Dabei können verschiedene Ausnahmen auftreten, wenn s nicht das richtige Format hat oder keinengültigen Wert enthält. Studieren Sie die möglichen Ausnahmen in der .NET-SDK-Dokumentation und schreibenSie ein Programm, in dem diese Ausnahmen durch eine try-catch-Anweisung abgefangen werden.

Aufgabe 3.30 Implementieren Sie eine sortierte verkettete Liste mit den Operationen Add(x) und Remove(x)sowie mit einem Property Count, das die Anzahl der Elemente der Liste liefert. Auÿerdem soll es einen Indexergeben, mit dem man auf jedes Listenelement zugreifen kann. Der Typ der Listenelemente soll beliebig sein.

Aufgabe 3.31 Schreiben Sie einen generischen binären Suchbaum BinaryTree<K, V>, der Werte vom Typ Vunter ihrem Schlüssel vom Typ K speichert. Sehen Sie eine Methode Insert vor, mit der man ein Schlüssel/Wert-Paar in den Baum einfügen kann, sowie eine Methode Contains, die prüft, ob ein bestimmter Schlüssel im Baumenthalten ist. Schreiben Sie auch einen Indexer, der den Wert zu einem gegebenen Schlüssel liefert oder eineAusnahme auslöst, wenn der Schlüssel nicht gefunden wurde.

Aufgabe 3.32 Verwenden Sie den in der Online-Dokumentation von .NET beschriebenen Typ List<T>, umeine Liste von Wortlisten anzulegen. Lesen Sie einen Text aus mehreren Zeilen, wobei jede Zeile aus mehre-ren Wörtern (Buchstabenfolgen) besteht. Alle Wörter einer Zeile sollen in einer Liste vom Typ List<string>gespeichert werden. Bilden Sie dann aus den Wortlisten der einzelnen Zeilen wiederum eine Liste vom TypList<List<string�.

Aufgabe 3.33 Schreiben Sie eine generische Methode list = Copy(array), die ein Array beliebigen Elementtypsin eine generische Liste gleichen Elementtyps kopiert. Verwenden Sie als Listentyp List<T> (siehe Online-Dokumentation von .NET).

Aufgabe 3.34 Implementieren Sie eine Klasse zur Verwaltung einer verketteten Liste von Namen (Strings).Sehen Sie folgende Iteratoren vor:

• Eine allgemeine Iterator-Methode, die alle in der Liste gespeicherten Namen liefert.

• Eine spezi�sche Iterator-Methode, die nur jene Namen der Liste liefert, die mit einem bestimmten Stringbeginnen. Der String soll als Parameter mitgegeben werden.

• Ein Iterator-Property, das nur jene Namen der Liste liefert, die länger als 5 Buchstaben sind.

Aufgabe 3.35 Versehen Sie eine von Ihnen implementierte Klasse mit XML-Kommentaren und sehen Sie sichan, was der Compiler daraus erzeugt, wenn er mit der Option /doc:myFile.xml aufgerufen wird.

36 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 37: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4 Komponenten

1In der Softwarebranche wird mit dem Begri� Komponente ein wiederverwendbares Objekt bezeichnet, das fürClients auf standardisierte Art eine oder mehrere Schnittstellen verfügbar macht. Zu den am häu�gsten ver-wendeten Komponenten im Bereich der .NET Framework-Programmierung zählen die visuellen Steuerelemente,die Windows Forms hinzugefügt werden. Wird eine Komponente in C# erstellt, kann diese auch von Clientsverwendet werden, die in einer anderen Sprache geschrieben sind, die der CLS (Common Language Speci�ca-tion) entspricht � dabei ist hervorzuheben, dass die Komponenten sich durch ihre Attribute selbst beschreibtund somit keine globale Registrierung zwingend erforderlich ist. Die Anwendung, die eine solche Komponentenutzen möchte, muss lediglich das zugehörige Assembly einbinden.

2Während der Begri� Komponente viele Bedeutungen hat, ist im .NET Framework eine Komponente eineKlasse, die das Interface IComponent implementiert (oder von einer solchen Klasse abgeleitet ist). Ohne dieseengere De�nition könnte man jedes Assembly als Komponente bezeichnen. Wobei, wenn man schaut, welcheKlassen IComponent implementieren, so ist man nicht weit davon entfernt . . . .

• System.ComponentModel.Component: Basisimplemantation von IComponent � typische Basisklasse fürnicht-visuelle Komponenten

• System.Web.UI.Control: Basisklasse für alle ASP.NET-Controls, implementiert IComponent direkt

• System.Windows.Forms.Control: erbt von System.ComponentModel.Component und ist die Basisklassefür alle WinForm-Controls

• System.Data.DataSet: erbt von System.ComponentModel.MarshalByValueComponent � implementiertIComponent.

• System.Diagnostics.EventLog: erbt von System.ComponentModel.Component.

Abbildung 4.1: IComponent Quelle: Leveraging .NET Components

Dies liegt vermutlich daran, dass IComponent nicht viel vorgibt:

• IComponent implementiert IDisposable

• public property: ISiteSite{get; set; } mit

� ISite erbt von IServiceProvider:

∗ ObjectGetService(TypeserviceType) � Ruft das Dienstobjekt des angegebenen Typs ab.

� IComponentComponent{get; } � Ruft bei der Implementierung durch eine Klasse die der ISite zu-geordnete Komponente ab.

� IContainerContainer{get; } � Ruft bei der Implementierung durch eine Klasse den der ISite zuge-ordneten Container ab. IContainer stellt Add, Dispose, Remove zur Verfügung.

� boolDesignMode{get; } � Bestimmt bei der Implementierung durch eine Klasse, ob sich die Kompo-nente im Entwurfsmodus be�ndet.

1Erstellen von Komponenten2Programmieren mit Komponenten

37

Page 38: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4 Komponenten

� stringName{get; set; } � Ruft bei der Implementierung durch eine Klasse den Namen der Kompo-nente ab, die ISite zugeordnet ist, oder legt diesen fest.

• public event: eventEventHandlerDisposed;

• public method, von IDisposable: voidDispose()

Komponente:Jede Komponente in .Net implementiert das Interface IComponent.

Unter einem Control versteht Microsoft eine UI-Komponente, diese werden von eine der zwei Klassen Sys-tem.Windows.Forms.Control oder System.Web.UI.Control abgeleitet. Abbildung 4.2 zeigt die zugehörigen In-terfaces und die Objekthierarchie Container3.

Abbildung 4.2: Control

Control vs Komponente:Jedes Control ist eine Komponente, aber nicht jede Komponente ist ein Control.

Das Thema sinnvoll abzugrenzen und zu strukturieren ist schwierig. Aufgrund der historischen Entwicklunggibt es viele Begri�e und Technologien: COM, ActiveX, Add-In, Excel-Addin, Plugin, ...

COM Das Component Object Model (COM) ist Microsofts Ansatz für objektorientierte Softwarekomponenten.Das .NET Framework sollte ursprünglich COM 3.0 heiÿen. COM-Komponenten müssen in die Registryeingetragen werden. COM basiert auf dem Client/Server-Prinzip. COM läuft nur unter Windows.

DCOM Distributed COM, Komponentenkommunikation über Prozess- und Maschinengrenzen hinweg möglich.

ActiveX ActiveX ist ein Dienst auf Basis von COM und DCOM.

ActiveX-Control Früher wurden diese Steuerelemente auch OLE-Steuerelemente bzw. OCXes (OLE CustomControls) genannt.

Es existieren viele COM-Komponenten, die in .Net genutzt werden sollen/müssen. Hier tri�t man auf COM-Interop. Bis Sie mit dem Studium fertig sind, existiert dieses Problem ho�entlich nicht mehr ;-) An dieserStelle sei jedoch erwähnt, Microsoft bietet eine Brücke zwischen .Net und COM an und die Abbildung vonParametertypen in COM nach .Net ist ein Problem.

Wann immer man eine Applikation mit fremden Komponenten zur Laufzeit erweitert, steht man vor folgendenProblemen:

• Discovery: Komponente au�nden

• Laden und Entladen (freigeben)

• Absichern der Host-Anwendung

3Custom Design-time Control

38 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 39: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4.1 Eigene GUI Steuerelemente

� gegen Fehler in der Komponente

� deren Daten und Prozesse

• Versionierung

.Net bietet hierfür zwei Frameworks, wie man eigene Applikationen für fremde Komponenten zur Laufzeit ö�nenkann. Microsoft verfolgt diese Ansätze selbst in den O�ce-Produkten und in Visual Studio. Beide Frameworkskönnen auch kombiniert werden � dies geschieht z.B. in VS2010. Doch schauen wir uns zunächst die visuellenKomponenten an, die zur Designzeit in die Applikation eingebunden werden.

4.1 Eigene GUI Steuerelemente

4Wenn Sie ein neues Steuerelement entwerfen, müssen Sie zuerst festlegen, welche programmiertechnischenMittel dazu eingesetzt werden sollen. Unter .NET gibt es drei unterschiedliche Szenarien:

• Sie erweitern ein existierendes Steuerelement durch Ableitung um spezi�sche Funktionalitäten. Ähnlich,wie Sie eine benutzerde�nierte Form erstellen, die aus Form abgeleitet ist, können Sie auch ein vorhandenesSteuerelement ableiten, um daraus eine eigene Klasse zu erstellen. Sie brauchen die abgeleitete Klasse nurum die Fähigkeiten zu erweitern, die Sie von Ihrem Steuerelement erwarten.

• Sie kombinieren mehrere existierende Steuerelemente zu einem neuen. Dazu stellt die Klasse UserControleinen Container bereit, der einem Panel ähnelt. Diesem Container lassen sich mehrere Steuerelementeaus der Toolbox hinzufügen, die in Kombination das neue Steuerelement bilden. Die Eigenschaften undMethoden der im UserControl enthaltenen Steuerelemente können o�engelegt oder ausgeblendet werden.UserControl ist ein Steuerelement und von Control abgeleitet. Es verö�entlicht daher seinerseits bereitsalle von der Basis geerbten Member.

• Die beiden vorgenannten Entwicklungsmöglichkeiten basierten auf vorhandenen Steuerelementen. WollenSie ein vollkommen neues Steuerelement entwickeln, müssen Sie die Klasse Control ableiten. Das bedeutetaber auch, dass Sie die gra�sche Darstellung des Steuerelements selbst in die Hand nehmen müssen,da es dafür keine Basiskomponenten gibt. Wenn Sie bedenken, dass allein schon die Aktivierung einesSteuerelements eine Änderung der Darstellung zur Laufzeit bewirkt, können Sie sich vermutlich vorstellen,dass der Aufwand beträchtlich ist.

Für welche Alternative Sie sich entscheiden, hängt letztendlich von den Anforderungen an das neue Steuerele-ment ab. Wann immer es aber möglich ist, sollten Sie auf existierende und damit letztendlich auch auf bewährteKomponenten zurückgreifen. Sie sparen damit nicht nur Entwicklungszeit, sondern auch die Zeit für ausgiebigeTests.

4.2 Managed Extensibility Framework (MEF)

5MEF ist eine Bibliothek die das Problem der Erweiterbarkeit zur Laufzeit (Runtime Extinsibility) löst. Sie ver-einfacht die Implementierung von Erweiterbaren Anwendungen und bietet Ermittlung von Typen, Erzeugungvon Instanzen und Composition Fähigkeiten an. Die wichtigste Module im Core der Bibliothek sind Catalogund CompositionContainer. Das Catalog kontrolliert das Laden von Komponenten, während das Composition-Container die Instanzen erzeugt und diese an die entsprechenden Variablen bindet. Parts sind die Objekte dievom Type Export oder Import sein können. Die Exports sind im beschriebenen Beispiel die Komponenten,die geladen und instanziiert werden sollen. Die Imports sind die Variablen an die die Instanzen von der Kom-ponenten gebunden werden sollen. Zum Beispiel wäre die bereits erwähnte Komponente Component1 in derMEF-Anwendung ein Export. Die Variable proxy vom Type IComponent die die Instanz dieser Komponenteenthalten soll, wäre ein Import. MEF gibt es erst mit .Net 4.0, also der aktuellen Version. Hiermit soll es leichterwerden, Applikationen zu erweitern. Leichter bringt leider auch Nachteile mit sich, wie z.B.

• Es wird keine Isolation in AppDomains unterstützt.

• Es wird kein dediziertes Berechtigungssystem unterstützt.

• Es wird keine Versionierung unterstüzt.

4Steuerelemente entwickeln5Managed Extensibility Framework

39 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 40: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4.3 Managed Add-in Framework (MAF)

Abbildung 4.3: MEF

Whitepaper und weitere Informationen zu diesem Framework �nden Sie unter Welcome to the MEF communitysite.

4.3 Managed Add-in Framework (MAF)

MAF gibt es seit .Net 3.5, also schon länger als MEF. MAF unterstützt Versionierung und verschiedene App-Domains, ist jedoch, wie wir gleich sehen werden, deutlich komplexer in der Architektur.

Microsoft liefert eine Vielzahl von Produkten für sehr unterschiedliche Benutzergruppen. Individualisierte Lösun-gen werden anderen Unternehmen überlassen. So gibt es viele Unternehmen auf dem Markt, die O�ce-Produktefür die Bedürfnisse ihrer Kunden anpassen und Komponente entwickeln, um O�ce mächtiger zu machen. Fürdie Kunden bedeutet dies, dass sie sich nicht in eine weitere Umgebung einarbeiten müssen, sondern in ihrem ge-liebten Excel oder Word oder . . . bleiben können. MIS, jetzt Infor, entwickelte beispielsweise einen OLAP-Serverund erlaubte durch ein Add-In Controllern Zugri� auf diesen Server. Controller können so, ähnlich wie sie esvon den Pivot-Tabellen in Standard-Excel kennen, auf multidimensionalen Daten navigieren, diese analysierenund für folgende Jahre eine Planung simulieren und schlieÿlich auf den Server schreiben. Dass dies nicht in VBAgeht, ist o�ensichtlich. Lesende oder schreibende Zugri�e auf Millionen von Daten wird üblicherweise mit C++implementiert. An der Ober�äche be�ndet sich ein Add-In für Excel. Mit VS2010 können diese Add-Ins nochleichter implementiert werden als noch vor wenigen Jahren, denn das Objektmodell steht bereits Out-of-the-boxzur Verfügung. Mit einem Add-In kann also zusätzliche Funktionalität für ein O�ce-Produkt, aber auch fürVisual Studio oder jedes andere für Add-Ins geö�nete Produkt, implementiert werden. Dieses Add-In kann aufDaten fremder Systeme, wie Webservices, SAP-Connectors, MIS-Alea oder andere zugreifen.

Selbst auf dem Server kann über das Objektmodell auf Dokumente zugegri�en und diese verändert werden,ohne dass O�ce auf dem Server installiert sein muss. Grundlage hierfür ist das o�ene XML-Format, in demO�ce mittlerweile seine Dokumente ablegt.

40 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 41: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4.3 Managed Add-in Framework (MAF)

Abbildung 4.4: Listen

4.3.1 Laden eines Add-Ins

6Die folgenden Schritte ausgeführt wenn ein Benutzer ein Dokument ö�net, die Teil einer Microsoft O�ce-Lösungist:

1. Die Microsoft O�ce-Anwendung überprüft die benutzerde�nierten Dokumenteigenschaften, ob verwalteteCodeerweiterungen, die dem Dokument zugeordnet sind. Wenn es verwaltete Codeerweiterungen gibt, lädtdie Anwendung VSTOEE.dll, die VSTOLoader.dll lädt. Diese sind nicht verwaltete DLLs, die die Loader-Komponenten für Visual Studio 2010 Tools for O�ce Runtime sind. VSTOLoader.dll .NET Frameworklädt und startet den verwalteten Hostbereich Visual Studio Tools for O�ce runtime.

2. Wenn das Dokument von einem anderen Speicherort als dem lokalen Computer geö�net wird, überprüft derVisual Studio Tools for O�ce runtime , dass der Speicherort des Dokuments in der Liste vertrauenswürdigeSpeicherorteVertrauensstellungscenter für die bestimmten O�ce-Anwendung ist. Wenn der Speicherort desDokuments nicht in einem vertrauenswürdigen Speicherort ist, die Anpassung ist nicht vertrauenswürdigund der Ladevorgang beendet hier.

3. Visual Studio Tools for O�ce runtime installiert die Lösung, wenn wurde noch nicht installiert, die neues-ten Anwendungs- und Bereitstellung Manifeste lädt und eine Reihe von Sicherheitsüberprüfungen führt.

4. Wenn die Anpassung auszuführenden vertrauenswürdig ist, verwendet Visual Studio Tools for O�ce runti-me die Bereitstellung-Manifest und Anwendungsmanifest nach Assembly Updates suchen. Wenn eine neueVersion der Assembly verfügbar ist, lädt die Common Language Runtime die neue Version der Assemblyzum Cache im ClickOnce auf dem Clientcomputer.

5. Visual Studio Tools for O�ce runtime erstellt eine neue Anwendungsdomäne, in die die Anpassungsas-sembly geladen werden.

6. Visual Studio Tools for O�ce runtime lädt die Anpassungsassembly in der Anwendungsdomäne.

7. Visual Studio Tools for O�ce runtime Ruft den Ereignishandler Startup in die Anpassungsassembly.

4.3.2 Modell

7)Das Add-In-Modell besteht aus einer Reihe von Segmenten, die die Add-In-Pipeline (wird auch als Kommu-nikationspipeline bezeichnet) bilden, die für die gesamte Kommunikation zwischen dem Add-In und dem Hostverantwortlich ist. Die Pipeline ist ein symmetrisches Kommunikationsmodell aus Segmenten, die Daten zwi-schen einem Add-In und dessen Host austauschen. Die Entwicklung dieser Segmente zwischen dem Host unddem Add-In stellt die erforderlichen Abstraktionsebenen bereit, die die Versionsverwaltung und Isolation desAdd-Ins unterstützen.

6Architecture of Document-Level Customizations7Quelle: Übersicht über Add-Ins

41 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 42: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4.3 Managed Add-in Framework (MAF)

Abbildung 4.5: Listen

Die Assemblys für diese Segmente müssen sich nicht in derselben Anwendungsdomäne be�nden. Sie könnenein Add-In in seine eigene neue Anwendungsdomäne, in eine vorhandene Anwendungsdomäne oder sogar indie Anwendungsdomäne des Hosts laden. Sie können mehrere Add-Ins in dieselbe Anwendungsdomäne laden.Dadurch können die Add-Ins Ressourcen und Sicherheitskontexte gemeinsam nutzen.

Das Add-In-Modell unterstützt und emp�ehlt eine optionale Grenze zwischen dem Host und dem Add-In, dieals Isolationsgrenze bezeichnet wird (auch unter der Bezeichnung Remotegrenze bekannt). Diese Grenze kanneine Anwendungsdomänen- oder Prozessgrenze sein.

Das Vertragssegment in der Mitte der Pipeline wird in die Anwendungsdomäne des Hosts und in die Anwen-dungsdomäne des Add-Ins geladen. Der Vertrag de�niert die virtuellen Methoden, mit denen der Host und dasAdd-In Typen austauschen.

Um die Isolationsgrenze zu überwinden, müssen Typen entweder Verträge oder serialisierbare Typen sein. Ty-pen, die weder Verträge noch serialisierbare Typen sind, müssen von den Adaptersegmenten in der Pipelinekonvertiert werden.

Die Ansichtssegmente der Pipeline sind abstrakte Basisklassen oder Schnittstellen, die dem Host und demAdd-In eine Ansicht der gemäÿ der De�nition im Vertrag gemeinsam genutzten Methoden bereitstellen.

Der Unterschied zwischen einem Add-In und einem Host besteht darin, dass der Host das Element ist, das dasAdd-In aktiviert. Der Host kann das gröÿere der beiden Elemente, wie z. B. eine Textverarbeitungsanwendungmit ihren Rechtschreibprüfungen, oder das kleinere der beiden Elemente sein, wie z. B. ein Instant Messaging-Client mit einem eingebetteten Media Player. Das Add-In-Modell unterstützt Add-Ins sowohl in Client- alsauch in Serverszenarios. Beispiele für Server-Add-Ins sind Add-Ins, die Virenschutz, Spam�lter und IP-Schutzfür Mailserver bereitstellen. Beispiele für Client-Add-Ins umfassen Referenz-Add-Ins für Textverarbeitungspro-gramme, spezielle Funktionen für Gra�kprogramme und Spiele sowie Virenschutz für lokale E-Mail-Clients.

8Im .NET Framework 3.5 �ndet man unter dem Namespace System.AddIn ein Objektmodell mit dem manAnwendungen um ein Plug-In Modell erweitern kann.

System.AddIn arbeitet hier sehr strikt mit einer Interface De�nition. Die De�nition ist die Grundlage fürentsprechende Adapterklassen, die letztendlich dafür verantwortlich sind den View zu aktualisieren. Der Viewauf beiden Seiten der Pipeline ist dann einmal mit dem Host, zum anderen mit dem AddIn verbunden.

Die Adapter und der Contract sind somit nicht mit dem Host und nicht mit dem AddIn direkt referenziert,sondern werden durch die System.AddIn Infrastruktur auf Basis von Attributen und Ableitungen entsprechendgeladen und instanziert.

Um ein Add-In zu entwickeln muÿ der Add-In Entwickler nur folgende Abhängigkeiten beachten:

• Man muÿ den entsprechenden View des Contracts referenzieren, eine abstrakte Basisklasse die überschrie-ben wird.

8.NET 3.5 Feature: System.AddIn

42 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 43: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4.3 Managed Add-in Framework (MAF)

• Das Add-In wird noch Attributiert, um dem Framework mitzuteilen, das es sich hier um ein Add-Inhandelt, das über die Infrastruktur instanziert werden kann.

• Kopieren des Add-In Assemblies in die entsprechende Verzeichnisstruktur. Der Host kann dann das Add-Inladen.

Hier ein kleines Beispiel einer Add-In Implementierung.

Listing 4.1: Add-In

using System;

using System.AddIn;

using Demo.View.Addin;

namespace Demo.Addin.Number2

{

[AddIn( "Hello World (TechTalk Version)", Version="1.0.0.0")]

public class TechTalkHelloWorld : HelloWorld

{

public override string SayHelloWorld()

{

return "Hallo TechTalk!";

}

}

}

HelloWorld ist der View von dem der Entwickler ableitet. Auf der Host-Seite gibt es nun den entsprechendenAktivierungscode für Add-Ins. Hier ein Beispiel um ein Add-In zu �nden und zu instanzieren.

Listing 4.2: Add-In nutzen

// pipeRoot ist das Verzeichnis in welchem die Infrastruktur sucht

//tokens enthält alle verfügbaren Add−Ins die das Interface HelloWorld implementiert haben.

Collection<AddInToken> tokens = AddInStore.FindAddIns( typeof( HelloWorld ), pipeRoot );

// Code zum anzeigen der verfügbaren Add−Ins und Auswahl−Logik// das ausgewählte Token wird referenziert

AddInToken helloWorldToken = tokens[ number - 1 ];

// Instanzieren des Add−Ins//Add−In in einer eigenen AppDomain im gleichen Prozess des Hosts instanziieren

HelloWorld helloWorld = helloWorldToken.Activate<HelloWorld>( AddInSecurityLevel.Host );

// Nutzen des Add−InsConsole.WriteLine( helloWorld.SayHelloWorld() );

Man erkennt schon, dass Add-Ins dementsprechend Remotable sein müssen, da .NET Remoting als Kommuni-kationsinfrastruktur genommen wird.

Um das Add-In nun in einen eigenen Prozess zu instanziieren, kann man folgende Zeilen schreiben.

AddInToken helloWorldToken = tokens[ number - 1 ];

AddInProcess process = new AddInProcess();

process.Start();

HelloWorld helloWorld = helloWorldToken.Active<HellWorld>( process,AddInSecurityLevel.Host );

Console.WriteLine( helloWorld.SayHelloWorld() );

Beim Benutzen des Add-Ins wird entsprechend ein neuer Prozess hochgezogen. Die Infrastruktur stellt hierzuden Host AddInProcess32.exe zur Verfügung.

Die Kommunikation zwischen dem gestarteten Prozess und dem Host läuft dann über den IPC Channel von.NET Remoting. Die Inter-Process-Communication des Betriebssystems erlaubt es direkt zwischen AppDomainsauf der gleichen physikalischen Maschine performant zu kommunizieren und .NET Remoting bietet eben diesenChannel an, der dann auch konsequent von AddInProcess32.exe genutzt wird.

Das System.AddIn Objektmodell erlaubt es nun mit relativ einfachen Mitteln, eine Plugin Architektur in ei-gene Anwendungen einzubauen, mit dem Vorteil der Versionierung und der Isolation durch die verfügbarenAktivierungsmöglichkeiten.

Das Hauptproblem bei der Übertragung von UI-Elementen besteht nun darin, dass die Klassen imSystem.Windows.Forms-Namespace nicht serialisierbar sind und somit nicht direkt übertragen werden können.Die leiten zwar von MarshalByRefObject ab, können aber auch nicht per Referenz übertragen werden. Dochwie sonst soll die Kommunikation erfolgen? Eine Lösung zum diesem Problem ist hier beschrieben Windows

43 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 44: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

4.3 Managed Add-in Framework (MAF)

Forms Support für System.AddIn, Teil 1. Wie das in WPF funktioniert, wird hier beschrieben: Gewusst wie:Erstellen eines Add-Ins, das eine Benutzerober�äche zurückgibt.

44 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 45: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

5 Windows Presentation Foundation

Abbildung 5.1: User Experience

Viele Anwendungen werden vorwiegend von Entwicklern entworfen. Das CI designen Pro�s, die auch Vorgabenfür Schriften und Farben der Produkte machen, das Look and Feel stammt jedoch häu�g von mehr oder wenigerbegabten Entwicklern. Sie gehen nüchtern davon aus, wenn das Produkt die tollen Features hat und diese fürsie gut bedienbar sind, dann reicht das. Gerade der Erfolg von Apple zeigt, dass Begeisterung für ein Produktetwas anderes ist. Zu diesem Thema gibt es einen ganzen Forschungszweig User Experience (UX) und Firmeninvestieren zunehmend in die Messung dieser Erfahrungen. Google liefert beispielsweise 15.300.000 Tre�er zudiesem Begri�.

User-experience is not like usability - it is about feelings. The aim here is to create happiness. Youwant people to feel happy before, during and after they have used your product.

Baekdal, Thomas. 20061

Dies sind sicher hohe Ansprüche für eine nüchterne DB-Modellierungsanwendung. Dennoch ist es bei jedemgröÿeren Produkt � mit dem entsprechenden Budget � sinnvoll, UX zu berücksichtigen und Pro�s hinzuzuziehen.Mehr zu diesem Thema �nden Sie z.B. unter What's new on Design IBM, An Agile approach to User Experienceand Design oder Windows User Experience Interaction Guidelines

Wie sieht die übliche Zusammenarbeit zwischen Designer und Entwicklern aus � bei Desktopapplikationen undmomentan noch verbreiteter im Web? Designer entwerfen coole Ober�ächen mit ihrem Lieblingstool auf ihremLieblingssystem, runde Ecken, Farbverläufe, Übergänge bei Aktionen, ... sehr schön anzusehen. Entwickler�uchen und möchten sich eigentlich auf die Funktionalität konzentrieren, stattdessen kämpfen sie Stunden,Wochen um die pixelgenaue Abbildung der in PNG o.ä. gelieferten Vorlagen. Kämpfe mit Vorgesetzten, ob mannicht hier und da Abstriche an der Vorlage machen kann. Aber gerade im Web, ist Präsentation wichtig, es sollin jedem Browser gleich aussehen.

Gesucht ist also ein einheitliches Format, das leicht unter Entwicklern und Designern auszutauschen ist und denAnforderungen der Designer genügt. Einfach auszutauschendes Format schreit geradezu nach XML. Basierendauf XML gibt es Beschreibungssprachen für Ober�ächen. Ein ausführliche Übersicht �nden Sie bei WikipediaList of user interface markup languages. Microsoft wollte nicht nur eine mächtige Beschreibungssprache, die allendenkbaren Designeransprüchen genügt, sondern auch eine 1:1-Integration in ein OO-Modell, genauer gesagt, inihr .Net-OO-Framework. Es wurde daher eine neue Beschreibungssprache de�niert � XAML � und von Grundauf ein neues Framework aufgesetzt, das sich in .Net integriert und alle XAML-Möglichkeiten in entsprechendeObjekte und Eigenschaften abbildet. Eine Ober�ächenanwendung in WPF enthält also nicht mehr irgendwiegenerierten Code, den man nicht anfassen darf, sondern das Form (oder jedes andere visuelle Element) besteht

1The Battle Between Usability and User-Experience

45

Page 46: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

5.1 Architektur

Abbildung 5.2: User Experience

aus zwei oder mehr partiellen Klassende�nitionen: XAML de�niert das Aussehen und visuelle Verhalten �deklarativ mit 1:1-Abbildung in das zugehörige Objektmodell � und klassischer C#-Code bildet die Logik ab.

5.1 Architektur

Abbildung 5.3: WPF Architektur

2Das primäre WPF-Programmiermodell wird mithilfe von verwaltetem Code bereitgestellt. Am Anfang derEntwurfsphase von WPF gab es einige Diskussionen darüber, wo die Linie zwischen den im System verwaltetenKomponenten und den nicht verwalteten gezogen werden sollte. Die CLR stellt eine Reihe von Features bereit,die zu einer produktiveren und robusteren Entwicklung führen (einschlieÿlich Speicherverwaltung, Fehlerbe-handlung, allgemeines Typsystem usw.), die jedoch auch ihren Preis haben.

PresentationFramework, PresentationCore und milcore stellen die Hauptbestandteile des Codes von WPF dar.Milcore ist in einem nicht verwalteten Code geschrieben, um eine enge Integration mit DirectX zu ermöglichen.Alle Anzeigen in WPF erfolgen durch das DirectX-Modul, um ein e�zientes Hardware- und Softwarerende-ring zu gewährleisten. WPF erforderte auÿerdem eine Feinsteuerung des Speichers und der Ausführung. DasErstellungsmodul in milcore ist äuÿerst leistungsabhängig und erforderte im Sinne der Leistungsverbesserungdie Aufgabe vieler CLR-Vorteile. Sowohl WPF als auch Silverlight können XAML verarbeiten, also parsen undrendern (darstellen). Es gibt auch viele Designerwerkzeuge, die einen Export oder Import von XAML unter-stützen.

2WPF-Architektur

46 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 47: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

5.1 Architektur

Abbildung 5.4: WPF wesentliche Klassen

Die meisten WPF-Objekte werden aus DispatcherObject abgeleitet, in dem die grundlegenden Konstrukte zurHandhabung von Parallelität und Threading bereitgestellt werden. WPF basiert auf einem vom Verteiler im-plementierten Nachrichtensystem. Die Funktionsweise ist mit der bekannten Meldungsverteilschleife in Win32zu vergleichen. Tatsächlich verwendet der WPF-Verteiler User32-Meldungen zum Ausführen von threadüber-greifenden Aufrufen.

WPF stellt ein umfangreicheres Eigenschaftensystem bereit, das vom DependencyObject-Typ abgeleitet wird.Das Eigenschaftensystem ist insofern wirklich ein Abhängigkeitseigenschaftensystem, als es die Abhängigkeitenzwischen den Eigenschaftsausdrücken erfasst und im Falle von Abhängigkeitsänderungen die Eigenschaftenwerteautomatisch erneut überprüft. Wenn Sie z. B. eine Eigenschaft verwenden, die Werte erbt (z. B. FontSize), wirddas System automatisch aktualisiert, wenn sich die Eigenschaft in einem übergeordneten Element des Elementsändert, das den Wert erbt.

Anhand von Measure kann eine Komponente seine Gröÿe bestimmen. Hierbei handelt es sich um eine vonArrange getrennte Phase, da viele Situationen auftreten können, in denen ein übergeordnetes Element einuntergeordnetes Element au�ordert, mehrere Measures zur Ermittlung seiner optimalen Position und Gröÿeauszuführen. Die Tatsache, dass untergeordnete Elemente von übergeordneten Elementen zur Durchführung vonMeasures aufgefordert werden, verdeutlicht eine andere Schlüsselphilosophie vonWPF � Das Anpassen der Gröÿean den Inhalt. Alle Steuerelemente in WPF unterstützen die Fähigkeit, sich an die natürliche Gröÿe ihres Inhaltsanzupassen. Dies führt zu einer einfacheren Lokalisierung und ermöglicht aufgrund der Gröÿenanpassung eindynamisches Elementlayout. Anhand der Arrange-Phase kann ein übergeordnetes Element jedes untergeordneteElement positionieren und dessen endgültige Gröÿe bestimmen.

Jedes Eingabeereignis wird in mindestens zwei Ereignisse konvertiert � in ein �Vorschauereignis� und in das tat-sächliche Ereignis. Alle WPF-Ereignisse können innerhalb der Elementstruktur weitergeleitet werden. Ereignissesteigen �in Blasen� auf, wenn sie von einem Ziel entlang der Struktur nach oben bis zum Stamm traversieren.Sie arbeiten nach dem Tunnelprinzip, wenn sie vom Stamm ausgehend nach unten zu einem Ziel traversieren.Eingabenvorschauereignisse nutzen das Tunnelprinzip, wodurch jedes in der Struktur enthaltene Element dasEreignis �ltern oder eine Ereignismaÿnahme ergreifen kann. Die regulären Ereignisse (keine Vorschauereignisse)steigen dann vom Ziel hoch zum Stamm auf.

Diese Unterteilung zwischen der Tunnel- und Aufstiegsphase ermöglicht eine einheitliche Implementierung vonFeatures (z. B. von Zugri�stasten) in einer gemischten Umgebung. In User32 würden Sie Zugri�stasten anhandeiner einzelnen globalen Tabelle implementieren, die alle zu unterstützenden Zugri�stasten (Strg+N mit �Neu�verknüpft) enthält. In dem Verteiler Ihrer Anwendung würden Sie TranslateAccelerator aufrufen, der die Ein-gabemeldungen in User32 einliest und ermittelt, ob eine Übereinstimmung mit einer registrierten Zugri�stastebesteht. In WPF würde dies nicht funktionieren, da das System vollständig zusammensetzbar ist, d. h., jedes Ele-ment kann jede beliebige Zugri�staste verarbeiten und verwenden. Dieses eingabenrelevante Zweiphasenmodellermöglicht es Komponenten, den eigenen �TranslateAccelerator� zu implementieren.

Darüber hinaus führt UIElement das Konzept der CommandBindings ein. Mithilfe des WPF-Befehlssystemkönnen Entwickler Funktionalitäten in Form von Befehlsendpunkten de�nieren � etwas, wodurch ICommandimplementiert wird. Befehlsbindungen ermöglichen, dass Elemente die Zuordnung einer Eingabeaktion (Strg+N)

47 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 48: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

5.2 Ereignisse

zu einem Befehl (Neu) de�nieren können. Sowohl Eingabeaktionen als auch Befehlsde�nitionen sind erweiterbarund können zum Zeitpunkt der Verwendung miteinander verknüpft werden. Dadurch können Endbenutzer z. B.die in einer Anwendung zu verwendenden Tastenbindungen überaus einfach anpassen.

5.2 Ereignisse

In XAML kann jeder Objekt andere Objekte enthalten. Ein Button, kann ein Image, einen Text und vielleichteine TextBox zur Eingabe von Text enthalten. Wenn ich nun in die Textbox klicke, möchte ich den Text ändernkönnen. Wie wird dieses Ereignis Klick in die Textbox verarbeitet? Wird gleichzeitig der Button geklickt undder Text geändert? Arbeiten Sie hierzu den Artikel auf Seite 29 im Anhang durch.

Aufgabe 5.1 Erstellen Sie eine WPF-Anwendung mit einem Button, in dem eine TextBox und ein weitererButton enthalten sind. Knüpfen Sie an die typischen Ereignisse eine Ausgabe. Debuggen Sie: Was passiert,welches Ereignis wird wo behandelt? Erstellen Sie zwei Styles für Ihre Applikation. Klick auf den äuÿerenButton vertauscht den aktuellen Style mit dem anderen Style.

5.3 WinForm und WPF

Abbildung 5.5: WPF mit WinForm und umgekehrt (Quelle:Interoperabilität zwischen Windows Forms undWPF

Soll ein auf WinForm basierendes bestehendes Produkt erweitert werden oder gibt es ein Control noch nichtfür WPF, so können WPF und WinForm auch gut zusammen verwendet werden. Zur Integration von WPFin WinForm benötigen Sie die Assemblies WindowsFormsIntegration.dll, PresentationFramework.dll, Presenta-tionCore.dll, WindowsBase.dll. Umgekehrt basiert der Zugri� auf WinForm aus WPF heraus auf den KlassenWindowsFormsHost und ElementHost aus dem Namespace System.Windows.Forms.Integration.

48 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 49: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6 Datenbankanbindung

Die typischen Probleme datenbankbasierter Applikationen, wie gleichzeitiger lesender oder schreibender Zugri�unter Berücksichtigung von Transaktionen oder die Unterstützung unterschiedlicher Datenbankmanagement-systeme bei sich veränderten Strukturen der beteiligten Tabellen, erfordern eine gute Unterstützung durch dieEntwicklungsumgebung und Klassenbibliotheken. Handarbeit und umfangreiche individuelle Implementationensind fehleranfällig und ine�zient. Die Abbildung der OO-Strukturen auf das relationale System erfolgt geradebei gröÿeren Projekten über ORM (Objekt-Relational-Mapping). Dadurch können Änderungen an den Tabellenrobust in die Applikation eingep�egt werden. Dies übernimmt bei Microsoft das Entity Framework.

Abbildung 6.1: DB: Technologien

Abbildung 6.2: DB Technologien

Die Basis von Datenbankzugri�en in .Net ist ADO.Net. ADO.Net bietet verschiedene Provider zum Zugri�auf Datenbanken wie SQL Server, Oracle oder über OLE.Db und ODBC auf unterschiedliche Datenquellen.XML wird als Datenquelle zunehmend bedeutender, so dass auch hier ein performanter lesender, suchender undschreibender Zugri� nötig ist. Über ADO.Net gibt es passende Interfaces, man muss jedoch für SQL Server undOracle unterschiedliche Provider verwenden. Bisher musste man zur Unterstützung verschiedener Datenbank-managementsysteme (DBMS) eine eigene Factory implementieren - dies wurde durch ein einheitliches Interfacegut unterstützt, aber war eben händisch zu implementieren. Mit dem nun herausgegebenen Entity Frameworksoll dies entfallen � ob dies auch für die grundunterschiedliche Handhabung von Triggern in Oracle und SQLServer zutri�t, konnte ich noch nicht evaluieren. Mit LINQ steht eine zusätzliche, vom jeweiligen Datenbank-managementsystem unabhängige Abfragesprache zur Verfügung, so dass auch Queries nicht für jedes Systementsprechend formuliert werden müssen. Diese Unterschiede zwischen SQL Server und Oracle � dies gilt na-türlich auch für DB2, MySQL und andere DBMS � können schon auf Select- oder Insert-Statements zutre�en.Abhängig davon wie DBMS-spezi�sch man bisher ausgebildet ist, fällt einem mitunter gar nicht auf, dass manspezielle Features bei seiner Abfrage nutzt, die so in anderen DBMS nicht zur Verfügung stehen oder eineranderen Syntax bedarfen.

Es gibt zwei Komponenten von ADO.NET, die Sie zum Zugreifen auf Daten und deren Bearbeitung verwendenkönnen: .NET Framework-Datenanbieter und das DataSet

6.0.1 .NET Framework-Datenanbieter

Die .NET Framework-Datenanbieter sind Komponenten, die explizit für die Datenbearbeitung und den schnellen,vorwärts gerichteten, schreibgeschützten Zugri� auf Daten entworfen wurden. Das Connection-Objekt sorgt füreine Verbindung mit einer Datenquelle. Mit dem Command-Objekt können Sie auf Datenbankbefehle zugreifen,um Daten zurückzugeben oder zu ändern, gespeicherte Prozeduren auszuführen und Parameterinformationen

49

Page 50: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6 Datenbankanbindung

Abbildung 6.3: Verbindungsorientierter DB-Zugri�

zu senden oder abzurufen. Das DataReader-Objekt stellt einen Hochleistungsstream von Daten aus der Daten-quelle zur Verfügung. Schlieÿlich fungiert der DataAdapter als Brücke zwischen dem DataSet-Objekt und derDatenquelle. Der DataAdapter verwendet Command-Objekte, zum Ausführen von SQL-Befehlen in der Daten-quelle, um damit das DataSet mit Daten zu laden und Änderungen an den Daten im DataSet in die Datenquellezu übernehmen. Der DataAdapter ö�net die Verbindung automatisch falls sie noch nicht o�en ist und schlieÿtdie Verbindung automatisch, falls diese selbst geö�net wurde. Die wichtigsten Methoden des DataAdapters sindFill() und Update().

Listing 6.1: Datenzugri� mit DataReader

string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;";

//mit using wird Connection und DataReader immer geschlossen!

using (SqlConnection conn = new SqlConnection(connString))

{

SqlCommand cmd = conn.CreateCommand();

cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";

conn.Open();

using (SqlDataReader dr = cmd.ExecuteReader())

{

while (dr.Read())

Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1));

}

}

SqlConnection nwindConn = new SqlConnection("Data Source=localhost; Integrated Security=SSPI;" +

"Initial Catalog=northwind");

nwindConn.Open();

SqlCommand catCMD = new SqlCommand("SELECT CategoryID, CategoryName FROM Categories", nwindConn);

SqlDataReader myReader = myCommand.ExecuteReader();

if (myReader.HasRows)

while (myReader.Read())

Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1));

else

Console.WriteLine("No rows returned.");

myReader.Close();

//nur einen Wert auslesen:

SqlCommand ordersCMD = new SqlCommand("SELECT Count(*) FROM Orders", nwindConn);

Int32 count = (Int32)ordersCMD.ExecuteScalar();

nwindConn.Close();

//mit Parametern

using (SqlConnection connection =

new SqlConnection(connectionString))

{

SqlDataAdapter dataAdpater = new SqlDataAdapter(

50 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 51: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6 Datenbankanbindung

"SELECT CategoryID, CategoryName FROM Categories",

connection);

dataAdpater.UpdateCommand = new SqlCommand(

"UPDATE Categories SET CategoryName = @CategoryName " +

"WHERE CategoryID = @CategoryID", connection);

dataAdpater.UpdateCommand.Parameters.Add(

"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add(

"@CategoryID", SqlDbType.Int);

parameter.SourceColumn = "CategoryID";

parameter.SourceVersion = DataRowVersion.Original;

DataTable categoryTable = new DataTable();

dataAdpater.Fill(categoryTable);

DataRow categoryRow = categoryTable.Rows[0];

categoryRow["CategoryName"] = "New Beverages";

dataAdpater.Update(categoryTable);

Console.WriteLine("Rows after update.");

foreach (DataRow row in categoryTable.Rows)

{

{

Console.WriteLine("{0}: {1}", row[0], row[1]);

}

}

}

6.0.2 DataSet

Abbildung 6.4: CTS in Relation zu CLS

Das DataSet, ein aus einer Datenquelle abgerufener Datencache im Arbeitsspeicher, Das DataSet besteht auseiner Au�istung von DataTable-Objekten, die Sie mit DataRelation-Objekten aufeinander beziehen können.In einer typischen Implementierung mit mehreren Ebenen werden die folgenden Schritte zum Erstellen undAktualisieren eines DataSet und zum anschlieÿenden Aktualisieren der ursprünglichen Daten ausgeführt:

1Das DataSet besteht aus einer Au�istung von DataTable-Objekten, die Sie mit DataRelation-Objekten auf-einander beziehen können.

1. Erstellen Sie mithilfe eines DataAdapter jede DataTable in einem DataSet, und füllen Sie sie mit Datenaus einer Datenquelle.

1DataSet-Klasse

51 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 52: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.1 Entity Framework

2. Ändern Sie die Daten in einzelnen DataTable-Objekten, indem Sie DataRow-Objekte hinzufügen, aktua-lisieren oder löschen.

3. Rufen Sie die GetChanges-Methode auf, um ein zweites DataSet zu erstellen, das nur die Änderungen anden Daten darstellt.

4. Rufen Sie die Update-Methode von DataAdapter auf, und übergeben Sie das zweite DataSet als Argument.

5. Rufen Sie die Merge-Methode auf, um die Änderungen aus dem zweiten DataSet mit dem ersten zusam-menzuführen.

6. Rufen Sie AcceptChanges für das DataSet auf. Sie können auch RejectChanges aufrufen, um die Ände-rungen zu verwerfen.

Listing 6.2: Update mit DataSet

//connect to db

SqlCommand mySelectCommand = New SqlCommand("select * from customers", myConnection);

SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(mySelectCommand);

DataSet myDataSet = new DataSet();

mySqlDataAdapter.Fill(myDataSet,"Customers");

//anzeigen

foreach (DataRow myDataRow in myDataSet.Tables["Customers"].Rows)

{

Console.WriteLine(myDataRow["CustomerId"].ToString());

}

//löschen

myDataSet.Tables["Customers"].Rows[0].Delete();

//update

myDataSet.Tables["Customers"].Rows[0]["ContactName"]="Peach";

mySqlDataAdapter.Update(myDataSet);

6.0.3 DataSet oder DataReader

Das ADO.NET-DataSet wurde explizit für den Datenzugri� unabhängig von Datenquellen entworfen. Aus die-sem Grund kann es mit mehreren, unterschiedlichen Datenquellen, mit XML-Daten oder zum Verwalten vonlokalen Anwendungsdaten verwendet werden. Das DataSet enthält eine Au�istung von einem oder mehrerenDataTable-Objekten, die aus Datenzeilen und -spalten sowie aus Primärschlüsseln, Fremdschlüsseln, Einschrän-kungen und Beziehungsinformationen über die Daten in den DataTable-Objekten besteht.

Wenn Sie sich zwischen einem DataReader oder einem DataSet für die Anwendung entscheiden möchten, müssenSie den für die Anwendung erforderlichen Funktionalitätstyp berücksichtigen. Verwenden Sie für folgende Zweckeein DataSet:

• Lokales Zwischenspeichern von Daten in Ihrer Anwendung, um sie bearbeiten zu können. Wenn Sie nurdie Ergebnisse einer Abfrage anzeigen müssen, ist der DataReader die bessere Alternative.

• Verschieben von Daten zwischen Ebenen oder von einem XML-Webdienst.

• Dynamisches Interagieren mit Daten, wie Datenbindung an ein Windows Forms-Steuerelement, oder Kom-binieren von und Erstellen einer Beziehung zwischen Daten aus mehreren Quellen.

• Ausführen umfangreicher Datenverarbeitungsschritte ohne eine o�ene Verbindung zur Datenquelle. Da-durch wird die Verbindung zur Nutzung durch andere Clients freigegeben.

Wenn Sie die von DataSet bereitgestellten Funktionen nicht benötigen, können Sie die Leistung Ihrer Anwen-dung verbessern, indem Sie mithilfe des DataReader die Daten als schreibgeschützte Vorwärtsdaten zurück-geben. Obwohl der DataAdapter den DataReader zum Füllen des DataSet-Inhalts verwendet (siehe Au�ülleneines DataSets mit einem DataAdapter-Objekt), können Sie die Leistung erhöhen, wenn Sie den DataReaderverwenden. Dies ist möglich, weil Sie den Arbeitsspeicher, der für das DataSet erforderlich ist, nicht benötigenund die Verarbeitung, die zum Erstellen und Füllen des DataSet-Inhalts nötig ist, über�üssig ist.

6.1 Entity Framework

Entity Framework ist ein Satz von Technologien in ADO.NET, die die Entwicklung datenorientierter (relational)Softwareanwendungen (objektorient) unterstützen.

52 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 53: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.1 Entity Framework

6.1.1 Mapping von Objekten zu Daten

2Die objektorientierte Programmierung stellt eine Herausforderung für die Interaktion mit Datenspeichersyste-men dar. Obwohl die Organisation der Klassen häu�g den Aufbau relationaler Datenbanktabellen recht genauwiderspiegelt, passen diese Strukturen nicht perfekt zusammen. Oft entsprechen mehrere normalisierte Tabel-len einer einzigen Klasse, und Beziehungen zwischen Klassen werden nicht auf dieselbe Weise dargestellt wieBeziehungen zwischen Tabellen. Um beispielsweise den Kunden für einen Auftrag darzustellen, verwendet dieOrder-Klasse eine Eigenschaft, die einen Verweis auf eine Instanz einer Customer-Klasse enthält. Eine Zei-le in der Order-Tabelle in einer Datenbank enthält jedoch eine Fremdschlüsselspalte (oder mehrere Spaltenals Fremdschlüssel) mit einem Wert, der einem Primärschlüsselwert in der Customer-Tabelle entspricht. EineCustomer-Klasse kann über eine Eigenschaft mit dem Namen Orders verfügen, die eine Au�istung von Instanzender Order-Klasse enthält. Die Customer-Tabelle in einer Datenbank verfügt jedoch nicht über eine vergleichbareSpalte.

Vorhandene Lösungen haben versucht, diese Lücke, oft als �Impedance Mismatch� bezeichnet, zu füllen, indemnur objektorientierte Klassen und Eigenschaften relationalen Tabellen und Spalten zugeordnet wurden. Stattdiesen herkömmlichen Ansatz zu verfolgen, ordnet Entity Framework relationale Tabellen, Spalten und Fremd-schlüsseleinschränkungen in logischen Modellen den Entitäten und Beziehungen in konzeptionellen Modellen zu.Dadurch wird sowohl die De�nition von Objekten als auch die Optimierung des logischen Modells viel �exibler.Die Entitätsdatenmodell-Tools generieren erweiterbare Datenklassen auf der Grundlage des konzeptionellenModells. Diese Klassen sind partielle Klassen, die mit zusätzlichen, vom Entwickler hinzuzufügenden Membernerweitert werden können. Die für ein bestimmtes konzeptionelles Modell erzeugten Klassen werden von Basis-klassen abgeleitet, die Object Services zur Umsetzung von Entitäten in Objekte und zum Nachverfolgen undSpeichern von Änderungen zur Verfügung stellen. Entwickler können diese Klassen verwenden, um die Entitätenund Beziehungen als Objekte zu behandeln, die durch Navigationseigenschaften verknüpft sind.

6.1.2 Zugreifen auf und Ändern von Entitätsdaten

Entity Framework ist mehr als nur eine weitere objektrelationale Mappinglösung. Es dient imWesentlichen dazu,Anwendungen den Zugri� auf und die Änderung von Daten zu ermöglichen, die als Entitäten und Beziehun-gen im konzeptionellen Modell dargestellt werden. Object Services verwendet das EDM, um Objektabfragenvon Entitätstypen, die im konzeptionellen Modell dargestellt werden, in datenquellenspezi�sche Abfragen zuübersetzen. Abfrageergebnisse werden in Objekte umgesetzt, die von Object Services verwaltet werden. EntityFramework bietet die folgenden Möglichkeiten, ein EDM abzufragen und Objekte zurückzugeben:

• LINQ-to-Entities � bietet Language Integrated Query (LINQ)-Unterstützung zum Abfragen von Entitäts-typen, die in einem konzeptionellen Modell de�niert sind.

• Entity SQL � ein speicherunabhängiger SQL-Dialekt, der direkt mit Entitäten im konzeptionellen Mo-dell arbeitet und EDM-Features, wie beispielsweise Vererbung und Beziehungen, unterstützt. Entity SQLwird sowohl für Objektabfragen als auch für Abfragen verwendet, die mithilfe des EntityClient-Anbietersausgeführt werden.

• Abfrage-Generator-Methoden � ermöglichen das Erstellen von Entity SQL-Abfragen unter Verwendungvon Abfragemethoden im Stil von LINQ.

Entity Framework enthält den EntityClient-Datenanbieter. Dieser Anbieter verwaltet Verbindungen, übersetztEntitätsabfragen in datenquellenspezi�sche Abfragen und gibt einen Datenleser zurück, mit dem Object ServicesEntitätsdaten in Objekte umsetzt. Wenn die Umsetzung in Objekte nicht erforderlich ist, kann der EntityClient-Anbieter auch wie ein ADO.NET-Standarddatenanbieter verwendet werden, indem er Anwendungen das Aus-führen von Entity SQL-Abfragen und die Verarbeitung des zurückgegebenen schreibgeschützten Datenlesersermöglicht.

Entity Framework generiert eine von ObjectContext abgeleitete Klasse, die den im konzeptionellen Modellde�nierten Entitätencontainer darstellt. Dieser Objektkontext stellt die Funktionen zum Nachverfolgen vonÄnderungen und zum Verwalten von Identitäten, Parallelität und Beziehungen bereit. Diese Klasse macht aucheine SaveChanges-Methode verfügbar, die Einfügungen, Aktualisierungen und Löschungen in die Datenquelleschreibt. Diese Änderungen werden wie bei Abfragen entweder durch automatisch vom System generierte Befehleoder durch vom Entwickler angegebene gespeicherte Prozeduren vorgenommen.

2Einführung in Entity Framework

53 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 54: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.2 LINQ

Abbildung 6.5: Entity Framework

6.1.3 Datenmodellierung im Entity Framework

3 Das Entitätsdatenmodell (EDM) ist ein Entitätsbeziehungsmodell. Mit dem EDM werden Daten in einemneutralen Format de�niert, das nicht durch die Struktur von Programmiersprachen oder relationalen Datenban-ken eingeschränkt ist. EDM-Schemas werden verwendet, um Details zu Entitäten und Beziehungen anzugebenund diese als Datenstrukturen zu implementieren.

Eine Entität ist etwas in der Anwendungsdomäne, das durch Daten dargestellt werden muss. Beispiele für Enti-täten und Beziehungen sind in einer typischen Geschäftsbereichsanwendung zu �nden. Entitäten in der Domäneeiner Geschäftsbereichsanwendung können Kunden, Aufträge, Auftragspositionen, Lieferanten, Produkte, Ver-kaufsmitarbeiter, Spediteure, Rechnungen usw. umfassen. Ein EDM-EntityType ist die Spezi�kation für einenDatentyp, der die Entität in der Anwendungsdomäne darstellt.

Eine Beziehung ist die logische Verbindung zwischen Entitäten, wie beispielsweise zwischen einem Warenauftragund dem Kunden, der den Auftrag aufgibt. Da ein Kunde über viele zugeordnete Aufträge verfügen kann, ist dieBeziehung zwischen dem Kunden und seinen Aufträgen eine 1:n-Beziehung. Zwischen Produkten und Lieferantenkann eine m:n-Beziehung bestehen.

Das De�nieren von Entitäten und Beziehungen kann ein sehr komplexer Vorgang sein. Etwas so Grundlegen-des wie ein Warenauftrag in einer Geschäftsbereichsanwendung erfordert eine nicht zu unterschätzende Mengean Details. Zum Beispiel kann ein Warenauftrag unterschiedliche Formen haben. So könnte der Auftrag imLaden, am Telefon, im Internet oder per Katalog aufgegeben werden. Im EDM sind die Details der einzelnenAuftragstypen konzeptionell in XML-Syntax angegeben. Die Eigenschaften jedes Auftragstyps und notwendigeEinschränkungen werden an Anwendungen weitergegeben, die Daten mit dem konzeptionellen Schema verwen-den.

Die Entitäten und ihre Beziehungen werden vom EDM mithilfe zweier grundlegender Typen modelliert.

• EntityType: Die abstrakte Spezi�kation für die Details einer Datenstruktur in der Anwendungsdomäne.

• AssociationType: Die logische Verbindung zwischen Typen.

EDM-Generator (EdmGen.exe) ist ein Befehlszeilendienstprogramm, dass eine Verbindung zu einer Datenquelleherstellt und ein EDM auf der Grundlage eines 1:1-Mappings zwischen Entitäten und Tabellen erstellt. Auÿer-dem verwendet es eine Konzeptmodelldatei (.csdl), um eine Objektebenendatei mit Klassen zu erzeugen, dieEntitätstypen und den ObjectContext darstellen.

6.2 LINQ

Mit LINQ (Language Integrated Query) gibt es eine Abfragesprache, die sowohl für Listen als auch als Daten-bankabfragesprache oder zur Abfrage von XML-Dateien verwendet werden kann. Dabei ist die Sprache direktin C# integriert, es müssen also nicht irgendwelche Strings zur Abfrage zusammengesetzt werden.

3Datenmodellierung im Entity Framework

54 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 55: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.2 LINQ

Abbildung 6.6: LINQ

4Im Gegensatz zu z.B. SQL wird die Abfrage nicht als String übergeben, sondern ist nativ in den Quellcodeintegriert. Das bietet z.B. den Vorteil, dass der .NET-Sprachcompiler die Abfrage direkt auf Syntaxfehler prüfenkann, um spätere Laufzeitfehler zu vermeiden. Die Syntax von LINQ erinnert sehr an die SQL-Abfragesprachemit Befehlen wie z.B. select, from, where und orderby. LINQ �ndet im Wesentlichen die folgenden vier Anwen-dungsgebiete:

• LINQ-to-Dataset: bietet Abfragemöglichkeiten für Objekte, die in einem DataSet-Objekt gespeichert sind.

• LINQ-to-SQL (DLINQ): übersetzt die native LINQ-Abfrage in SQL und sendet diese an eine relationaleDatenbank. Von der Datenbank gelieferte Ergebnisse werden dann entsprechend wieder in ein Objektmo-dell übersetzt.

• LINQ-to-XML (XLINQ): bietet Zugri� auf XML-Dokumente (DOM-Manipulation)

• LINQ-to-Objects: bietet Abfragemöglichkeiten für .NET-Objektmengen, die in Listen gespeichert sind,z.B. in List, Array oder Dictionary (müssen das Interface IEnumerable bzw. IEnumerable<T> implemen-tieren.

Die wesentlichen LINQ-Klassen werden in den Namensräumen System.Linq.*, System.Data.Linq sowie Sys-tem.Xml.Linq zur Verfügung gestellt.

Darüber hinaus gibt es zahlreiche LINQ-Projekte anderer Hersteller und Anbieter, um nur einige der zahlreichenBeispiele zu nennen: LINQ-to-MySQL, LINQ-to-LDAP, LINQ-to-Flickr, LINQ-to-Amazon, . . .

Listing 6.3: LINQ-Beispiele

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var lowNums =

from n in numbers

where n < 5

select n;

List<Product> products = GetProductList();

var soldOutProducts =

from p in products

where p.UnitsInStock == 0

select p;

string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };

var upperLowerWords =

from w in words

select new { Upper = w.ToUpper(), Lower = w.ToLower() };

4.Net Glossar

55 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 56: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.3 Datenbindung

6.3 Datenbindung

Der Begri� Datenbindung beschäftigt sich mit der Problematik Änderungen an den Daten einer Datenquelle inSteuerelementen eines Formulars anzuzeigen und vorzunehmen.

Die Datenbindung ist grundsätzlich ein automatisches Verfahren, um für beliebige Steuerelemente im FormularEigenschaften festzulegen, auf die zur Laufzeit zugegri�en werden kann.

5Die Änderungsbenachrichtigung ist einer der wichtigsten Bestandteile der Windows Forms-Datenbindung. Umsicherzustellen, dass die Datenquelle und die gebundenen Steuerelemente immer über aktuelle Daten verfügen,müssen Sie die Änderungsbenachrichtigung für die Datenbindung hinzufügen. Der Hauptzweck liegt darin, dassgebundene Steuerelemente über Änderungen an der zugehörigen Datenquelle benachrichtigt werden und dieDatenquelle über Änderungen benachrichtigt wird, die an den gebundenen Eigenschaften eines Steuerelementsvorgenommen wurden.

6.3.1 Datenbindung in WinForm

6Herkömmlicherweise wurde die Datenbindung in Anwendungen verwendet, um in Datenbanken gespeicherteDaten nutzen zu können. Mit der Datenbindung von Windows Forms können Sie auf Daten zugreifen, die inDatenbanken sowie in anderen Strukturen, z. B. Arrays oder Au�istungen, enthalten sind.

Die folgende Liste enthält die Strukturen, an die Sie in Windows Forms eine Bindung vornehmen können.

• BindingSource ist die häu�gste Windows Forms-Datenquelle. Sie fungiert als Proxy zwischen einer Da-tenquelle und Windows Forms-Steuerelementen. Das allgemeine Verwendungsmuster von BindingSourcesieht vor, Steuerelemente an BindingSource und BindingSource an die Datenquelle (z. B. eine ADO.NET-Datenquelle oder ein Geschäftsobjekt) zu binden.

• Windows Forms unterstützt in Objektinstanzen mithilfe des Binding-Typs die Datenbindung von Steue-relementeigenschaften an ö�entliche Eigenschaften. Auÿerdem unterstützt Windows Forms die Bindunglistenbasierter Steuerelemente, z. B. ListControl, an eine Objektinstanz, wenn BindingSource verwendetwird.

• Damit eine Liste als Datenquelle verwendet werden kann, muss sie die IList-Schnittstelle implementieren.

• ADO.NET:

� DataColumn ist der wichtigste Baustein von DataTable, da eine Tabelle sich aus einer Reihe vonSpalten zusammensetzt. Jedes DataColumn-Objekt verfügt über eine DataType-Eigenschaft, die dieArt der Daten bestimmt, die in der Spalte enthalten sind (in einer Tabelle zur Beschreibung vonAutos z. B. die Marke der Autos). Ein Steuerelement (bei einem TextBox-Steuerelement z. B. dieText-Eigenschaft) kann mithilfe der einfachen Bindung an eine Spalte in einer Datentabelle gebundenwerden.

� DataTable ist die Darstellung einer Tabelle mit Zeilen und Spalten, wie sie in ADO.NET verwen-det wird. Eine Datentabelle umfasst zwei Au�istungen: DataColumn für die Datenspalten in einerbestimmten Tabelle (dadurch wird letztlich bestimmt, welche Arten von Daten in diese Tabelleeingegeben werden können) sowie DataRow für die Datenzeilen in einer bestimmten Tabelle. EinSteuerelement kann mithilfe der komplexen Bindung an die Daten in einer Datentabelle gebundenwerden (beispielsweise bei Bindung des DataGridView-Steuerelements an eine Datentabelle). Bei derBindung an DataTable erstellen Sie tatsächlich eine Bindung an die Standardansicht dieser Tabelle.

� DataView ist eine angepasste Ansicht einer einzelnen Datentabelle, die ge�ltert oder sortiert werdenkann. Eine Datenansicht ist eine Momentaufnahme der Daten, die von komplex gebundenen Steuer-elementen verwendet werden. Für die Daten in einer Datenansicht können Sie einfache oder komplexeBindungen vornehmen. Dabei sollten Sie jedoch beachten, dass Sie nicht Bindungen an eine saubere,aktualisierende Datenquelle, sondern an ein festes Abbild der Daten vornehmen.

� DataSet ist eine Au�istung von Tabellen, Beziehungen und Einschränkungen der Daten in einer Da-tenbank. Sie können Daten innerhalb eines DataSets mithilfe der einfachen oder komplexen Bindungbinden. Stellen Sie jedoch sicher, dass Sie an den standardmäÿigen DataViewManager für DataSetbinden (siehe dazu den nächsten Gliederungspunkt).

5Änderungsbenachrichtigung in der Windows Forms-Datenbindung6Von Windows Forms unterstützte Datenquellen

56 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 57: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.3 Datenbindung

� DataViewManager ist eine angepasste Ansicht des gesamten DataSet. Sie entspricht grundsätzlichDataView, enthält aber zusätzlich Beziehungen. Mit einer DataViewSettings-Au�istung können Siestandardmäÿige Filter- und Sortieroptionen für beliebige Ansichten festlegen, die DataViewManagerfür eine bestimmte Tabelle bereitstellt.

Die Bindung eines Datagrids DataGrid1 an eine Tabelle Customers eines Datasets dsCustomers ist nun nurnoch ein Einzeiler:

Listing 6.4: Datenbindung an ein DataGrid

DataGrid1.SetDataBinding(dsCustomers, "Customers");

6.3.2 Datenbindung in WPF

7Elemente können an Daten aus einer Vielzahl von Datenquellen in Form von common language runtime (CLR)-Objekten und XML gebunden werden. ContentControl wie Button und ItemsControl wie ListBox und ListViewverfügen über integrierte Funktionen, die eine �exible Formatierung von einzelnen Datenelementen oder Auf-listungen von Datenelementen ermöglichen. Sortier-, Filter- und Gruppenansichten können übergreifend für dieDaten generiert werden.

Unabhängig davon, was für ein Element Sie binden und welcher Art die Datenquelle ist, geschieht die Bindungimmer nach dem in Abbildung 6.7 gezeigten Modell:

Abbildung 6.7: Datenbindung in WPF

Wie in der Abbildung dargestellt, ist Datenbindung die Brücke zwischen dem Bindungsziel und der Bindungs-quelle. Eine Bindung besteht in der Regel aus diesen vier Komponenten: einem Bindungszielobjekt, einer Zielei-genschaft, einer Bindungsquelle sowie einem Pfad zum Wert in der Bindungsquelle, die verwendet werden soll.Angenommen, Sie möchten den Inhalt von TextBox an die Name-Eigenschaft eines Employee-Objekts binden,dann ist Ihr Zielobjekt TextBox, die Zieleigenschaft die Text-Eigenschaft, der zu verwendende Wert Name unddas Quellobjekt das Employee-Objekt.

Über die Mode-Eigenschaft des Binding-Objekts können Sie festlegen, ob und wie Änderungen festgeschriebenwerden.

Abbildung 6.8: Datenbindungs�uss

OneWay-Bindung bewirkt, dass bei Änderungen an der Quelleigenschaft die Zieleigenschaft automatisch ak-tualisiert wird, ohne dass Änderungen an der Zieleigenschaft an die Quelleigenschaft zurückübertragen werden.TwoWay-Bindung bewirkt, dass bei Änderungen an der Quelleigenschaft bzw. der Zieleigenschaft die jeweilsandere automatisch aktualisiert wird. OneWayToSource stellt die Umkehrung der OneWay-Bindung dar: dieQuelleigenschaft wird aktualisiert, wenn sich die Zieleigenschaft ändert.

Folgendes Beispiel zeigt, wie ein Windows Presentation Foundation (WPF)ListBox-Steuerelement an einADO.NETDataSet gebunden wird.

7Übersicht über Datenbindung

57 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 58: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

6.4 Aufgaben

Listing 6.5: Datenbindung in WPF

DataSet myDataSet;

private void OnInit(object sender, EventArgs e)

{

//Verbindung herstellen ...

OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM BookTable;", conn);

myDataSet = new DataSet();

adapter.Fill(myDataSet, "BookTable");

myListBox.DataContext = myDataSet;

...

}

XAML:

<ListBox Name="myListBox" Height="200"

ItemsSource="{Binding Path=BookTable}"

ItemTemplate ="{StaticResource BookItemTemplate}"/>

<StackPanel.Resources>

<c:IntColorConverter x:Key="MyConverter"/>

<DataTemplate x:Key="BookItemTemplate">

<Grid>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="250" />

<ColumnDefinition Width="100" />

<ColumnDefinition Width="*"/>

</Grid.ColumnDefinitions>

<TextBlock Text="{Binding Path=Title}" Grid.Column="0"

FontWeight="Bold" />

<TextBlock Text="{Binding Path=ISBN}" Grid.Column="1" />

<TextBlock Grid.Column="2" Text="{Binding Path=NumPages}"

Background="{Binding Path=NumPages,

Converter={StaticResource MyConverter}}"/>

</Grid>

</DataTemplate>

</StackPanel.Resources>

6.3.3 Datenbindung in Asp.Net

Die serverseitige Datenbindung in WebForms erfolgt analog zu WinForm:

Listing 6.6: Serverseitige Datenbindung in Asp.Net

Collection<StockInfo> stocks = GetLatestQuotes(...);

DataGrid1.DataSource = stocks;

DataGrid1.DataBind();

Mit AJAX gibt es auch die Möglichkeit einer clientseitigen Datenbindung.

6.4 Aufgaben

Die Aufgaben stammen von .Net AT JKU.

Aufgabe 6.1 Zugri�sart bei gleichzeitigem Zugri�. In einer Anwendung möchten viele Benutzer gleichzeitigdie Daten einer Tabelle (z.B. Produkte) lesen. Verwenden Sie eher einen verbindungsorientierten oder einenverbindungslosen Zugri�?

Aufgabe 6.2 Ändern von Daten. Werden die Änderungen der Daten bei einem verbindungslosen Zugri� auchin der Datenbank automatisch durchgeführt? Falls ja, wodurch, falls nein, wie können sie trotzdem durchgeführt

Aufgabe 6.3 Schreiben Sie ein Kommandozeilenprogramm ExportXML, welches alle Daten und Tabellen einerDatenbank ausliest und in einer XML-Datei abspeichert. Der erste Kommandozeilenparameter ist der Name derVerbindung und der zweite Paramter gibt den Namen der zu erzeugenden XML-Datei an. Die exportierte XML-Datei soll auch das Schema der Datenbank enthalten.

58 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 59: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7 Webentwicklung

Abbildung 7.1: Asp.Net Überblick

Aus den ursprünglichen skriptbasierten ASP-Seiten, die ähnlich wie frühere PHP-Seiten aussahen, häu�g sogarmit VB-Skript nur browserspezi�sch arbeiteten, ist mittlerweile eine gute Entwicklungsmöglichkeit fürs Webentwachsen. Selbst die erste Version von Asp.Net war schon mächtig. Man gestaltet seine Webseiten wie beiWinForm durch Drag&Drop vorhandener Steuerelemente, kann eigene Komponenten implementieren und überCodeBehind programmiert man die zugehörigen PostBacks. Prinzipiell wird immer an die gleiche Seite zurück-geschickt. Das Konzept bei Asp.Net ist ähnlich zu WinForm: ereignisorientiert. Über Server.Transfer kann manzu einer anderen Seite navigieren. Die Steuerelemente ermöglichen einen einfachen Anschluss an Datenstruk-turen und Datenbanken und hinter der Ober�äche arbeitet man mit C# � wie gewohnt objektorientiert. Inder ersten Version von Asp.Net waren die Seiten noch nicht barrierefrei und ein einheitliches Look&Feel warnur umständlich selbst zu programmieren. Durch die Einführung von sogenannten Masterpages und eine bes-sere HTML-Codeerzeugung sind diese beiden Probleme gut gelöst. Die aktuelle Version setzt auf JSON undunterstützt nun auch die Entwicklung von AJAX-basierten Seiten gut. Asp.Net implementiert nicht das MVC-Pattern, Modell und Controller sind typischerweise miteinander vermischt. Für gröÿere Applikationen ist MVCjedoch aktuell State of the Art, so dass mit .Net 4.0 nun auch eine Klassenbibliothek hierfür existiert, die aufAsp.Net aufsetzt.

59

Page 60: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.1 Asp.Net

7.1 Asp.Net

Das ASP.NET-Framework für Seiten und Steuerelemente ist ein Programmierframework, das auf einem Webser-ver ausgeführt wird, um ASP.NET-Webseiten dynamisch zu erstellen und zu rendern. ASP.NET-Webseitenkönnen von jedem Browser oder Clientgerät angefordert werden, und ASP.NET rendert Markup (wie HTML)für den anfordernden Browser. Grundsätzlich kann dieselbe Seite für mehrere Browser verwenden werden, daASP.NET das Markup dem anfordernden Browser entsprechend rendert. ASP.NET unterstützt mobile Steuer-elemente für webkompatible Geräte, wie Handys, Taschencomputer und persönliche digitale Assistenten (PDAs,Personal Digital Assistants). ASP.NET-Webseiten sind vollkommen objektorientiert. Innerhalb von ASP.NET-Webseiten können Sie für die Arbeit mit HTML-Elementen Eigenschaften, Methoden und Ereignisse verwenden.Das ASP.NET-Seitengerüst entfernt die Implementierungsdetails der Trennung von Client und Server, die denwebbasierten Anwendungen eigen ist, indem ein vereinheitlichtes Modell für das Reagieren auf Clientereignisseim Code verwendet wird, das auf dem Server ausgeführt wird. Das Gerüst verwaltet während des Lebenszy-klus der Seitenverarbeitung auch automatisch den Status einer Seite und der Steuerelemente auf der Seite. DasASP.NET-Framework für Seiten und Steuerelemente bietet durch verschiedene Designs die Möglichkeit, dasGesamterscheinungsbild und Verhalten einer Website zu steuern. Sie können Designs und Skins de�nieren unddann auf Seitenebene oder Steuerelementebene anwenden.

7.1.1 Masterseiten

Neben Designs können Sie auch Masterseiten de�nieren, um ein konsistentes Layout der Anwendungsseiten zuerstellen. In einer Masterseite werden das Layout und das Standardverhalten aller Seiten (oder einer Gruppevon Seiten) der Anwendung festgelegt. Sie können anschlieÿend einzelne Inhaltsseiten erstellen, die den spe-zi�schen Inhalt für die darzustellende Seite enthalten. Beim Anfordern einer Inhaltsseite durch den Benutzerwerden Inhaltsseite und Masterseite zusammengeführt. Das Ergebnis ist eine Kombination aus dem Inhalt derInhaltsseite und dem Layout der Masterseite. Mit ASP.NET-Masterseiten können Sie ein Seitenlayout erstellen(eine Masterseite), das Sie mit ausgewählten oder allen Seiten (Inhaltsseiten) in der Website verwenden kön-nen. Masterseiten können ein nützliches Hilfsmittel beim Erstellen eines einheitlichen Erscheinungsbilds einerSite sein. Die Themen in diesem Abschnitt führen in Masterseiten ein und beschreiben, wie diese erstellt undverwaltet werden.

7.1.2 Lebenszyklus einer Seite

Im Allgemeinen durchläuft eine Asp.Net-Seite die folgenden Phasen:

• Seitenanforderung: Der Lebenszyklus einer Seite beginnt, nachdem die Seite angefordert wurde. Wenn einBenutzer die Seite anfordert, prüft ASP.NET, ob die Seite analysiert und kompiliert werden muss (Beginndes Lebenszyklus einer Seite) oder ob eine zwischengespeicherte Version der Seite zurückgesendet werdenkann, ohne dass die Seite verarbeitet werden muss.

• Starten: In der Startphase werden Seiteneigenschaften wie Request und Response festgelegt. In dieserPhase wird auch bestimmt, ob es sich bei der Anforderung um ein Postback oder um eine neue Anforderunghandelt, und die IsPostBack-Eigenschaft wird entsprechend festgelegt. Auÿerdem wird in der Startphasedie UICulture-Eigenschaft festgelegt.

• Seiteninitialisierung: Während der Seiteninitialisierung sind die Steuerelemente auf der Seite verfügbar,und die UniqueID-Eigenschaft jedes Steuerelements wird festgelegt. Ebenso werden gegebenenfalls Designsfür die Seite übernommen. Wenn es sich bei der aktuellen Anforderung um ein Postback handelt, sind diePostbackdaten noch nicht geladen, und die Eigenschaftenwerte des Steuerelements wurden noch nicht mitden Werten aus dem Ansichtszustand wiederhergestellt.

• Laden:Wenn es sich bei der aktuellen Anforderung um ein Postback handelt, werden die Steuerelemen-teigenschaften während der Ladephase mithilfe von Informationen aus dem Ansichtszustand und demSteuerelementzustand wiederhergestellt.

• Validierung: Während der Validierung wird die Validate-Methode aller Validierungssteuerelemente aufge-rufen, die die IsValid-Eigenschaft einzelner Validierungssteuerelemente sowie der Seite festlegt.

• Behandlung von Postbackereignissen: Wenn es sich bei der Anforderung um ein Postback handelt, werdensämtliche Ereignishandler aufgerufen.

60 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 61: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.1 Asp.Net

• Rendern: Vor der Darstellung werden der Ansichtszustand für die Seite sowie alle Steuerelemente ge-speichert. Während der Wiedergabephase wird von der Seite die Render-Methode für jedes Steuerele-ment aufgerufen. Dadurch wird ein Textschreiber bereitgestellt, der eine Ausgabe in OutputStream derResponse-Eigenschaft der Seite schreibt.

• Entladen: Nachdem die Seite vollständig dargestellt und an den Client gesendet wurde und daher verwor-fen werden kann, wird der Entladevorgang aufgerufen. An dieser Stelle werden Seiteneigenschaften wieResponse und Request entladen, und es werden sämtliche Bereinigungsoperationen ausgeführt.

7.1.3 Ereignismodell für ASP.NET-Webserversteuerelemente

Ein wichtiges Feature von ASP.NET ist die Möglichkeit, Webseiten mit einem ereignisbasierten Modell zuprogrammieren, wie es auch für Clientanwendungen verwendet wird. Ein einfaches Beispiel: Sie fügen einerASP.NET-Webseite eine Schalt�äche hinzu und schreiben anschlieÿend einen Ereignishandler für das Klicker-eignis der Schalt�äche. Dieses Vorgehen kennen wir zwar schon von Webseiten her, die ausschlieÿlich mit Client-skript arbeiten (wobei das onclick-Ereignis mit dynamischem HTML behandelt wird). ASP.NET überträgt diesesModell jedoch auf die serverbasierte Verarbeitung. Durch ASP.NET-Serversteuerelemente ausgelöste Ereignisseunterscheiden sich geringfügig von Ereignissen in herkömmlichen HTML-Seiten oder clientbasierten Webanwen-dungen. Der Unterschied entsteht hauptsächlich durch die Trennung des Ereignisses selbst von der Stelle, wo dasEreignis behandelt wird. In clientbasierten Anwendungen werden Ereignisse auf dem Client ausgelöst und be-handelt. In ASP.NET-Webseiten haben Ereignisse, die Serversteuerelementen zugeordnet sind, ihren Ursprungauf dem Client (Browser). Sie werden jedoch von der ASP.NET-Seite auf dem Webserver behandelt. Für aufdem Client ausgelöste Ereignisse benötigt das Ereignismodell für ASP.NET-Websteuerelemente auf dem Clienterfasste Ereignisinformationen sowie eine Ereignismeldung, die über HTTP Post an den Server übertragen wird.Die Seite muss die Meldung interpretieren, um festzustellen, welches Ereignis aufgetreten ist, und muss danndie entsprechende Methode im Code auf dem Server aufrufen, um das Ereignis zu behandeln. ASP.NET behan-delt alle Aufgaben, die beim Erfassen, Senden und Interpretieren des Ereignisses durchgeführt werden. BeimErstellen von Ereignishandlern in einer ASP.NET-Webseite müssen Sie sich normalerweise nicht darum küm-mern, wie die Ereignisinformationen erfasst und dem Code zur Verfügung gestellt werden. Stattdessen könnenSie Ereignishandler wie in einem herkömmlichen Clientformular erstellen. Allerdings gibt es einige Aspekte beider Ereignisbehandlung in ASP.NET-Webseiten, die Sie beachten sollten. Ereignissatz für Serversteuerelementeund Seiten

Serversteuerelementereignisse und PostBack:Da die meisten ASP.NET-Serversteuerelementereignisse zur Verarbeitung einen Roundtrip zum Server erfordern,können sie die Leistung der Seite beeinträchtigen.

Daher bieten Serversteuerelemente einen eingeschränkten Satz an Ereignissen, die normalerweise auf Klicker-eignisse beschränkt sind. Einige Serversteuerelemente unterstützen Änderungsereignisse. Zum Beispiel löst dasCheckBox-Webserversteuerelement im Servercode ein CheckedChanged-Ereignis aus, wenn der Benutzer aufdas Kontrollkästchen klickt. Einige Serversteuerelemente unterstützen abstraktere Ereignisse. Das Calendar-Webserversteuerelement z. B. löst ein SelectionChanged-Ereignis aus, das eine abstraktere Version des Klicker-eignisses ist. Ereignisse, die häu�g auftreten (und ohne das Wissen des Benutzers ausgelöst werden können), wiedas onmouseover-Ereignis, werden von Serversteuerelementen nicht unterstützt. ASP.NET-Serversteuerelementekönnen für solche Ereignisse aber clientseitige Handler aufrufen. Dies wird weiter unten unter Ereignismodellfür ASP.NET-Webserversteuerelemente erklärt. Steuerelemente und die Seite selbst lösen bei jedem Verar-beitungsschritt ebenfalls Lebenszyklusereignisse aus, z. B. Init, Load und PreRender. Sie können diese Le-benszyklusereignisse in der Anwendung nutzen. Sie können zum Beispiel im Load-Ereignis einer Seite Stan-dardwerte für Steuerelemente festlegen. Ereignisargumente Serverbasierte Ereignisse von ASP.NET-Seiten und-Steuerelementen folgen einem .NET Framework-Standardmuster für Ereignishandlermethoden. Alle Ereignisseübergeben zwei Argumente: ein Objekt, das das Ereignis auslösende Objekt darstellt, und ein Ereignisobjekt, dasereignisspezi�sche Informationen enthält. Das zweite Argument ist normalerweise vom Typ EventArgs, aber beimanchen Steuerelementen ist es von einem Typ, der für dieses Steuerelement spezi�sch ist. Für ein ImageButton-Webserversteuerelement ist z. B. das zweite Argument vom Typ ImageClickEventArgs. Es enthält Informatio-nen über die Koordinaten des Punkts, auf den der Benutzer geklickt hat. Ereignisse für Seiten (beispielsweisedas Load-Ereignis der Seite) akzeptieren die beiden Standardargumente, in denen dann allerdings keine Werteübergeben werden. Postbackereignisse und Nicht-Postbackereignisse in Serversteuerelementen In Serversteue-relementen führen bestimmte Ereignisse (normalerweise Klickereignisse) dazu, dass die Seite unmittelbar anden Server zurückgesendet wird. Änderungsereignisse in HTML-Serversteuerelementen und Webserversteuer-elementen, wie das TextBox-Steuerelement, verursachen kein sofortiges Postback. Stattdessen werden sie bei

61 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 62: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.1 Asp.Net

dem nächsten Postback ausgelöst. Falls dies vom Browser unterstützt wird, können Validierungssteuerelemen-te Benutzereingaben mit Clientskript prüfen � ohne einen Roundtrip zum Server. Ausführliche Informationen�nden Sie unter Überprüfen der Benutzereingabe in ASP.NET-Webseiten. Nach dem Zurücksenden der Seitewerden die Initialisierungsereignisse der Seite (Page_Init und Page_Load) ausgelöst, und anschlieÿend werdenSteuerelementereignisse verarbeitet. Sie sollten keine Anwendungslogik erstellen, die von in einer bestimmtenReihenfolge ausgelösten Änderungsereignissen abhängt, es sei denn, Sie verfügen über fundierte Kenntnisseder Seitenereignisverarbeitung. Ausführliche Informationen �nden Sie unter Übersicht über den Lebenszyklusvon ASP.NET-Seiten. Falls es für Ihre Anwendung sinnvoll ist, können Sie angeben, dass Änderungsereignisseeinen Postback der Seite auslösen. Webserversteuerelemente, die ein Änderungsereignis unterstützen, enthalteneine AutoPostBack-Eigenschaft. Wenn diese Eigenschaft auf true festgelegt wurde, löst das Änderungsereig-nis des Steuerelements sofort ein Postback der Seite aus, ohne dass auf ein Klickereignis gewartet wird. DasCheckedChanged-Ereignis eines CheckBox-Steuerelements löst z. B. standardmäÿig keine Übermittlung der Seiteaus. Wenn Sie aber die AutoPostBack-Eigenschaft des Steuerelements auf true festlegen, wird die Seite zur Ver-arbeitung an den Server gesendet, sobald ein Benutzer auf das Kontrollkästchen klickt. Damit die AutoPostBack-Eigenschaft ordnungsgemäÿ funktioniert, muss der Browser des Benutzers Skripts zulassen. Meistens ist das dieStandardeinstellung. Allerdings deaktivieren manche Benutzer Skripts aus Sicherheitsgründen. Ausführliche In-formationen �nden Sie unter Clientskript in ASP.NET-Webseiten. Weitergeleitete Ereignisse: Zusätzlich zuSeiten- und Steuerelementereignissen bietet ASP.NET Möglichkeiten zum Arbeiten mit Lebenszyklusereignis-sen, die ausgelöst werden, wenn die Anwendung gestartet oder beendet wird (Anwendungsereignisse) oder wenndie Sitzung eines einzelnen Benutzers gestartet oder beendet wird (Sitzungsereignisse):

• Anwendungsereignisse werden für alle Anforderungen einer Anwendung ausgelöst. Zum Beispiel wirddas BeginRequest-Ereignis des HttpApplication-Objekts (Application/_BeginRequest) ausgelöst, wenneine ASP.NET-Webseite oder ein XML-Webdienst in der Anwendung angefordert wird. Mit diesem Er-eignis können Sie Ressourcen initialisieren, die für die Anforderungen an die Anwendung verwendetwerden. Ein entsprechendes Ereignis, das EndRequest-Ereignis des HttpApplication-Objekts (Applica-tion/_EndRequest), gibt Ihnen die Möglichkeit, für die Anforderung verwendete Ressourcen zu schlieÿenoder freizugeben.

• Sitzungsereignisse ähneln Anwendungsereignissen (es gibt ein Start-Ereignis und ein End-Ereignis). Siewerden jedoch für jede einzelne Sitzung innerhalb der Anwendung ausgelöst. Eine Sitzung beginnt, wennein Benutzer eine Seite zum ersten Mal von der Anwendung anfordert, und endet, wenn die Anwendungdie Sitzung explizit schlieÿt oder wenn die Sitzung das Zeitlimit überschreitet.

Das Session_End-Ereignis wird nicht unter allen Umständen ausgelöst.

7.1.4 Zustandsverwaltung

Bei jeder Bereitstellung einer Webseite auf dem Server wird eine neue Instanz der Webseitenklasse erstellt.Bei der herkömmlichen Webprogrammierung würde dies normalerweise bedeuten, dass alle zur Seite gehören-den Informationen und die Steuerelemente auf der Seite bei jeder Client-Server-Schleife (Roundtrip) verlorengingen. Wenn z. B. ein Benutzer Informationen in ein Textfeld eingibt, würden diese Informationen währendder Schleife vom Browser oder Clientgerät zum Server verloren gehen. Um diese inhärente Einschränkung derherkömmlichen Webprogrammierung zu überwinden, enthält ASP.NET eine Reihe von Optionen, mit denenSie die Daten sowohl auf Seitenbasis als auch auf Anwendungsbasis bewahren können. Es handelt sich um fol-gende Features: Ansichtszustand, Steuerelementzustand, Ausgeblendete Felder, Cookies, Abfragezeichenfolgen,Anwendungszustand, Sitzungszustand, Pro�leigenschaften. Ansichtszustand, Steuerelementzustand, ausgeblen-dete Felder, Cookies und Abfragezeichenfolgen speichern die Daten jeweils auf dem Client. Anwendungszustand,Sitzungszustand und Pro�leigenschaften hingegen speichern die Daten im Speicher des Servers. Jede Option hatje nach Einsatzszenario unterschiedliche Vor- und Nachteile.

In den folgenden Abschnitten werden Optionen für die Zustandsverwaltung beschrieben, für die das Speichernder Informationen entweder in der Seite oder auf dem Client erforderlich ist. Bei diesen Optionen bleiben dieInformationen auf dem Server zwischen den Schleifen nicht erhalten.

Ansichtszustand:Die ViewState-Eigenschaft enthält ein Dictionary-Objekt, um Werte zwischen mehreren An-forderungen derselben Seite beizubehalten. Dies ist die Standardmethode, die die Seite zum Beibehalten vonSeitenwerten und Werten für Steuerelementeigenschaften zwischen den Schleifen verwendet. Bei der Verarbei-tung der Seite wird der aktuelle Status der Seite und der Steuerelemente in eine Zeichenfolge gehasht und inder Seite als ausgeblendetes Feld gespeichert. Wenn die in der ViewState-Eigenschaft gespeicherte Datenmenge

62 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 63: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.1 Asp.Net

den in der MaxPageStateFieldLength-Eigenschaft angegebenen Wert übersteigt, werden zur Speicherung meh-rere ausgeblendete Felder verwendet. Beim Zurücksenden der Seite an den Server wird die Zeichenfolge für denAnsichtszustand bei der Seiteninitialisierung analysiert, und die Eigenschafteninformationen in der Seite wer-den wiederhergestellt. Die Möglichkeiten von Cookies und URLs stehen Ihnen in Asp.Net auch zur Verfügung.ASP.NET bietet Ihnen vielfältige Möglichkeiten zum Verwalten von Zustandsinformationen auf dem Server, so-dass die Informationen nicht für längere Zeit auf dem Client gespeichert werden müssen. Mit der serverbasiertenZustandsverwaltung können Sie die Informationsmenge verringern, die zur Beibehaltung des Zustands an denClient gesendet wird. Dabei werden jedoch kostbare Ressourcen auf dem Server verbraucht. In den folgendenAbschnitten werden drei Features für die serverbasierte Zustandsverwaltung beschrieben: Anwendungszustand,Sitzungszustand und Pro�leigenschaften.

Anwendungszustand: In ASP.NET können Sie mithilfe des Anwendungszustands, der eine Instanz derHttpApplicationState-Klasse ist, Werte für jede aktive Webanwendung speichern. Der Anwendungszustandist ein globaler Speichermechanismus, auf den von allen Seiten der Webanwendung aus zugegri�en werdenkann. Der Anwendungszustand ist daher nützlich, um Informationen zu speichern, die zwischen Serverschlei-fen und zwischen Seitenanforderungen beibehalten werden müssen. Der Anwendungszustand wird in einemSchlüssel/Wert-Wörterbuch gespeichert, das während jeder Anforderung einer bestimmten URL erstellt wird.Sie können dieser Struktur die anwendungsspezi�schen Informationen hinzufügen, um sie zwischen Seitenan-forderungen zu speichern. Nachdem Sie dem Anwendungszustand die anwendungsspezi�schen Informationenhinzugefügt haben, wird er vom Server verwaltet. Verwendungsempfehlungen �nden Sie unter Empfehlungenzur ASP.NET-Zustandsverwaltung. Sitzungszustand: In ASP.NET können Sie mithilfe des Sitzungszustands,der eine Instanz der HttpSessionState-Klasse ist, Werte für jede aktive Webanwendungssitzung speichern. EineÜbersicht �nden Sie unter Übersicht über den ASP.NET-Sitzungszustand.

Der Sitzungszustand ist ähnlich dem Anwendungszustand, mit der Ausnahme, dass sich der Gültigkeitsbereichauf die aktuelle Browsersitzung erstreckt. Wenn die Anwendung von verschiedenen Benutzern verwendet wird,hat jede Benutzersitzung einen anderen Sitzungszustand. Wenn ein Benutzer die Anwendung verlässt und späterwieder damit arbeitet, hat die zweite Benutzersitzung einen anderen Sitzungszustand wie die erste. ASP.NETenthält ein Feature mit dem Namen Pro�leigenschaften, mit dem Sie benutzerspezi�sche Daten speichern kön-nen. Dieses Feature ähnelt dem Sitzungszustand, mit dem Unterschied, dass Pro�ldaten nicht verloren gehen,wenn eine Benutzersitzung abläuft. Das Pro�leigenschaftenfeature verwendet ein ASP.NET-Pro�l, das in einempersistenten Format gespeichert und einem bestimmten Benutzer zugeordnet wird. Über das ASP.NET-Pro�lkönnen Sie problemlos Benutzerinformationen verwalten, ohne eine eigene Datenbank erstellen und p�egen zumüssen. Auÿerdem stellt das Pro�l die Benutzerinformationen mithilfe einer stark typisierten API zur Verfü-gung, auf die Sie von überall in Ihrer Anwendung zugreifen können. Sie können Objekte eines beliebigen Typsim Pro�l speichern. Das ASP.NET-Pro�lfeature stellt ein generisches Speichersystem dar, mit dem Sie praktischalle Arten von Daten de�nieren und verwalten können. Trotzdem können Sie damit Daten typsicher zur Ver-fügung stellen. Um Pro�leigenschaften zu verwenden, müssen Sie einen Pro�lanbieter kon�gurieren. ASP.NETenthält eine SqlPro�leProvider-Klasse, mit der Sie Pro�ldaten in einer SQL-Datenbank speichern können. Siekönnen jedoch auch eine eigene Pro�lanbieterklasse erstellen, die Pro�ldaten in einem benutzerde�nierten For-mat und mit einem benutzerde�nierten Speichermechanismus speichert (z. B. in einer XML-Datei und sogarin einem Webdienst). Da in Pro�leigenschaften gespeicherte Daten nicht im Anwendungsspeicher gespeichertwerden, bleiben sie auch bei Neustarts der Internetinformationsdienste (IIS) und des Workerprozesses vollstän-dig erhalten. Auÿerdem können Pro�leigenschaften auch über mehre Prozesse hinweg erhalten bleiben, z. B. ineiner Webfarm oder in einem Webgarten.

7.1.5 Kon�guration

Mit den Features des ASP.NET-Kon�gurationssystems können Sie alle ASP.NET-Anwendungen auf einem kom-pletten Server, eine einzelne ASP.NET-Anwendung, einzelne Seiten oder Anwendungsunterverzeichnisse kon�gu-rieren. Zu den kon�gurierbaren Features gehören Authenti�zierungsmodi, Seitenzwischenspeicherung, Compile-roptionen, benutzerde�nierte Fehler, Debug- und Ablaufverfolgungsoptionen usw. Eine ausführliche Darstellung�nden Sie unter Verwalten von ASP.NET-Websites. Schauen Sie sich die Kon�gurationsdateien Maschine.con�gund Web.con�g an.

Aufgabe 7.1 Arbeiten Sie den Artikel Con�guration Overview: ASP.NET im Anhang ab Seite 47 durch. Wel-che Kon�gurationsmöglichkeiten bietet Asp.Net?

Aufgabe 7.2 Arbeiten Sie die Unterschiede und die Gemeinsamkeiten zwischen ASP.Net und PHP heraus.

63 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 64: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.2 Asp.Net MVC

7.1.6 Ajax

Ajax-Entwicklung ist mit VS2010 direkt in die IDE eingebunden. Die ASP.NET Ajax Library enthält vielfältigeControls und basiert auf der weit verbreiteten Bibliothek jQuery. Sie können natürlich, wie in EWA eingeführt,eigene clientseitige Scripts erstellen und in Ihrere Applikation verwenden. Um die Ajax-Funktionalität einfachnutzen zu können, müssen Sie ein ScriptManager Control auf Ihre Seite platzieren. Mit diesem Control werdenautomatisch die benötigten Skriptbibliotheken auf den Client in der aktuellen Version geladen.

Mithilfe eines UpdatePanel können Sie die Teile der Seite begrenzen, die sich ändern, wenn Benutzeraktionennormalerweise ein Postback verursachen würden.

Häu�g werden mit Ajax Daten eines WCF-Services angezeigt. Die Theorie zu WCF �nden Sie in Abschnitt8 auf Seite 67. 1In VS2010 können Sie ein neues Projekt auf Basis des Ajax-enabled WCF erzeugen und dortfolgenden Code eingeben:

Listing 7.1: Ajax-enabled WCF

[ServiceContract(Namespace = "http://www.yourcompany.com/ApplicationName")]

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class CustomerService

{

[OperationContract]

public List<Customer> GetCustomers(int numberToFetch)

{

using (NorthwindDataContext context = new NorthwindDataContext())

{

return context.Customers.Take(numberToFetch).ToList();

}

}

}

Speichern Sie dies als Service.svc ab.

Mit dem DataView-Control können Sie diese Daten nun einfach anzeigen.

<!DOCTYPE ...>

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

<head>

<script type="text/javascript" src="Scripts/MicrosoftAjax.debug.js"></script>

<script type="text/javascript" src="Scripts/MicrosoftAjaxTemplates.debug.js"></script>

<script type="text/javascript" src="Scripts/MicrosoftAjaxAdoNet.debug.js"></script>

</head>

<body xmlns:sys="javascript:Sys"

xmlns:dataview="javascript:Sys.UI.DataView"

sys:activate="*">

<ul class="list sys-template"

sys:attach="dataview"

dataview:autofetch="true"

dataview:dataprovider="{{ new Sys.Data.AdoNetServiceProxy('WebDataService1.svc') }}"

dataview:fetchoperation="GetCustomers"

<li>{{ ContactName }}</li>

</ul>

</body>

</html>

7.2 Asp.Net MVC

2Ein zentrales Thema in ASP.NET MVC 2.0 ist die Zusammensetzbarkeit von Websites aus Bausteinen. Nunkann der Entwickler einen View auf einfache Weise aus mehreren anderen Views zusammensetzen. Auÿerdemkann eine MVC-Anwendung mehrere Unteranwendungen (sogenannte �Areas�) mit eigenen Views, Controllernund Modellen besitzen, sodass sie eine eigenständige MVC-Anwendung bilden. Groÿe Webanwendungen werdensomit überschaubarer und können besser in getrennten Teams entwickelt werden. Während bei den dynamischenDatenwebsites (Dynamic Data Websites) eine schnelle Lösung die oberste Maxime ist, zielt Microsoft mit demim März 2009 verö�entlichten ASP.NET Model View Controller Framework (Namensraum System.Web.Mvc)

1WALKTHROUGH Using a DataView with server data2TechEd Europe 2009: Microsoft stellt ASP.NET MVC 2 vor

64 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 65: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.2 Asp.Net MVC

auf klare Kompetenztrennung in lose gekoppelten, beliebig vielen Schichten, gute Test- und Wartbarkeit, die ge-naue Kontrolle über die Ausgabe und möglichst einfache, d.h. parameterlosen URLs. ASP.NET MVC folgt demallgemeinen MVC-Ansatz. Das Modell besteht aus Geschäftsobjekten, Logik und Datenzugri�. Views erzeugendie Benutzerschnittstelle und Aufgabe der Controller ist es, auf Benutzerinteraktionen zu reagieren und die dazupassenden Daten aus dem Modell mit den Views zusammenzubringen. Als Modell können wieder LINQ-to-SQLoder das ADO.NET EF zum Einsatz kommen � das ist dann aber auch schon die einzige Gemeinsamkeit mitden dynamischen Datenwebsites. Controller sind Klassen, die von System.Web.Mvc.Controller abgeleitet sindund Aktionen in Form von Methoden implementieren. Sie nehmen die HTTP-Anfrage entgegen, wobei die Stan-dardform /Controller/Aktion/Daten ist. So würde also http://Server/Flug/Edit/103 in der Controller-KlasseFlug die Methode Edit() mit dem Parameter 103 aufrufen. Die Handhabung der vom Benutzer eingegebe-nen Daten würde eine gleichnamige Methode mit einem Parameter vom Typ FormCollection erledigen (sieheListing 7). Den gröÿten Unterschied zu den dynamischen Datenwebsites und auch den ASP.NET-Webformsmerkt man allerdings in den Views, denn für das MVC Framework gibt es nicht die komfortablen ASP.NET-Webserversteuerelemente. Stattdessen fällt MVC zurück in die Welt von puren HTML-Tags und eingewobenenPlatzhaltern (< % = ... % >) wie einst die klassischen Active Server Pages (ASP) und heute noch PHP.

Wer eine Tabelle ausgeben will, kann sich also nicht auf die Abstraktion des GridView-Steuerelements stützen,sondern muss die HTML-Tags <table>, <tr>, <td> etc. verwenden und die zugehörige Schleife über dieDatensätze explizit programmieren. MVC stellt lediglich zur Ausgabeerzeugung eine sehr bescheidene Anzahlvon Hilfsroutinen wie Html.TextBox(), Html.DropDownList() und Html.ActionLink() zur Verfügung, die abernur wenig Hilfe sind. Auch die Seitenzustandsverwaltung mit dem ASP.NET View State ist in MVC-Seitennicht möglich, d.h. der Entwickler muss sich nun wieder selbst darum kümmern, dass zwischen zwei Aufrufender gleichen Seite der Zustand der Seite wiederhergestellt wird. Auf der Haben-Seite notiert man dafür aberzum einen die volle Kontrolle über das erzeugte HTML und zum anderen die Vermeidung des erzwungenenAufblähens der Webseite durch die Zustandsverwaltung.

Die Views sind hierarchisch im Dateisystem organisierte .aspx-Dateien, denen aber die sonst in ASP.NETüblichen Code-Behind-Dateien fehlen. Listing 8 zeigt einen einfachen View zur Bearbeitung bestehender Flug-Objekte. Erwähnt sein muss auch noch, dass es für die Views derzeit keinen Designer in Visual Studio gibt; hierist also Handarbeit angesagt. Zwar spricht Microsoft auch bei MVC von Sca�olding, meint hier aber andersals bei den dynamischen Datenwebsites eine einfache Codegenerierung zur Entwicklungszeit, die man in VisualStudio für Controller und View anstoÿen kann.

Viele andere nicht an Webserversteuerelemente gebundene Dienste von ASP.NET Webforms wie die Vorlagen-seiten, Authenti�zierung, Benutzerverwaltung, Sitzungsverwaltung, Zwischenspeicherung, Laufzeitüberwachungund Sitemaps stehen aber im MVC Framework zur Verfügung.

Listing 7.2: ASP.Net MVC

//Ausschnitt aus dem Controller "Flug"

Public ActionResult Edit(int id)

{

// Zu aktualisierender Flug aus Datenbank holen

var movieToUpdate = _db.Flug.First(m => m.FlugNr == id);

ViewData.Model = movieToUpdate;

return View("Edit");

}

[AcceptVerbs(HttpVerbs.Post)]

public ActionResult Edit(FormCollection form)

{

// Zu aktualisierender Flug aus Datenbank holen

int id = Int32.Parse(form["FlugNr"]);

Flug flug = _db.Flug.First(m => m.FlugNr == id);

// Deserialisieren

TryUpdateModel(flug, new string[] { "Abflugort", "Zielort" }, form.ToValueProvider());

// Validieren

if (String.IsNullOrEmpty(flug.Abflugort))

ModelState.AddModelError("Abflugort", "Abflugort is required!");

if (String.IsNullOrEmpty(flug.Zielort))

ModelState.AddModelError("Zielort", "Zielort is required!");

// Speichern

if (ModelState.IsValid)

{

65 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 66: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

7.2 Asp.Net MVC

_db.SaveChanges();

return RedirectToAction("Index");

}

// Sonst, zeige Eingabeformular erneut an

return View(flug);

}

//View "/Flug/Edit.aspx" zur Eingabe von Flugdaten

<form method="post" action="/Flug/Edit">

<%= Html.Hidden("FlugNr")%>

FlugNr:

<br />

<%= Model.FlugNr %>

<br />

Abflugort:

<br />

<%= Html.TextBox("Abflugort")%>

<br />

Zielort:

<br />

<%= Html.TextBox("Zielort")%>

<br />

<input type="submit" value="Save Flug" />

</form>

Aufgabe 7.3 Arbeiten Sie den Artikel Erstellen von Webanwendungen ohne Webformulare im Anhang ab Seite56 durch.

66 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 67: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

8 Windows Communication Foundation

Abbildung 8.1: Assembly

Was ist die Windows Communication Foundation? Was ist die Windows Communication Foundation? Dieglobale Akzeptanz von Webdiensten, wozu Standardprotokolle für Anwendung-zu-Anwendung-Kommunikationgehören, hat die Softwareentwicklung verändert. Beispielsweise gehören zu den Funktionen, die Webdienstenun bereitstellen, Sicherheit, Koordination verteilter Transaktionen und zuverlässige Kommunikation. Die Vor-teile der Veränderungen im Bereich Webdienste sollten in den von Entwicklern verwendeten Hilfsmitteln undTechnologien widergespiegelt werden. Windows Communication Foundation (WCF) ist darauf ausgelegt, einenverwaltbaren Ansatz zum verteilten Computing, zur weitreichenden Interoperabilität und für eine direkte Un-terstützung der Dienstorientierung zu bieten. WCF erleichtert die Entwicklung von verbundenen Anwendungendurch ein neues dienstorientiertes Programmierungsmodell. WCF unterstützt durch seine Schichtenarchitekturviele Arten der Entwicklung verteilter Anwendungen. Auf der Basisschicht bietet die WCF-Kanalarchitekturasynchrone, nicht typisierte nachrichtenübertragende Stammfunktionen. Auf dieser Basisschicht be�nden sichProtokollfunktionen für einen sicheren, zuverlässigen, durchgeführten Datenaustausch und eine weitläu�ge Aus-wahl an Transport- und Codierungsoptionen.

8.1 Problembeispiel

Im folgenden Beispiel werden einige der Probleme veranschaulicht, die WCF angeht. Ein Autovermieter ent-scheidet sich, eine neue Anwendung zum Reservieren von Autos zu erstellen. Die Entwickler dieser Reservie-rungsanwendung für Mietwagen wissen, dass es möglich sein muss, auf die von ihr implementierte Geschäftslogiküber andere Software, sowohl innerhalb als auch auÿerhalb des Unternehmens, zuzugreifen. Daher entscheiden siesich, auf dienstorientierte Weise zu programmieren, wobei die Logik der Anwendung anderer Software über einende�nierten Satz von Diensten verfügbar gemacht wird. Zum Implementieren dieser Dienste und somit zum Kom-munizieren mit anderer Software verwendet die neue Anwendung WCF.Mietwagen-Szenario:Während ihrerLebensdauer wird voraussichtlich eine Reihe anderer Anwendungen auf die Mietwagen-Reservierungsanwendungzugreifen. Bei ihrer Programmierung wissen die Ersteller der Mietwagen-Reservierungsanwendung jedoch, dassdrei andere Arten von Software auf ihre Geschäftslogik zugreifen werden:

• Eine Call-Center-Clientanwendung, die auf den Windows-Desktops läuft, mit denen die Mitarbeiter imCall-Center des Unternehmens arbeiten. Da diese Anwendung speziell für das neue Reservierungssystemerstellt wird, wird sie auch mit Microsoft .NET Framework und WCF programmiert. Diese Anwendungist nicht wirklich unabhängig von der neuen Mitwagenreservierungs-Anwendung, da ihr einziger Zweckdarin besteht, als Client für das neue System zu dienen. Aus dienstorientierter Perspektive ist sie nur einweiterer Client für die Geschäftslogik des Reservierungssystems.

67

Page 68: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

8.1 Problembeispiel

• Eine vorhandene Reservierungsanwendung, die auf einem J2EE-Server erstellt wird, der auf einem Nicht-Windows-System läuft. Aufgrund einer kürzlich stattgefundenen Fusion mit einem anderen Autovermietermuss das vorhandene System auf die Logik der neuen Anwendung zugreifen können, damit den Kundender fusionierten Unternehmen eine einheitliche Umgebung geliefert wird.

• Partneranwendungen, die auf einer Vielzahl von Plattformen laufen, wobei sich jede in einem Unternehmenbe�ndet, das ein geschäftliches Arrangement mit dem Autovermieter eingegangen ist. Zu den Partnernkönnen Reisebüros, Fluggesellschaften und andere gehören, die für ihre Geschäfte Autovermietungsreser-vierungen vornehmen müssen.

Die verschiedenartigen Kommunikationsanforderungen für die neue Mietwagen-Reservierungsanwendung sindnicht einfach. Für Interaktionen mit der Call-Center-Clientanwendung ist beispielsweise die Leistung wichtig,während die Interoperabilität unkompliziert ist, da beide auf .NET Framework programmiert sind. Für dieKommunikation mit der vorhandenen J2EE-basierten Reservierungsanwendung und den verschiedenen Part-neranwendungen ist jedoch die Interoperabilität von höchster Bedeutung. Auÿerdem sind die Sicherheitsanfor-derungen sehr unterschiedlich, da sie je nach den lokalen Windows-basierten Anwendungen, einer J2EE-basiertenAnwendung, die auf einem anderen Betriebssystem läuft, und einer Vielzahl von Partneranwendungen, die überdas Internet hereinkommen, variieren. Sogar Transaktionsanforderungen könnten variieren, wenn nur die in-ternen Anwendungen Transaktionsanforderungen stellen dürfen. Wie können diese verschiedenen geschäftlichenund technischen Anforderungen erfüllt werden, ohne die Ersteller der neuen Anwendung vor unüberwindbareSchwierigkeiten zu stellen?

WCF wurde für dieses komplexe, aber realistische Szenario entworfen und ist die Standardtechnologie fürWindows-Anwendungen, die Dienste verfügbar machen und die auf sie zugreifen. Dieses Thema bietet eineEinführung in WCF, untersucht, was es bereitstellt und zeigt, wie es eingesetzt wird. In dieser Einführungdient das gerade beschriebene Szenario als Beispiel. Das Ziel ist es, deutlich zu machen, was WCF ist, welcheProbleme es löst und zu zeigen, wie es diese Probleme löst. Angehen des Problems: Die Grundlage für neueWindows-basierte Anwendungen ist .NET Framework. Dementsprechend wird WCF hauptsächlich als ein auf.NET Framework-CLR aufbauender Satz von Klassen implementiert. Da es die ihnen bekannte Umgebung er-weitert, ermöglicht es WCF den Entwicklern, die derzeit objektorientierte Anwendungen über .NET Frameworkerstellen, auch dienstorientierte Anwendungen auf gewohnte Weise zu programmieren. Kommunikation zwi-

schen einem WCF-Client und -Dienst:Wie das bereits beschriebene Szenario zeigt, dient WCF zum Löseneiner Reihe von Herausforderungen für kommunizierende Anwendungen. Drei Punkte sind jedoch eindeutig diewichtigsten Aspekte von WCF:

• Vereinigung vorhandener .NET Framework-Kommunikationstechnologien.

• Unterstützung für anbieterübergreifende Interoperabilität, einschlieÿlich der Zuverlässigkeit, Sicherheitund Transaktionen.

• Explizite Dienstausrichtung.

Da WCFs grundlegende Kommunikationsmechanismen SOAP-basierte Webdienste sind, können WCF-basierteAnwendungen mit anderer Software kommunizieren, die in einer Vielzahl an Kontexten läuft. Eine unter WCFerstellte Anwendung kann mit folgenden Anwendungen interagieren:

• WCF-basierten Anwendungen, die in einem anderen Prozess auf dem gleichen Windows-Computer ausge-führt werden.

• WCF-basierten Anwendungen, die auf einem anderen Windows-Computer ausgeführt werden.

• Auf anderen Technologien erstellten Anwendungen, z. B. J2EE-Anwendungsservern, die Standardweb-dienste unterstützen. Diese Anwendungen können auf Windows-Computern oder auf Computern, aufdenen andere Betriebssysteme laufen, ausgeführt werden.

Um mehr als nur grundlegende Kommunikation zuzulassen, implementiert WCF Webdiensttechnologien, diedurch WS-*-Spezi�kationen de�niert werden. All diese Spezi�kationen wurden ursprünglich von Microsoft,IBM und anderen Anbietern in Zusammenarbeit de�niert. Wenn sich diese Spezi�kationen durchsetzen, ge-hen sie oft in den Besitz von Standardgremien wie dem World Wide Web Consortium (W3C) oder derOrganization for the Advancement of Structured Information Standards (OASIS) über. Diese Spezi�katio-nen betre�en mehrere Bereiche, unter anderem grundlegendes Messaging, Sicherheit, Zuverlässigkeit, Trans-aktionen und das Arbeiten mit den Metadaten eines Diensts. Weitere Informationen �nden Sie unter Inter-operabilität und Integration. Weitere Informationen zu erweiterten Webdienstspezi�kationen �nden Sie unterhttp://go.microsoft.com/fwlink/?LinkId=86603 (möglicherweise in englischer Sprache).

68 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 69: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

8.2 Architektur

8.2 Architektur

Abbildung 8.2: Assembly

Windows Communication Foundation-Architektur Verträge und Beschreibungen

Verträge de�nieren verschiedene Aspekte des Nachrichtensystems. Der Datenvertrag beschreibt alle Parameter,aus denen die einzelnen Nachrichten bestehen, die ein Dienst erstellen oder verarbeiten kann. Die Nachrichten-parameter werden in XSD-Dokumenten (XML-Schemade�nitionssprache) de�niert. Dadurch kann jedes XML-fähige System die Dokumente verarbeiten. Der Nachrichtenvertrag de�niert anhand von SOAP-Protokollenbestimmte Nachrichtenteile und ermöglicht eine detailliertere Steuerung der Teile einer Nachricht, wenn dieInteroperabilität diese Genauigkeit verlangt. Der Dienstvertrag gibt die tatsächlichen Methodensignaturen desDienstes an und wird als Schnittstelle in einer der unterstützten Programmiersprachen verteilt (z. B. VisualBasic oder Visual C#).

Richtlinien und Bindungen legen die zur Kommunikation mit einem Dienst erforderlichen Bedingungen fest.Beispielsweise muss die Bindung (mindestens) den verwendeten Transport (z. B. HTTP oder TCP) und eineCodierung angeben. Richtlinien schlieÿen Sicherheitsanforderungen und andere Bedingungen ein, die für dieKommunikation mit einem Dienst erfüllt werden müssen.

8.2.1 Dienstlaufzeit

Die Dienstlaufzeitebene umfasst Verhaltensweisen, die nur während der tatsächlichen Ausführung des Dienstesauftreten, d. h. das Laufzeitverhalten des Dienstes. Die Drosselung steuert, wie viele Nachrichten verarbeitetwerden. Diese Zahl kann geändert werden, wenn die Nachfrage nach dem Dienst ein voreingestelltes Limit er-reicht. Für den Fall eines internen Dienstfehlers gibt das Fehlerverhalten die zu ergreifenden Maÿnahmen an, z.B. indem es steuert, welche Informationen an den Client übermittelt werden. (Zu viele Informationen könnteneinem böswilligen Benutzer einen Angri� erleichtern.) Das Metadatenverhalten bestimmt, wie und ob Meta-daten ö�entlich verfügbar gemacht werden. Wie viele Instanzen des Dienstes ausgeführt werden können, wirdvom Instanzenverhalten angegeben (Singleton gibt z. B. nur eine Instanz zur Verarbeitung aller Nachrichtenan). Das Transaktionsverhalten ermöglicht einen Rollback von durchgeführten Vorgängen im Fall eines Fehlers.Mit dem Verteilungsverhalten wird die Verarbeitung von Nachrichten durch die WCF-Infrastruktur gesteuert.Die Erweiterbarkeit ermöglicht eine Anpassung der Laufzeitprozesse. Beispielsweise werden mit der Nachrich-teninspektion Teile einer Nachricht überprüft, und mit der Parameter�lterung �nden anhand von Filtern, dieauf Nachrichtenheader angewendet werden, voreingestellte Aktionen statt.

8.2.2 Messaging

Die Messagingebene besteht aus Kanälen. Ein Kanal ist eine Komponete, die eine Nachricht in bestimmter Wei-se verarbeitet, z. B. durch Authenti�zierung. Ein Reihe von Kanälen wird auch Kanalstapel genannt. Kanälearbeiten mit Nachrichten und Nachrichtenheadern. Die Dienstlaufzeitebene hingegen verarbeitet hauptsäch-lich den Inhalt des Nachrichtentexts. Es gibt zwei Arten von Kanälen: Transportkanäle und Protokollkanäle.

69 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 70: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

8.2 Architektur

Transportkanäle lesen und schreiben Nachrichten aus dem Netzwerk (oder einem Kommunikationspunkt zurAuÿenwelt). Bei einigen Transporten wird ein Encoder verwendet, um Nachrichten (die als XML-Infosets dar-gestellt werden) in und aus der Bytestreamdarstellung des Netzwerks zu konvertieren. HTTP, benannte Pipes,TCP und MSMQ sind Beispiele für Transporte. Beispiele für Codierungen sind XML und optimierte Binär-dateien. Protokollkanäle implementieren Nachrichtenverarbeitungsprotokolle. Das erfolgt häu�g durch Lesenoder Schreiben zusätzlicher Header in die Nachricht. Zu diesen Protokollen gehören beispielsweise WS-Securityund WS-Reliability. Die Messagingebene stellt die möglichen Formate und die Austauschmuster der Datendar. WS-Security ist eine Implementierung der WS-Security-Spezi�kation, die die Sicherheit auf der Nach-richtenebene aktiviert. Der WS-Reliable Messaging-Kanal stellt die Nachrichtenübermittlung sicher. Mit einerVielzahl von Codierungen ermöglichen es Encoder, die Nachrichtenanforderungen zu erfüllen. Der HTTP-Kanalgibt die Verwendung des Hypertextübertragungsprotokolls zur Nachrichtenübermittlung an. Entsprechend gibtder TCP-Kanal die Verwendung des TCP-Protokolls an. Der Transaktions�usskanal bestimmt die Muster vonTransaktionsnachrichten. Der Kanal für benannte Pipes ermöglicht die prozessübergreifende Kommunikation.Der MSMQ-Kanal ermöglicht die Interoperation zwischen MSMQ-Anwendungen.

8.2.3 Hosting und Aktivierung

Ein Dienst ist letztendlich ein Programm. Wie andere Programme muss ein Dienst in einer ausführbaren Dateiausgeführt werden. Dabei handelt es sich um einen so genannten selbst gehosteten Dienst. Dienste werden jedochauch gehosted oder in einer ausführbaren Datei von einem externen Agent verwaltet ausgeführt, z. B. IIS oderWindows Activation Services (WAS). WAS ermöglicht es, WCF-Anwendungen auf Computern, auf denen WASausgeführt wird, automatisch zu aktivieren. Sie können Dienste gegebenenfalls manuell als ausführbare Dateien(EXE-Dateien) ausführen. Ein Dienst kann auch automatisch als Windows-Dienst ausgeführt werden. AuchCOM+-Komponenten können als WCF-Dienste gehostet werden.

Ein einfaches Beispiel für einen Ajax-fähigen WCF-Dienst �nden Sie in Abschnitt 7.1 auf Seite 64.

70 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 71: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

9 Windows Work�ow Foundation

1Windows Work�ow Foundation (WF) ist ein Framework, das Benutzern das Erstellen von system- oder be-nutzerbezogenen Work�ows in ihren Anwendungen für die Betriebssysteme Windows Vista, Windows XP undWindows Server 2003 ermöglicht. Es besteht aus einem Namespace, einem prozessinternen Work�owmodul undDesignern für Visual Studio. Windows Work�ow Foundation kann sowohl in einfachen Szenarios (z. B. zumAnzeigen bestimmter Benutzerober�ächensteuerelemente in Abhängigkeit von Benutzereingaben) als auch inkomplexen Szenarios (z. B. zur Bestellabwicklung und Lagerverwaltung) eingesetzt werden. Windows Work�owFoundation umfasst ein Programmiermodell, ein neu zu hostendes und anpassbares Work�owmodul und dieTools zum schnellen Erstellen work�owfähiger Anwendungen unter Windows.

Zu den Szenarios, für die die Windows Work�ow Foundation eingesetzt werden kann, zählen Folgende:

• Aktivieren von Work�ows innerhalb von Branchenanwendungen

• Benutzerober�ächen-Page�ows

• Dokumentorientierte Work�ows

• Benutzerbezogene Work�ows

• Zusammengesetzte Work�ows für dienstorientierte Anwendungen

• Geschäftsregelbasierte Work�ows

• Systemverwaltungswork�ows

Gehen wir einen Schritt zurück. Was ist ein Work�ow? EinWork�ow ist eine Folge von Schritten, Entscheidungenund Regeln, die abgearbeitet werden müssen, um eine bestimmte Aufgabe zu erledigen.

Aufgabe 9.1 Beschreiben Sie die Schritte, Entscheidungen und Regeln, die für die Anmeldung zu einer Prüfungnotwendig sind.

Microsoft nennt die einzelnen Schritte eines Work�ows Activities. Ein Work�owprojekt ist keine eigentlicheAnwendung, sondern benötigt immer einen Host, eine Asp.Net Applikation kann beispielsweise ein solcher Hostsein. Denken Sie beispielsweise an mögliche Verzweigungen in einer Web-Applikation, je nach Benutzereingabenwerden Sie auf eine andere Seite geleitet. Auch ein Neustart des Betriebssystems beendet einen Work�ow nicht.Warum?

Es werden zwei Arten von Work�ows unterschieden: Statuscomputerwork�ows und Sequenzielle Work�ows.

9.1 Statuscomputerwork�ows

2Im Statuscomputerformat der Work�owerstellung modelliert der Autor den Work�ow als Statuscomputer. DerWork�ow selbst besteht aus einer Reihe von Zuständen. Ein Zustand wird als Ausgangszustand bezeichnet. JederZustand kann einen bestimmten Satz Ereignisse erhalten. Auf Grundlage eines Ereignisses kann ein Übergangzu einem anderen Zustand erfolgen. Der Statuscomputerwork�ow kann über einen Endzustand verfügen. BeimÜbergang zum Endzustand wird der Work�ow abgeschlossen.

Das folgende Flussdiagramm ist ein Beispiel für einen Statuscomputerwork�ow.

Folgende Aktivitäten stehen out of the box zur Verfügung:

• EventDrivenActivity � Wird für Zustände verwendet, die für die Ausführung ein externes Ereignis benö-tigen. Die EventDrivenActivity-Aktivität muss über eine Aktivität verfügen, mit der die IEventActivity-Schnittstelle als erste untergeordnete Aktivität implementiert wird. Weitere Informationen �nden Sie unterVerwenden der EventDrivenActivity-Aktivität.

1Übersicht über die Windows Work�ow Foundation2Statuscomputerwork�ows

71

Page 72: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

9.2 Sequenzielle Work�ows

Abbildung 9.1: Beispiel für einen Statuscomputerwork�ow

• SetStateActivity � Gibt einen Übergang zu einem neuen Zustand an. Weitere Informationen �nden Sieunter Verwenden der SetStateActivity-Aktivität.

• StateActivity � Stellt einen Zustand auf einem Statuscomputer dar; enthält möglicherweise zusätzlicheZustandsaktivitäten. Weitere Informationen �nden Sie unter Verwenden der StateActivity-Aktivität.

• StateInitializationActivity � Wird ausgeführt, wenn ein Zustand eintritt. Enthält möglicherweise andereAktivitäten. Weitere Informationen �nden Sie unter Verwenden der StateInitializationActivity-Aktivität.

• StateFinalizationActivity � Führt beim Verlassen einer StateActivity-Aktivität enthaltene Aktivitätenaus. Weitere Informationen �nden Sie unter Verwenden der StateFinalizationActivity-Aktivität.

9.2 Sequenzielle Work�ows

3Mit dem sequenziellen Work�owformat wird eine Reihe enthaltener Aktivitäten der Reihenfolge nach ausge-führt. Sie können einem sequenziellen Work�ow andere zusammengesetzte Aktivitäten hinzufügen, um Folgendeszu erreichen: Parallelismus (ParallelActivity), ereignisgesteuerten Parallelismus (EventHandlingScopeActivity),datengesteuerte Ausführung (ConditionedActivityGroup), ereignisgesteuerte Verzweigung (ListenActivity) undvertraute imperative Ablaufsteuerungsmuster wie bedingte Verzweigung (IfElseActivity) und Iteration (Whi-leActivity, ReplicatorActivity). Zudem können benutzerde�nierte zusammengesetzte Aktivitäten geschriebenwerden, mit denen spezielle Ablaufsteuerungsmuster implementiert werden.

Das folgende Flussdiagramm zeigt ein Beispiel eines sequenziellen Work�ows.

Abbildung 9.2: Beispiel für einen sequenziellen Work�ow

Ein sequenzieller Work�ow führt Aktivitäten bis zur Fertigstellung der letzten Aktivität sequenziell aus. Se-quenzielle Work�ows sind auch im normalen Betrieb nicht notwendigerweise vollständig deterministisch. Bei-spielsweise kann eine ListenActivity-Aktivität oder eine ParallelActivity-Aktivität verwendet werden, wobei diegenaue Reihenfolge der Ereignisse in diesen Fällen variieren kann.

3Sequenzielle Work�ows

72 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 73: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10 .Net Framework

Im letzten Jahrtausend wurde Cliententwicklung im Microsoftumfeld hauptsächlich mit VB6 betrieben. VB6ist eine prozedurale interpretierte Sprache, mit der schon Vieles ging. Die aber die Entwicklung der Software-entwicklung (OO, Modellierung, ...) nicht mitmachen konnte. Zusätzlich wurden performante Komponenten inC++ entwickelt. Die Kommunkation zwischen den Komponenten wurde oft über COM realisiert. Die Konver-tierung von Datentypen in VB6 und C++ war ein gängiges Problem. Auch die Installation und das Update vonKomponenten machte immer wieder Probleme (Versionskon�ikte, Registry). Der Siegeszug von Java mit derVirtual Maschine und der damit verbundenen Plattformunabhängigkeit war ein groÿes Vorbild. Microsoft hatviel von Java übernommen, einige Features dazu gepackt und daraus ein eigenes Framework entwickelt. .Netadressiert die folgenden bis dahin aktuellen Probleme:

• Austausch von Variablen/Objekten zwischen verschiedenen Komponenten, die evtl. in verschiedenen Pro-grammiersprachen implementiert sind.

• Interoperabilität, d.h. standardisierte Protokolle, Datenformate (XML-basiert)

• Zustandslosigkeit des Webs

• Ungleiche Programmierschnittstellen, zu viele APIs (COM, Win32, Microsoft Foundation Classes, ...)

• Speicherverwaltung

• Routinetätigkeiten, Implementation ähnlicher wiederkehrender Probleme

• Softwareverteilung (Deployment)

Durch Mono gibt es .Net nicht nur für Windows, sondern auch für Unix/Linux. Die Entwickler von Mono sindsehr aktiv, so gibt es jetzt schon eine Unterstützung von .Net 4.0 - was erst im April ausgerollt wird.

Im Originalton von Microsoft liest sich das wie folgt .Net: .NET Framework ist eine integrale Windows-Komponente, die die Entwicklung und Ausführung von Anwendungen und XML-Webdiensten der nächstenGeneration unterstützt. .NET Framework wurde im Hinblick auf folgende Zielsetzungen entwickelt:

• Bereitstellung einer konsistenten, objektorientierten Programmierumgebung, in der Objektcode gespei-chert wird. Die Ausführung erfolgt dann entweder lokal oder über Remotezugri� bzw. lokal mit Verteilungüber das Internet.

• Bereitstellung einer Codeausführungsumgebung, mit der Kon�ikte bei der Softwarebereitstellung undVersionskon�ikte auf ein Minimum beschränkt werden.

• Bereitstellung einer Codeausführungsumgebung, die eine sichere Ausführung ermöglicht, und zwar auchvon Code, der von unbekannten oder nur halb-vertrauenswürdigen Dritten erstellt wurde.

• Bereitstellung einer Codeausführungsumgebung, die nicht mehr die bei interpretations- oder skriptbasier-ten Umgebungen auftretenden Leistungsprobleme aufweist.

• Scha�ung einer konsistenten Entwicklungsumgebung für die verschiedensten Anwendungsarten, wie bei-spielsweise Windows- und webbasierte Anwendungen.

• Aufbau der gesamten Kommunikation auf Industriestandards, um die Integration von Code, der auf .NETFramework basiert, in jeden anderen Code zu gewährleisten.

73

Page 74: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.1 Vokabeln

10.1 Vokabeln

Grundlage ist die Common Language Infrastructure (CLI), die auch bei Standard ECMA-335 de�niert ist:

• Partition I: Konzepte und Architektur � Beschreibt die Gesamtarchitektur der CLI. Spezi�ziert dazudas Common Type System (CTS), das Virtual Execution System (VES) und die Common LanguageSpeci�cation (CLS).

• Partition II: Metadatende�nition und Semantik � Enthält Informationen über Metadaten: Das physischeLayout der Dateien, die logischen Inhalte und deren Struktur.

• Partition III: CIL � Beschreibt die Instruktionen der Common Intermediate Language (CIL).

• Partition IV: Bibliotheken � Enthält eine Spezi�kation von Klassen und Klassenbibliotheken, die als Teilder CLI standardisiert sind.

• Partition V: Beschreibt das einheitliche Debuggingformat.

• Partition VI: Anhänge.

Abbildung 10.1: CTS in Relation zu CLS

Das Common Type System (CTS) ist die Gesamtmenge aller möglichen Eigenschaften und Typen (siehe Abbil-dung 10.3). Die Common Language Speci�cation (CLS) ist eine Untermenge der CTS-Eigenschaften und Typen,die von jedem Compiler unterstützt werden müssen, um die Interoperabilität zwischen der .Net Applikationenzu garantieren. Nicht CLS-konforme Typen sind beispielsweise Int8 oder Unsigned int64.

Die CLS stellt einen Satz von Merkmalen sicher, die in jeder Sprache, die in die Laufzeitumgebung integriertwird, garantiert zur Verfügung stehen. Die CLS garantiert, dass jedes Programm bzw. jeder Programmteil (z. B.eine einzelne Klasse), der CLS-konform entwickelt worden ist, in jeder anderen CLS-kompatiblen Programmier-sprache vollständig genutzt werden kann1. Beispielsweise gibt es nur einen unicode-basierten Stringtyp und allesist ein Objekt. Dadurch ist die .Net Plattform selbst sprachneutral und jede der dort verwendeten Sprachen istgleich berechtigt. Momentan sind 57 Sprachen bei Wikipedia (Liste von .NET-Sprachen) gelistet, die in einermodi�zierten Implementation, im .Net Framework laufen. Darunter �nden sich PHP, Python, Prolog, Fortran,Java, Perl und Ruby.

Ähnlich zur Java Virtual Maschine gibt es bei .Net eine Common Language Runtime (CLR), die alles zurVerfügung stellt, damit Komponenten miteinander interagieren können. Sie erzeugt Objekte, übernimmt Me-thodenaufrufe und verwaltet Speicher, Prozesse und Sicherheit.

Abbildung ?? zeigt wie das Framework arbeitet. Mit einem Vorcompiler wird Programmcode in die CommonIntermediate Language (CIL) (teilweise auch nur Intermediate Language (IL) genannt) übersetzt. CIL ist eineobjektorientierte Assemblersprache. Das physikalische Ergebnis dieses Compilevorgangs sind die sogenanntenAssemblies (siehe Abschnitt 10.2 auf Seite 78). Die Assemblies be�nden sich im lokalen Verzeichnis oder sindim Global Assembly Cache (GAC) registriert. Der Just in time compiler (JIT) erzeugt daraus sogenanntenManaged Code, der in der Common Language Runtime (CLR) für das jeweilige Betriebssystem umgesetzt wird.

1Die Regeln der CLS gelten dabei immer nur für ö�entliche (public oder protected) Schnittstellen.

74 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 75: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.1 Vokabeln

Abbildung 10.2: Ausführungsmodell

Managed Code ist per De�nition Code, der von der CLR interpretiert wird. Jeder andere Code ist unmanaged.Unmanaged Code unterliegt also nicht der Aufsicht der CLR, ist plattformabhängig und typischerweise ohnedas .Net Framework erstellt. Der durch den JIT-Compiler erstellte Maschinencode wird bei Beendigung desProzesses, der die ausführbare Datei ausführt, verworfen. Daher muss die Methode beim erneuten Ausführender Anwendung wieder neu kompiliert werden. NGen erlaubt die Vorkompilierung von CIL-Anwendungen inMaschinencode vor der Ausführungszeit � typischerweise während der Installation. Dies führt zu zwei wesentli-chen Leistungsvorteilen. Erstens wird die Dauer zum Starten der Anwendung verringert, da eine Kompilierungzur Ausführungszeit vermieden wird. Zweitens wird die Speichernutzung verbessert, da ein gemeinsames Ver-wenden der Codepages durch mehrere Prozesse unterstützt wird. Obwohl NGen auf den ersten Blick einerherkömmlichen statischen Back-End-Kompilierung zu entsprechen scheint, ist es tatsächlich etwas ganz ande-res. Im Unterschied zu statisch kompilierten Binärdateien werden NGen-Images an die Maschine gebunden,auf denen sie erstellt wurden, und können daher nicht allgemein bereitgestellt werden. Stattdessen muss dasInstallationsprogramm einer Anwendung Befehle ausführen, um systemeigene Images der jeweiligen Assemblysauf dem Clientcomputer zu erstellen (ausführlichere Artikel: Die Leistungsvorteile durch NGen NGen Revs UpYour Performance with Powerful New Features).

Abbildung 10.3: Speicherverwaltung

Der Garbage Collector der CLR verwaltet die Reservierung und Freigabe von Arbeitsspeicher für die Anwen-dung. Wenn Sie den Operator new zum Erstellen eines Objekts verwenden, reserviert die CLR Arbeitsspeicher fürdas Objekt auf dem verwalteten Heap. Solange ein Adressbereich im verwalteten Heap verfügbar ist, reserviertdie Laufzeit Arbeitsspeicher für neue Objekte. Arbeitsspeicher ist jedoch nicht unendlich verfügbar. Möglicher-weise muss mithilfe der Garbage Collection Arbeitsspeicher freigegeben werden. Das Optimierungsmodul derGarbage Collection bestimmt den besten Zeitpunkt für das Einsammeln anhand der erfolgten Reservierungen.Beim Einsammeln durch die Garbage Collection wird nach Objekten im verwalteten Heap gesucht, die nicht

75 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 76: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.1 Vokabeln

mehr von der Anwendung verwendet werden. Anschlieÿend werden die für das Freigeben des Arbeitsspeicherserforderlichen Operationen ausgeführt. Dafür weiÿ man nicht, wann der Speicher wirklich freigegeben wird. DasFramework bietet auch direkten Zugri� auf den GC, damit sollte man jedoch sehr sehr vorsichtig umgehen.

Garbage Collector:gibt Speicher aller Objekte frei, auf die kein Verweis mehr existiert.

Eine genaue Beschreibung der Arbeitsweise des GC in C# bzw. Java �nden Sie im Anhang auf Seite 84.

Aufgabe 10.1 • Beschreiben Sie die wesentlichen Phasen im Lebenszyklus eines Objekts.

• Klären Sie die Begri�e Stack, Heap, managed Heap,

• Wie ist die prinzipielle Arbeitsweise des GC?

• Wie viele Generationen werden verwaltet?

• Was landet auf dem Stack, was auf dem Heap?

• Lesen Sie auch den Artikel des GC in Java: Gibt es prinzipielle Unterschiede, wenn ja, welche?

Das Sicherheitsmodell der CLR deckt folgende Bereiche ab:

• Typsicherheit: C# erlaubt Zeiger nur innerhalb eines unsafe-Blocks, Basis ist die CLS. Die CLR führt eineTypsicherheitsprüfung während des JIT-Vorgangs durch und weigert sich bei Fehlern, diesen auszuführen.Sie ist manuell durchführbar mittels PEVerify.exe.

• Code-Signierung ermöglicht es Sicherheitseinschränkungen für Assemblies und Code zu de�nieren. DieseEinschränkungen und Regeln werden von der CLR erzwungen und sowohl durch die Entwicklerin als auchdurch die Systemadministratorin de�niert.

• Kryptographie

• Code Access Security (CAS)

Abbildung 10.4: Code Access Security, Quelle: Uni Karlsruhe, Microsoft .Net Sicherheit

Abbildung 10.4

CAS schützt den Zugri� auf Systemresourcen über Berechtigungen (Permissions). Berechtigungen können mitHilfe eines Berechtigungsatzes (Permission Set) gruppiert werden. Dieser Vorgang ist mit den von Windows herbekannten Benutzern und Benutzergruppen vergleichbar. Ein Berechtigungssatz ist dabei grundsätzlich einerCodegruppe (Code Group) zugeordnet. Eine Code-Gruppe ist eine logische Einstufung von Code anhand einerBedingung, z.B. alle Assemblies von http://www.somewebsite.com/ gehören zur Gruppe SomeCode. Sobald einAssembly geladen wird, untersucht die CLR dieses Assembly und legt fest, welche Codemerkmale (Evidence) esbesitzt � Standardmäÿig sind im .NET Framework insgesamt sieben verschiedene Codemerkmale vorde�niert:Application Directory, Hash, Publisher, Site, Strong Name, URL und Zone. Die Zuordnung eines Codemerk-mals zu einer Codegruppe � und damit den Rechten, die das Assembly zur Laufzeit erhält � erfolgt über diesogenannte Zugehörigkeitsbedingung (Membership Condition). Wichtig dabei: Besitzt ein Assembly mehrere

76 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 77: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.1 Vokabeln

Codemerkmale, wird es auch mehreren Codegruppen gleichzeitig zugeordnet. Die jeweiligen Rechtemengen wer-den dabei zu einer Vereinigungsmenge zusammengeführt (ODER-Verknüpfung)! Durch dieses Verhalten kannein Assembly mehr Rechte zugewiesen bekommen, als ursprünglich vorgesehen. Aus diesem Grund kann eineCodegruppe mit dem Attribut exklusiv versehen werden. Das Assembly bekommt dann nur die Berechtigungendieser einzelnen Gruppe zugewiesen. Mit PermView.exe kann man analysieren, welche Rechte ein Programmbenötigt. Der Lader liest die Metadaten und legt im Hauptspeicher eine interne Repräsentation der Klassen undseiner Mitglieder an. Eine Klasse wird nur dann geladen, wenn sie auch referenziert wird.

Abbildung 10.5: Zusammenspiel von Loader, CAS und GAC (Quelle:Microsoft.NET Framework Runtime, Teil1: Die ausführung von Assemblys)

Abbildung 10.5 zeigt, wie und wann die CLR Sicherheitschecks durchführt. Einen ausführlichen Artikel hierzu�nden Sie unter Understanding The CLR Binder.

Auch die Fehlerbehandlung passiert in der CLR. Früher gab es diverse Methoden, wie Win32 Error Codes,COM HRESULTs oder in VB6 On Error GoTo. Dabei war es problematisch, dass diese leicht zu ignorierenund nicht strukturiert waren, zu wenig Informationen enthielten und nicht sprach- und technologieübergreifendzur Verfügung standen. Exceptions der CLR lösen dies auf: Signalisiert ein Objekt einen Ausnahmezustand, soist diese Ausnahme einheitlich für alle .Net Sprachen, müssen behandelt werden, können vererbt werden (überSprachgrenzen hinaus) und enthalten Zusatzinformationen (Stacktrace). Nicht behandelte Ausnahmen werdenan die aufrufende Komponente weitergereicht.

77 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 78: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.2 Assembly

10.2 Assembly

Abbildung 10.6: Assembly

Microsoft de�niert ein Assembly so:

Assemblies sind die Grundbausteine von .NET Framework-Anwendungen. Sie bilden die Basiseinheit für Weiter-gabe, Versionskontrolle, Wiederverwendung, Aktivierungs-Scoping und Sicherheitsberechtigungen. Eine Assem-bly ist eine Au�istung von Typen und Ressourcen, die so erstellt wurden, dass sie zusammenarbeiten und einelogische funktionelle Einheit bilden. Eine Assembly stellt der Common Language Runtime die für das Erkennenvon Typimplementierungen erforderlichen Informationen zur Verfügung. Für die Common Language Runtimesind Typen nur im Kontext einer Assembly vorhanden. Quelle: Assemblies kurz und knapp.

Ein Assembly ist somit . . .

• eine Menge von Typen, die zusammenkompiliert werden, inklusive Ressourcen wie Bilder.

• eine Laufzeiteinheit, die als Datei gespeichert wird (*.exe oder *.dll), auch portable Executable (PE)genannt.

• die kleinste Einheit in Bezug auf

� Deployment,

� dynamischem Laden,

� Sicherheit

� Versionierung

• enthält Code (objekorientierter Assemblercode)

• enthält Metadaten

• enthält ein Manifest (Inhaltsverzeichnis)

• ist vergleichbar mit einer Java JAR-Datei

• ist eine .Net Komponente

Man unterscheidet zwischen privaten und ö�entlichen Assemblies. Ö�entliche Assemblies werden im GAC re-gistriert, was zur Folge hat, dass sie über einen strong name verfügen müssen. Ein solcher Name ist eindeutig,ähnlich einer GUID. Assemblies im GAC stehen allen Applikationen zur Verfügung. Private Assemblies be�ndensich im Verzeichnis der Applikation, können nur von dieser Applikation verwendet werden und können nichtsigniert werden. Der GAC ist ein sicherer Speicher, der verschiedene Versionen einer Assembly enthalten kann.

Ein Strong Name setzt sich aus vier Teilen zusammen:

• Dateiname, z.B. db.dll

• Versionsnummer, z.B. 1.1.123.25

78 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 79: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.2 Assembly

• der Kultur (de-de, en-us, ...), Sprach und länderspezi�sche Information

• einem ö�entlichen Schlüssel

Die Metadaten enthalten den public key, Informationen über exportierte Typen (Name, Sichtbarkeit, Basisklasse,implementierte Interfaces, Members, Attribute), Abhängigkeiten zu anderen Assemblies und Sicherheitseinstel-lungen.

Abbildung 10.7: Der Assembly Resolver im Detail, Quelle: Assemblies Pakete einer .NET Anwendung

Abbildung 10.7 zeigt, wie die richtige Version einer Assembly gesucht wird.

Wenn verwalteter Code höhere Berechtigungen verlangt, ist es besser, diesen Code in einer anderen Assemblyals den Code zu speichern, für den die höheren Berechtigungen nicht erforderlich sind.

Bei der Assemblysicherheit unterscheidet man drei Arten:

• SAFE ist der Standardberechtigungssatz und die restriktivste Einstellung. Code, der von einer Assemblymit SAFE-Berechtigungen ausgeführt wird, kann nicht auf externe Systemressourcen wie z. B. Dateien,das Netzwerk, Umgebungsvariablen oder die Registrierung zugreifen. SAFE-Code kann auf Daten ausden lokalen SQL Server-Datenbanken zugreifen oder Berechnungen und Geschäftslogik ausführen, für diekein Zugri� auf Ressourcen auÿerhalb der lokalen Datenbanken erforderlich ist. Die meisten Assemblysführen Berechnungs- und Datenverwaltungsaufgaben aus, ohne dass ein Zugri� auf Ressourcen auÿerhalberforderlich ist. Aus diesem Grund wird empfohlen, SAFE als Berechtigungssatz für die Assembly zuverwenden.

• EXTERNAL_ACCESS ermöglicht Assemblys den Zugri� auf bestimmte externe Systemressourcenwie z. B. Dateien, Netzwerke, Webdienste, Umgebungsvariablen und die Registrierung. SAFE- undEXTERNAL_ACCESS-Assemblys können nur Code enthalten, der überprüfbar typsicher ist. Dies be-deutet, dass diese Assemblys auf Klassen nur über de�nierte Einstiegspunkte zugreifen können, die für die

79 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 80: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.3 Anwendungsdomäne (AppDomain)

Typde�nition gültig sind. Daher können sie nicht beliebig auf Speicherpu�er zugreifen, die sich nicht imBesitz des Codes be�nden.

• UNSAFE ermöglicht Assemblys den uneingeschränkten Zugri� auf Ressourcen. Code, der aus einerUNSAFE-Assembly ausgeführt wird, kann nicht verwalteten Code aufrufen. Wenn Sie UNSAFE ange-ben, kann der Code in der Assembly auÿerdem Vorgänge ausführen, die von der CLR-Überprüfung alstypunsicher betrachtet werden. UNSAFE-Assemblys können auÿerdem potenziell das Sicherheitssystemder CLR unterlaufen. UNSAFE-Berechtigungen sollten nur hochgradig vertrauenswürdigen Assemblysdurch erfahrene Entwickler oder Administratoren erteilt werden. Nur Mitglieder der festen Serverrollesysadmin können UNSAFE-Assemblys erstellen.

Immer dann, wenn Sie innerhalb einer Assembly auf Nicht-.Net-Code, wie z.B. COM-Komponenten zugreifenmöchten, müssen Sie leider UNSAFE wählen.

10.3 Anwendungsdomäne (AppDomain)

Anwendungsdomänen sind isolierte Bereiche im CLR-Prozess. Sie werden auch als leichtgewichtige Unterpro-zesse oder Pseudoprozesse bezeichnet. Wichtig ist, sie sind KEINE Prozesse! Innerhalb der CLR gibt es keineHardware-Kapselung. Anwendungsdomänen ...

• Schirmen Applikationen gegeneinander ab

• Umfassen (und isolieren) Assemblies, Module und Typen einer Applikation, Methodentabellen der gela-denen Typen und Statische Felder und Objekte der geladenen Typen

• Können entweder direkt im Code (System.AppDomain.CreateDomain) oder vom jeweiligen Host der CLRerzeugt werden.

Es gibt keine direkte Kommunikation zwischen Anwendungsdomänen. Es ist auch kein direkter Zugri�auf Assemblies anderer Anwendungsdomänen möglich. Die CLR verhindert die Übergabe von Objekt-Referenzen zwischen Anwendungsdomänen. Eine Interaktion ist nur über einen speziell gesicherten Inter-Domain-Kommunikationsmechanismus möglich.

Bei dem Prinzip der Anwendungsdomänen werden verschiedene Anwendungen oder Ausführungseinheiten in dengleichen Prozessraum geladen und in verschiedene Subprozesse gekapselt. Hierdurch entfallen Leistungeinbuÿendurch Prozesswechsel. Die .NET Laufzeitumgebung sorgt dafür, dass die einzelnen Anwendungsdomänen nichtin den Speicherbereich anderer Anwendungen im gleichen Prozessraum schreiben.

Typischerweise arbeitet man nur mit einer Anwendungsdomäne. Mehrere Anwendungsdomänen sind erforder-lich, wenn Code mit anderen Sicherheits-Einstellungen/Zugri�srechten geladen wird, die Isolation zwischenCode-Teilen explizit gewünscht wird, oder Code unabhängig voneinander terminieren können soll. Für Plug-Insbeispielsweise bieten sich separate Anwendungsdomänen an, also eine AppDomain pro Plug-In. Das Stichwortzur Kommunikation zwischen verschiedenen Anwendungsdomänen ist Remoting. Remoting ist die Bezeichnungfür (Netzwerk)komunikation der Programmiersprache C#. Kommunikation zwischen Objekten in verschiedenenAppDomains oder in Prozessen auf verschiedenen Computern wird ermöglicht.

Abbildung ??

Wie genau, die Kommunikation zwischen verschiedenen Anwendungsdomänen erfolgt beschreibt der ArtikelBaukasten für verteilte Anwendungen:

Marshalling:Sobald ein Aufruf eine Appdomain-Grenze überschreitet ist Marshalling notwendig.

Oder anders formuliert: Die Remoting-Mechanismen greifen. Bild ?? zeigt diesen Zusammenhang. Das Mars-halling 2 kann dabei entweder by Value oder by Reference erfolgen und es kommt die zweite,wichtige Regel insSpiel: Marshalling ist immer mit Serialisierung und Deserialisierung verbunden.

2Marshalling ist das Verpacken eines Programmcodeaufrufs in eine Nachricht, sodass ein Aufruf zwischen zwei Programmenmöglich ist, die keinen gemeinsamen Speicher besitzen.

80 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 81: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.4 Basisklassen

Abbildung 10.8: Beschreibung.

10.4 Basisklassen

Ein Namensraum ist ein logisches Benennungsschema zum Gruppieren von verwandten Typen. In einer Dateikönnen mehrere Klassen und mehrere Namensräume de�niert werden � dies erhöht sicher nicht die Übersicht.Namensräume und Klassen sind vom Verzeichnisbaum unabhängig! Mit dem Schlüsselwort using können in C#Namensräume in andere Namensräume importiert werden. Es ist auch möglich, Aliase zu vergeben. Namensräu-me sind hierarchisch organisiert. Mit using System werden alle ö�entlichen Typen von System importiert.Möchteman eine Klasse innerhalb von System verwenden, so ist diese in Bezug auf System voll anzugeben.

Die Basisklassen von .Net sind in solchen Namensräumen abgelegt. .Net Framework 4 and Extensions oder .NetFramework 4 Universe veranschaulichen die aktuelle Struktur und Neuerungen.

Aufgabe 10.2 Vergleichen Sie die Begri�e Namensraum in C# und Package in Java. Arbeiten Sie die Ge-meinsamkeiten und die Unterschiede heraus.

10.5 Typsystem von C#

Abbildung 10.9: Links: Common Type System, Rechts: value-type und reference-type

Betrachtet man das CTS, also die Gesamtzahl aller Typen, so kann man diese für C# wie folgt klassi�zieren:

81 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 82: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

10.5 Typsystem von C#

• Werttypen

� Enumeration (enum)

� Strukturen (struct)

� einfache Typen (sbyte, int, char, . . . � nicht string!)

• Referenztypen

� Klassen

� Felder

� Schnittstellen

� Delegate

Dies hat jedoch nichts mit der Parameterübergabe byref bzw. byval zu tun.

Zwischen den einfachen Typen gibt es die typischen impliziten Konvertierungen. Wir erinnern uns daran, dassalle Typen in C# von object abgeleitet sind. Dies erö�net uns zusätzliche Möglichkeiten: Jeder Datentyp kannals Objekt gespeichert oder übergeben werden.

Abbildung 10.10: Boxing und Unboxing

Der Boxing Vorgang beschreibt die Umwandlung von einem value-type zu einem Objekt. Bei dem BoxingVorgang wird die Instanz eines Objekts erstellt und der ursprüngliche Wert des value-types wird in das neueObjekt kopiert. Man könnte sagen, dies sei der umgekehrte Vorgang. Beim Unboxing Vorgang wird ein value typeaus einem Objekt extrahiert. Der C# Compiler überprüft zusätzlich, ob die von Ihnen angeforderte Variable,welche Sie aus dem Objekt extrahieren wollen, tatsächlich den angeforderten unboxed Type entsprechen kann.Folie 10.10 veranschaulicht den Vorgang an einem Beispiel.

Boxing und Unboxing ist ein zentraler Teil im Type-System von C# (und der Runtime). Es stellt ein Binde-glied zwischen value-types und reference-types zur Verfügung. Value-types haben den Vorteil, dass sie wenigSpeicherplatz benötigen. In manchen Fällen ist es jedoch von Vorteil, dass value-types auch die die Eigenschaf-ten von Objekten haben. Diese Übereinkunft macht den Hauptteil von C# aus, daÿ die Verknüpfung zwischenvalue-types und reference-types dadurch geschieht, dass ein value-type aus einem und in ein Objekt ungewandeltwerden kann. Unter dem Motto �Es ist zwar alles ein Objekt, aber nur dann wenn es eines sein muss.� Quelle:Datentypen

Zahlen - Werttypen:Es gibt Werttypen für Zahlen mit verschiedenen Gröÿen und Genauigkeiten. Eine gröÿere Zahl oder eine Zahlmit einer höheren Genauigkeit braucht mehr Speicher auf dem Stack. Jeder Werttyp hat eine Gröÿe. Es ist nichtmöglich, einen Wert eines gröÿeren Typs in eine kleinere Variable zu stecken, unabhängig davon, wie groÿ dieZahl tatsächlich ist.

1 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Page 83: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Listings

3.1 C# Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.2 Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.3 Stringreferenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.4 Stringreferenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.5 Strings mit @ - ähnlich Heredoc von PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.6 StringBuilder zur Verkettung von Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.7 String auf leer prüfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.8 is/as performant einsetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.9 Beispiel für Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.10 Generika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.11 Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.12 Enumerationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.13 Enumeration als Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.14 Trace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.15 Schlechter Code � Fehler schlucken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193.16 Indexer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.17 Iteratoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.18 Delegaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.19 Delegaten, Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.20 Multicastdelegaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.21 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253.22 Asynchrone Delegate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263.23 Benutzerde�niertes Attribute und Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.24 Ausdruckslambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.25 Anweisungslambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.26 Dispose-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313.27 using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333.28 Attribut Serializable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.1 Add-In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434.2 Add-In nutzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

6.1 Datenzugri� mit DataReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506.2 Update mit DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526.3 LINQ-Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556.4 Datenbindung an ein DataGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576.5 Datenbindung in WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586.6 Serverseitige Datenbindung in Asp.Net . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

7.1 Ajax-enabled WCF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647.2 ASP.Net MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

2

Page 84: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 1 von 10

C# Event Implementation Fundamentals, Best Practices and Conventions By Jeffrey Schaefer

Download source - 12.5 KB

Shortened, full article see http://www.codeproject.com/KB/cs/event_fundamentals.aspx

Terminology and Definitions

The literature presenting events and related concepts frequently makes use of multiple words

or expressions to describe any given concept. The following list catalogs much of this

terminology with brief explanations of the concepts behind the expressions. The term, event,

typically means that a change in state of an object has occurred or is about to occur. The term

is also used in reference to some activity taking place within an object or application --

activity like the processing of a gesture from an end user (e.g., button clicked), or the

reporting of progress during a long-running task. The term, state, refers to the current set of

values of one or more variables in an object or application. A change in state means the value

of one or more variables within an object has changed. In the event notification process,

changes in state, or expected changes in state, are primary motivations for raising events. So,

we have two ways to define an event relative to a change in state: immediately prior to a

change in state, or immediately after a change in state. While the former are referred to as pre-

events, the latter are referred to as post-events. event publisher, event source, subject

maintain their internal state, and notify other classes (subscribers) through the raising of

events or similar notification mechanisms. event subscriber, sink, listener, observer are the

classes or objects that are interested in changes in state (or expected changes in state) of the

event publishers. Event notifications (frequently expressed as, "fire an event" or "raise an

event" or "trigger an event") are generally in the form of the event publisher calling a method

in one or more subscribers. Consequently, the raising of an event ultimately means that code

in the event publisher causes code in one or more subscribers to run. When an event is raised,

the publisher will frequently include data (event data, event-related data, and event arguments

("event args"))that gets sent to the subscribers through the event notification process. This

data is presumably relevant to the particular event that was raised, and would be of interest to

the event subscribers.

For example, an event can be raised when a file gets renamed. Data relevant to that particular

"file renamed" event could include (1) the name of the file before the name was changed, and

(2) the name of the file after the name was changed. Those file names could comprise the

event data that are sent to the subscribers during the raising of the "file renamed" event.

To summarize; an "event handler" is the delegate upon which an event is based, while an

"event handling method" is a method called in the subscriber when an event is raised. Event

handlers are delegates, although delegates are not necessarily event handlers (there are many

uses of delegates beyond supporting events). Delegates are presented in more detail later in

this article, but only to the extent that they are relevant to events.

Events, as implemented in the .NET Framework and as described in this article, constitute

a .NET optimized implementation of the Observer Pattern that was documented by the "Gang

of Four" or "GoF" (Gamma et al.1995).

Page 85: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 2 von 10

Delegates

In order to understand events, as implemented in .NET applications, one must have a clear

understanding of the .NET delegate type and the role it plays in the implementation of

events.

Definition and Usage of Delegates

Delegates can be understood as intelligent containers that hold references to methods, as

opposed to containers that hold references to objects. Delegates can contain references to zero,

one, or many methods. In order for a method to be called by a particular delegate instance,

that method must be registered with the delegate instance. When registered, the method is

added to the delegate's internal collection of method references (the delegate's "invocation

list"). Delegates can hold references to static methods or instance methods in any class visible

to the delegate instance. Delegate instances can call their referenced methods either

synchronously, or asynchronously. When called asynchronously, the methods execute on a

separate thread. When a delegate instance is invoked ("called"), then all methods referenced

by the delegate are called automatically by the delegate. Delegates cannot contain references

to just any method. Delegates can hold references only to methods defined with a method

signature that exactly matches the signature of the delegate. Consider the following delegate

declaration:

public delegate void MyDelegate(string myString);

Notice that the delegate declaration looks like a method declaration, but with no method body.

The signature of the delegate determines the signature of methods that can be referenced by

the delegate. So, the sample delegate above (MyDelegate) can hold references only to

methods that return void while accepting a single string argument. Consequently, the

following method can be registered with an instance of MyDelegate:

private void MyMethod(string someString)

{

// method body here.

}

The following methods, however, cannot be referenced by a MyDelegate instance because

their signatures do not match that of MyDelegate.

private string MyOtherMethod(string someString)

{

// method body here.

}

private void YetAnotherMethod(string someString, int someInt)

{

// method body here.

}

After a new delegate type is declared, an instance of that delegate must be created so that

methods can be registered with, and ultimately invoked by, the delegate instance.

// instantiate the delegate and register a method with the new instance.

Page 86: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 3 von 10

MyDelegate del = new MyDelegate(MyMethod);

After a delegate is instantiated, additional methods can be registered with the delegate

instance, like this:

del += new MyDelegate(MyOtherMethod);

At this point, the delegate can be invoked, like this:

del("my string value");

And, because both MyMethod and MyOtherMethod are registered with the MyDelegate

instance (named del), that instance will invoke both MyMethod and MyOtherMethod when the

above line executes, passing each the string value, "my string value."

Delegates and Overloaded Methods

In the case of an overloaded method, only the particular overload having a signature that

exactly matches the signature of the delegate can be referenced by (or registered with) the

delegate. When you write code that registers an overloaded method with a delegate instance,

the C# compiler will automatically select and register the particular overload with a matching

signature.

So, for example, if your application declared the following delegate type...

public delegate int MyOtherDelegate(); // returns int, no parameters

... and you registered an overloaded method named MyOverloadedMethod with an instance of

MyOtherDelegate, like this...

anotherDel += new MyOtherDelegate(MyOverloadedMethod);

... the C# compiler will register only the particular overload with a matching signature. Of the

following two overloads, only the first would be registered with the anotherDel instance of

the MyOtherDelegate type:

// requires no parameters - so can be registered with a MyOtherDelegate

// instance.

private int MyOverloadedMethod()

{

// method body here.

}

// requires a string parameter - so cannot be registered with a

MyOtherDelegate instance.

private int MyOverloadedMethod(string someString)

{

// method body here.

}

A single delegate cannot selectively register or call both (multiple) overloads. If you need to

call both (multiple) overloads, then you would need additional delegate types -- one delegate

Page 87: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 4 von 10

type per signature. Your application-specific logic would then determine which delegate to

invoke, and therefore which overload is called (by the delegate with the corresponding

signature).

Why Delegates?

If this is your first introduction to delegates, you may be wondering, "Why bother? It's just

simpler to call the method directly -- so what is the benefit of going through a delegate?"

Necessary Indirection

A brief answer (to the "why bother?" question above) is that the code we write or components

we use cannot always "know" which specific method to call at a particular point in time. So,

one important perspective of delegates is that they provide a way for .NET components to call

your code -- without the .NET component having to know anything about your code beyond

the method signature (as mandated by the delegate type). For example, .NET Framework

components, like the Timer component, frequently need to execute code that you write.

Because the Timer component cannot possibly know which specific method to call, it

specifies a delegate type (and therefore signature of a method) to be invoked. Then you

connect your method -- with the requisite signature -- to the Timer component by registering

your method with a delegate instance of the delegate type expected by the Timer component.

The Timer component can then run your code by invoking the delegate which, in turn, calls

your method. Note that the Timer component still knows nothing about your specific method.

All the Timer component knows about is the delegate. The delegate, in turn, knows about

your method because you registered your method with that delegate. The end result is that the

Timer component causes your method to run, but without knowing anything about your

specific method.

Just like the Timer component example above, we can make use of delegates in a way that

enables us to write our code without our code having to "know" the specific method that will

ultimately be called at a specific point. Rather than calling a method at that point, our code

can invoke a delegate instance � which, in turn, calls any methods that are registered with the

delegate instance. The end result is that a compatible method is called even though the

specific method to be called was not written directly into our code.

Synchronous and Asynchronous Method Invocation

All delegates inherently provide for both synchronous and asynchronous method invocation.

So, another common reason to call methods via delegate instances is to invoke methods

asynchronously -- in which case the called method runs on a separate thread pool thread.

Event Foundation

As you'll see later in this article, delegates play an integral role in the implementation of

events in the .NET Framework. In brief, delegates provide a necessary layer of indirection

between event publishers and their subscribers. This indirection is necessary in order to

maintain a clean separation between the publisher and subscriber(s) � meaning that

subscribers can be added and removed without the publisher needing to be modified in any

way. In the case of event publication, the use of a delegate makes it possible for an event

Page 88: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 5 von 10

publisher to know nothing about any of its subscribers while still broadcasting events and

associated event data to any/all subscribers.

Delegate Internals

Declaring a Delegate Results in A New Class Being Created

A delegate declaration that you write is sufficient to define an entire and new delegate class.

The C# compiler takes your delegate declaration and inserts a new delegate class in the output

assembly. The name of that new class is the name of the delegate type you supply in your

delegate declaration. The signature you specify in your delegate declaration becomes the

signature of the methods in the new class used to call any/all of the delegate's referenced

methods (specifically the Invoke and BeginInvoke methods). This new class extends

(inherits) System.MulticastDelegate. So most of the methods and properties available in

your new delegate class come from System.MulticastDelegate. The Invoke, BeginInvoke,

and EndInvoke methods are inserted by the C# compiler when it creates the new class in the

output assembly (these are the methods you can call to cause the delegate to invoke any/all

referenced methods -- Invoke for synchronous invocation, and BeginInvoke and EndInvoke

used in asynchronous invocations).

The new class created from your delegate declaration can be understood as being a completed

and full-blown MulticastDelegate implementation that has the type name you supplied in

your delegate declaration, and is capable of calling methods with the specific signature that

you also supplied in your delegate declaration.

As an example, when the C# compiler encounters the following delegate declaration...

public delegate string MyFabulousDelegate(int myIntParm);

... the compiler inserts a new class named MyFabulousDelegate into the output assembly.

The Invoke, BeginInvoke, and EndInvoke methods of the MyFabulousDelegate class

include the int parameter and returned string value in their respective method signatures.

It should be noted that MulticastDelegate is a special class in that compilers can derive

from it, but you cannot derive from it explicitly. Your use of the C# delegate keyword and

associated syntax is how you instruct the C# compiler to extend MulticastDelegate for your

purposes.

Meaning of Multicast

The meaning of "multicast" in System.MulticastDelegate is that the delegate is capable of

holding references to multiple methods -- not just one method. In the case of delegate

instances that hold references to multiple methods, all referenced methods are called when the

delegate instance is invoked.

Finally, C and C++ programmers will recognize that delegates are similar to C-style function

pointers. An important difference, though, is that a delegate is not simply a pointer to a raw

memory address. Instead, delegate instances are type-safe objects that are managed by

the .NET CLR and that specifically reference one or more "methods" (as opposed to

memory addresses.

Page 89: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 6 von 10

The Relationship Between Delegates and Events

Events in .NET programming are based on delegates. Specifically, an event can be understood

as providing a conceptual wrapper around a particular delegate. The event then controls

access to that underlying delegate. When a client subscribes to an event, the event ultimately

registers the subscribing method with the underlying delegate. Then, when the event is raised,

the underlying delegate invokes each method that is registered with it (the delegate). In the

context of events, then, delegates act as intermediaries between the code that raises events and

the code that executes in response thereby decoupling event publishers from their subscribers.

Events do not, by themselves, maintain a list of subscribers. Instead, events control access to

some underlying list of subscribers and that list is typically implemented as a delegate

(although other list-type objects or collections can serve in place of a delegate).

Event Handlers (in general)

A delegate that exists in support of an event is referred to as an "event handler". To be clear,

an "event handler" is a delegate, although delegates are frequently not event handlers.

This article uses the expression, "event handler," only in reference to the delegate, while using

the expression, "event handling method," in reference to any method registered with the

delegate.

Custom Event Handlers

You can define your own event handlers (delegates), or you can use one of the event handlers

provided by the .NET Framework (i.e., System.EventHandler, or the generic

System.EventHandler<TEventArgs>). The following sample event declaration make use of

a custom event handler rather than using a Framework-provided event handler.

Consider the following:

Line 1: public delegate void MyDelegate(string whatHappened);

Line 2: public event MyDelegate MyEvent;

• Line 1 declares a delegate type for which any method can be assigned -- provided that

the method returns void and accepts a single string argument.

Line 2 declares an event in terms of the delegate type. Notice that the event (which is named

MyEvent) is declared very much like a method declaration -- but with its data type specified as

the delegate type:

• public scope specifying that objects outside of our class can subscribe to the event.

• event keyword used to define the event.

• MyDelegate data type of the event (this is the custom delegate type defined in Line

1.)

• MyEvent name of the event.

The delegate declared in Line 1 is just an ordinary delegate (as are all delegates), and can be

used for any purpose delegates can fulfill. Line 2 (i.e., the usage of the delegate type) is what

turns that delegate into an event handler. In order to communicate that a particular delegate

Page 90: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 7 von 10

type is being used as an event handler, a naming convention has emerged whereby the

delegate type name ends with "Handler" (more on this later).

Standardized Event Handlers

While you can create your own event handlers (and sometimes you might need to), you

should use one of the EventHandler delegates provided by the .NET Framework in cases

where one of the Framework's event handlers would work with your particular event

implementation. Many events make use of event handlers that can have common or identical

signatures. So, rather than clutter your source code with many delegates that differ only by

type name, you can/should make use of the built-in event handlers, as doing so reduces the

amount of code you would need to write and maintain, and makes your code more easily

understood. If someone reading your code sees you are basing an event on the

System.EventHandler delegate, for example, then they automatically know a lot about your

event implementation without having to look further.

The Generic System.EventHandler<TEventArgs> Delegate

The declaration of this built-in delegate enforces the constraint that the type, TEventArgs, be

of type System.EventArgs (including, of course, subclasses thereof):

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)

where TEventArgs : EventArgs;

Now suppose you want to strongly type the sender, rather than having it typed as object.

You can leverage generics to create your own generic event handler:

public delegate void MyGenericEventHandler<T, U>(T sender,

U u) where U : EventArgs;

You can then use this custom generic event handler to additionally specify a type-safe sender

parameter (i.e., thereby limiting the type of object that can be communicated as having raised

the event):

public event MyGenericEventHandler<MyPublisher, MyEventArgs> MyEvent;

The intent here would be that this event will only be raised by objects of type MyPublisher.

Subscribers to the event would therefore be able to subscribe only to events published by the

MyPublisher class.

Event Arguments (EventArgs)

Event arguments -- sometimes referred to as "event args" -- constitute the data sent by the

publisher of an event to the subscribers during the raising of the event. Presumably this data is

relevant to the occurrence of the event. For example, when a "file was just renamed" event is

raised, the event arguments would likely include the name of the file before the name change,

as well as the name of the file after the name was changed. The event handling methods can

read the event arguments (referred to as "event data") to learn more about the event

occurrence.

Page 91: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 8 von 10

The Role of System.EventArgs

You have two basic alternatives for including event arguments with your events.

1. You can encapsulate all event arguments as properties of a class that derives from

System.EventArgs. At runtime, an instance of that class is then sent to the event

subscribers when the event is raised. The event subscribers read the event arguments

as properties of that class.

2. You can avoid the use of System.EventArgs and, instead, declare individual event

arguments -- much as you would include arguments in a method declaration. This

approach is discouraged.

The first alternative listed above is strongly encouraged, and support for it is built into

the .NET Framework through the System.EventArgs class. Events implemented in .NET

Framework components, by convention, provide their event arguments as instances

System.EventArgs, or as event-specific subclasses of System.EventArgs.

Some events carry no data. In these cases, System.EventArgs is used as a placeholder,

primarily for purposes of keeping one consistent event handler signature across all events

regardless of whether the events carry data or carry no data. In cases of events with no data,

the event publisher sends the value, System.EventArgs.Empty, during the raising of the

event.

Event Declaration Syntax

Event Declaration Syntax Alternatives

The event keyword is used to formally declare an event. The C# compiler will translate the

declaration into the following three components in the output assembly.

1. Privately scoped event handler (or a functionally equivalent data structure). The

delegate is privately scoped in order to prevent external code from invoking the event,

and thereby preserving encapsulation.

2. publicly scoped Add method; used to add subscribers to the private event handler.

3. publicly scoped Remove method used to remove subscribers from the private event

handler.

Field-like syntax: public event TheEventHandler MyEvent;

The field-like syntax declares the event in one or two lines of code (one line for the event,

another for the asso ciated event handler -- if/when not using a built-in EventHandler

delegate). Alternatively, there exists a property-like syntax (details see full article).

Event Raising Code

For each event, the publisher should include a protected virtual method that is responsible for

raising the event. This will allow subclasses to [more] easily access base class events. Of

course the recommendation to make this method protected and virtual applies only to non

static events in unsealed classes.

protected virtual void OnMailArrived(MailArrivedEventArgs)

Page 92: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 9 von 10

{ // Raise event here}

Once an event and any associated delegate and publishing method have been defined, the

publisher will need to raise the event. Raising the event should generally be a two step process.

The first step would be to check to see if there are any subscribers. The second step is to raise

the event, but only if there are any subscribers.

Any unhandled exceptions raised in the event handling methods in subscribers will be

propagated to the event publisher. The raising of the event should therefore be attempted only

within a try/catch block:

public void RaiseTheEvent(MyEventArgs eventArgs)

{

try

{

MyEventHandler handler = MyEvent;

if (handler != null)

{

handler (this, eventArgs)

}

}

catch

{

// Handle exceptions here

}}

Events can have multiple subscribers each of which is called, in turn, by the event handler

(delegate) when the event handler is invoked by the [handler (this, eventArgs)] line.

The event handler used in the above block of code would stop iterating over it's invocation list

(of subscribed event handling methods) when the first unhandled exception is raised by a

subscriber. So, if there were 3 subscribers, for example, and the 2nd one threw an unhandled

exception when invoked by the delegate, then the 3rd subscriber would never receive the

event notification. If you want for every subscriber to receive the event notification even if

other subscribers throw unhandled exceptions, then you could use the following logic which

explicitly loops through the event handler's invocation list:

public void RaiseTheEvent(MyEventArgs eventArgs)

{

MyEventHandler handler = MyEvent;

if (handler != null)

{

Delegate[] eventHandlers = handler.GetInvocationList();

foreach (Delegate currentHandler in eventHandlers)

{

MyEventHandler currentSubscriber = (MyEventHandler)currentHandler;

try

{

currentSubscriber(this, eventArgs);

}

catch (Exception ex)

{

// Handle exception here.

}

}

}

}

Page 93: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

C# Event Implementation Fundamentals, Best Practices and Conventions Seite 10 von 10

Event Subscriber Registration and Unregistration

By design, the publisher of an event has absolutely no knowledge of any of the subscribers.

Consequently, it is the job of subscribers to register or unregister themselves with the

publisher of an event.

Registering A Subscriber

In order to subscribe to an event, the subscriber needs three things:

1. a reference to the object publishing the event of interest

2. an instance of the delegate upon which the event is defined

3. a method that will be called by the publisher when it raises the event

The subscriber then registers its event handler (delegate) instance with the publisher, like this:

thePublisher.EventName += new

MyEventHandlerDelegate(EventHandlingMethodName);

In the above line...

• thePublisher is the reference to the object that will raise the event of interest. Notice

how the event, EventName, is accessed as if it were a public property of

thePublisher.

• The += operator is used to add the delegate instance to the invocation list of the event

handler in the publisher. Remember, multiple subscribers may register with the event.

Use the += operator to append the current subscriber to the underlying delegate's

invocation list.

• MyEventHandlerDelegate is a reference the particular event hander delegate to be

used (if not one of the built-in EventHandler delegates).

• Finally EventHandlingMethodName supplies the name of the method in the

subscribing class that is to be called upon the raising of the event.

WARNING: Do not use the = operator when registering an event subscriber with a publisher.

Doing so would replace any/all currently registered event subscribers with the current

subscriber. Instead, be sure to use the += operator to cause the current subscriber to be

appended to the event handler's invocation list.

Unregistering A Subscriber

A subscriber can unregister from the publisher, like this:

thePublisher.EventName -=

EventHandlerDelegate(EventHandlingMethodName);

The -= operator is used to remove the delegate instance from the invocation list in the

publisher.

Subscribers are automatically unregistered when an object is disposed � if the subscriber was

not already explicitly unregistered from the event.

Page 94: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 95: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 96: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 97: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 98: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 99: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 100: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 101: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 102: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 103: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 104: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 105: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 106: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 107: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 108: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 109: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010
Page 110: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 1 von 12

Understanding Routed Events and Commands In WPF Brian Noyes, September 2008

http://msdn.microsoft.com/en-us/magazine/cc785480.aspx

One of the most daunting things about getting up to speed on Windows® Presentation

Foundation (WPF) is that there are so many new constructs to master. Even simple things like

Microsoft® .NET Framework properties and events have new counterparts in WPF with

added capabilities and associated complexity—specifically dependency properties and routed

events. Then there is all the brand new stuff, such as animations, styling, control templates,

and routed commands. There is a lot to learn.

In this article I am going to focus on two very important items in the list of new WPF

elements to master. These items—routed events and routed commands—are related to each

other. They form the basis for communication among the various parts of your user

interface—whether those parts are individual controls on one big Window class or whether

they are controls and their supporting code in separate, decoupled parts of your user interface.

For this article I am assuming you are already familiar with the fundamentals of WPF, such as

how to construct a UI using built-in WPF controls and declaring the layout of your UI in

XAML.

Routed Events Overview

When you are first getting started with WPF, you will likely use routed events without even

knowing you are using them. For example, if you add a button to your window in the Visual

Studio® designer and name it myButton and then double-click on it, the Click event will get

hooked up in your XAML markup and an event handler for the Click event will be added to

the codebehind of your Window class. This should feel no different than hooking up events in

Windows Forms and ASP.NET. It is actually a little closer to the coding model for ASP.NET

but more like the runtime model of Windows Forms. Specifically, in your XAML markup for

the button, you end up with code like this: <Button Name="myButton" Click="myButton_Click">Click Me</Button>

The XAML declaration for hooking up an event looks just like a property assignment in

XAML but results in a normal event hookup on the object that specified the event handler.

This hookup actually happens in a compile-time-generated partial class for your window. To

see this, go to the constructor for your class, right-click on the InitializeComponent method

call, and select Go To Definition from the context menu. The editor will display a generated

code file (with a naming convention of .i.g.cs or .i.g.vb) containing the code that is normally

generated at compile time. Scroll down in the displayed partial class to the Connect method

where you will see the following: #line 6 "..\..\Window1.xaml" this.myButton.Click += new

System.Windows.RoutedEventHandler( this.myButton_Click);

This partial class is generated from the XAML at compile time and contains those elements of

the XAML that need design-time compilation. Most of your XAML ends up as a binary-

embedded resource in your compiled assembly and is merged at run time with the compiled

code from the binary representation of your markup.

If you take a look at the codebehind for the window, the Click handler looks like this: private void myButton_Click( object sender, RoutedEventArgs e) { }

So far this looks like any other .NET event hookup—you have an explicitly declared delegate

hooked up to an event on an object, and the delegate points to a handling method. The only

thing to tip you off that you are using routed events is the type of the event argument for the

Page 111: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 2 von 12

Click event, which is RoutedEventArgs. What is so special about routed events then? To

understand that, you first need to understand the elemental composition model of WPF.

WPF Element Trees

If you start with a new window in a project and drag a button onto the window in the designer,

you end up with an element tree in the XAML that looks like this (attributes omitted for

clarity): <Window> <Grid> <Button/> </Grid> </Window>

Each of these elements represents a runtime instance of a corresponding .NET type, and the

declared hierarchy of elements forms what is called the logical tree. In addition, many

controls in WPF are either ContentControls or ItemsControls, meaning they can have child

elements. For example, a Button is a ContentControl, and it can have a complex child element

as its content. You could expand the logical tree, as you see here: <Window> <Grid> <Button> <StackPanel> <Image/> <TextBlock/> </StackPanel>

</Button> </Grid> </Window>

This results in a UI like that shown in Figure 1.

Figure 1 Simple Window with Button Content

As you can imagine, the tree could start to take on multiple

branches (another Button in the Grid), and the logical tree

can grow significantly in complexity. The thing to realize

about WPF elements in the logical tree is that what you see

is not really what you get at run time. Each of those elements

usually expands to a more complicated tree of visual elements at run time. In this example,

the logical tree of elements expands to the visual tree of elements shown in Figure 2.

Figure 2 Simple Window Visual Tree

I used a tool called Snoop (blois.us/Snoop) to see the

elements of the visual tree shown in Figure 2. You

can see that the window (EventsWindow) actually

wraps its content in a Border and an Adorner-

Decorator and presents the content inside of that with

a ContentPresenter. The button does something

similar, wrapping its content in a ButtonChrome

object and presenting the content with a

ContentPresenter.

When I click on my button, I may not actually be clicking on the Button element at all; I may

be clicking on a child element in the visual tree, possibly even one that is not shown in my

logical tree (such as the ButtonChrome). For example, say that I click the mouse on top of the

image inside my button. The click really manifests itself initially as a MouseLeftButtonDown

event inside the Image element. But somehow that needs to get translated into a Click event at

the Button level. This is where the routing in routed events comes in.

Page 112: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 3 von 12

Event Routing

Understanding a little about the logical and visual trees is important because routed events get

routed based primarily on the visual tree. Routed events support a RoutingStrategy of Bubble,

Tunnel, or Direct.

Bubble is the most common and means that an event will bubble (propagate) up the visual

tree from the source element until either it has been handled or it reaches the root element.

This allows you to handle an event on an object further up the element hierarchy from the

source element. For example, you could attach a Button.Click handler on the enclosing Grid

element instead of directly on the button itself. Bubble events just have names that indicate

their action (for example, MouseDown).

Tunnel events go in the other direction, starting at the root element and traversing down the

element tree until they are handled or reach the source element for the event. This allows

upstream elements to intercept the event and handle it before the event reaches the source

element. Tunnel events have their names prefixed with Preview by convention (such as

PreviewMouseDown).

Direct events behave like normal events in the .NET Framework. The only potential handler

for the event is a delegate that is hooked up to the event.

Usually if a Tunnel event is defined for a particular event, there is a corresponding Bubble

event. In that case, the Tunnel event fires first, starting at the root and working its way down

to the source element looking for a handler. Once it has been handled or has reached the

source element, the Bubble event is fired, working its way up from the source element and

looking for a handler. A Bubble or Tunnel event does not stop its routing just because an

event handler is called. If you want to stop the bubbling or tunneling process, you mark the

event as handled in your event handler using the event arguments you are passed: private void OnChildElementMouseDown(object sender, MouseButtonEventArgs e)

{ e.Handled = true; }

Once your handler marks an event as handled, it will not be raised to any more handlers. Well,

that is only partially true. In reality, event routing continues behind the scenes, and you can

explicitly hook up event handlers in code with an override of the UIElement.AddHandler

method that has an additional flag to effectively say, "Call me even if the event has been

marked as handled." You specify that flag with a call like the following: m_SomeChildElement.AddHandler(UIElement.MouseDownEvent,

(RoutedEventHandler)OnMouseDownCallMeAlways,true);

The first parameter to AddHandler is the RoutedEvent you want to handle. The second is a

delegate to the event-handling method (which will need to have the correct signature for the

event's delegate). The third parameter indicates whether you want to be notified even if

another handler has marked the event as handled. The element on which you call AddHandler

is the one that will be watching for the event to flow by during routing.

Routed Events and Composition

Let's walk through how the Button.Click event comes into being to drive home why all this is

important. As I mentioned before, a user will initiate a Click event with a

MouseLeftButtonDown event on some child element in the visual tree of your Button, such as

the Image in the previous example.

When the MouseLeftButtonDown event happens inside the Image element, a

PreviewMouseLeftButtonDown event starts at the root and tunnels down to the Image. If no

handlers set the Handled flag to true for the preview event, the MouseLeftButtonDown event

then starts bubbling up from the Image element until it gets to the Button. The button handles

that event, sets the Handled flag to true, and raises its own Click event. The sample code for

this article includes an application with handlers hooked up throughout the routing chain to

help you visualize this process.

Page 113: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 4 von 12

The implications are quite powerful. For example, if I choose to replace the default button

appearance by applying a control template that contains an Ellipse element, I don't have to do

anything to ensure that clicks outside the Ellipse don't fire the Click event. Clicks just outside

the edge of the Ellipse will still be inside the rectangular bounds of my button, but Ellipse has

its own hit detection for MouseLeftButtonDown, and the empty portions of the button outside

the Ellipse do not.

So only clicks inside the Ellipse raise the MouseLeftButtonDown event. It is still handled by

the Button class to which the template is attached, so you will get predictable behavior from

even your customized button. This is also an extremely important concept to remember when

writing your own custom composite controls because you will likely need to do things similar

to what Button is doing to handle events from the child elements that get placed inside your

control.

Attached Events

In order to enable elements to handle events that are declared in a different element, WPF

supports something called attached events. Attached events are routed events that support a

hookup in XAML on elements other than the type on which the event is declared. For

example, if you want the Grid element to listen for a Button.Click event to bubble past, you

would simply hook it up like the following: <Grid Button.Click="myButton_Click"> <Button Name="myButton" >Click

Me</Button> </Grid>

The resulting code in the compile-time-generated partial class now looks like this: #line 5 "..\..\Window1.xaml"

((System.Windows.Controls.Grid)(target)).AddHandler( System.Windows.Control

s.Primitives.ButtonBase.ClickEvent, new

System.Windows.RoutedEventHandler(this.myButton_Click));

Attached events simply give you a little more flexibility in where you hook up your event

handlers. But if the elements are contained in the same class (as in this example), it may not

be apparent what difference it makes because, in either case, the handling method is still just a

method on the Window class.

It matters in two ways. First, event handlers are called based on where the handling element is

in the bubbling or tunneling element chain. Second, this lets you do something like handle

events from objects that may be encapsulated down inside a control you are using. For

example, you could handle Button.Click events like those shown on the Grid, but those

Button.Click events could be bubbling out from inside of a user control contained in your

window. Not all events are declared as attached events. In fact, most are not. But attached

events can be quite handy when you need to do event handling somewhere else besides the

control's source.

Routed Commands Overview

Now that you have seen routed events, you are ready to understand routed commands. WPF-

routed commands give you a specific mechanism for hooking up UI controls such as toolbar

buttons and menu items to handlers without introducing a lot of tight coupling and repetitive

code into your application. Routed commands give you three main things on top of normal

event handling:

• Routed command source elements (invokers) can be decoupled from command targets

(handlers)—they do not need direct references to one another, as they would if they

were linked by an event handler.

• Routed commands will automatically enable or disable all associated UI controls when

the handler indicates the command is disabled.

Page 114: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 5 von 12

• Routed commands allow you to associate keyboard shortcuts and other forms of input

gestures (ink gestures, for example) as another way to invoke the command.

In addition, a specific flavor of routed command, the RoutedUICommand class, adds the

ability to define a single Text property to be used for the text prompting of any controls that

are invokers for the command. The Text property can also be localized more easily than

visiting each associated invoker control.

To declare a command on an invoker, you simply set a Command property on the control that

will fire the command: <Button Command="ApplicationCommands.Save">Save</Button>

The Command property is supported by MenuItem, Button, RadioButton, CheckBox,

Hyperlink, and a number of other controls.

For an element that you want to act as a command handler, you set up a CommandBinding: <UserControl ...> <UserControl.CommandBindings> <CommandBinding

Command="ApplicationCommands.Save" CanExecute="OnCanExecute"

Executed="OnExecute"/> </UserControl.CommandBindings> ... </UserControl>

The CanExecute and Executed properties of a command binding point to methods in the

codebehind of the declaring class that are called during the command-handling process. The

important thing here is that the command invoker does not need any knowledge of or

reference to the command handlers, and a handler does not have to know what element is

going to invoke the command.

CanExecute is called to determine whether or not the command should be enabled. To enable

the command, set the CanExecute property of the event arguments to true, as shown here: private void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)

{ e.CanExecute = true; }

A command is also enabled if there is a command handler with a defined Executed method

but no CanExecute method (CanExecute is implicitly true in that case). The Executed method

is where you take whatever action is appropriate based on that command being invoked. This

may be saving a document, submitting an order, sending an e-mail, or some other action with

which the command is associated.

Routed Command in Action

To make this more concrete and to quickly see the benefits of routed commands, let's look at a

simple example. In Figure 3 you see a simple UI with two input textboxes and a toolbar

button for performing a Cut action on the text in the textboxes.

Figure 3 Simple App with Cut Command

Toolbar Button To get this hooked up using events, you would

need to define a Click handler for the toolbar

button, and that code would need references to

the two textboxes. You would have to determine

which textbox has the focus and call appropriate

clipboard operations based on the text selection

in the control. You would also have to worry about enabling and disabling the toolbar button

at appropriate times based on where the focus is and whether anything was selected in the

textbox. Ugly, messy, tightly coupled code.

It doesn't sound too bad for this simple form, but what if those textboxes are now down inside

a user control or a custom control and your window codebehind doesn't have direct access to

them? You would either have to expose an API at your user control boundary to make

Page 115: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 6 von 12

hooking things up from the container possible or expose the textboxes publicly—neither

approach is ideal.

With commands, all you need to do is set the Command property on the toolbar button to the

Cut command that is defined in WPF and you are done: <ToolBar DockPanel.Dock="Top" Height="25"> <Button

Command="ApplicationCommands.Cut"> <Image Source="cut.png"/> </Button>

</ToolBar>

You could now run the app and see that the toolbar button is initially disabled. After you

select some text in one of the textboxes, the toolbar button becomes enabled and, if you click

it, the text is actually cut to the clipboard. And it would work for any textbox anywhere in

your UI. Wow—pretty cool, huh?

What is happening here is that the TextBox class implementation has a built-in command

binding for the Cut command and encapsulates the clipboard handling for that command (and

Copy and Paste as well) for you. So how does the command only get invoked on the in-focus

textbox, and how does the message get to the textbox to tell it to handle the command? That is

where the routed part of routed commands comes into play.

Command Routing

The difference between routed commands and routed events is in how the command gets

routed from the command invoker to the command handler. Specifically, routed events are

used under the covers to route messages between the command invokers and the command

handlers (through the command binding that hooks it into the visual tree).

There could be a many-to-many relationship here, but only one command handler will

actually be active at any given time. The active command handler is determined by a

combination of where the command invoker and command handler are in the visual tree, and

where the focus is in the UI. Routed events are used to call the active command handler to ask

whether the command should be enabled, as well as to invoke the command handler's

Executed method handler.

Usually, a command invoker looks for a command binding between its own location in the

visual tree and the root of the visual tree. If it finds one, the bound command handler will

determine whether the command is enabled and will be called when the command is invoked.

If the command is hooked up to a control inside a toolbar or menu (or, more generally, a

container that sets FocusManager.IsFocusScope = true), then some additional logic runs that

also looks along the visual tree path from the root to the focus element for a command binding.

In the simple application from Figure 3, what happens is this: because the Cut command

button is in a toolbar, CanExecute and Execute are handled by the TextBox instance that has

the focus. If the textboxes in Figure 3 were contained within a user control, then you would

have an opportunity to set up a command binding on the window, the user control that

contains the Grid, the Grid that contains the textboxes, or on the individual textboxes.

Whichever textbox has the focus will determine the end of the focus path originating from the

root.

An important thing to understand about the routing of WPF routed commands is that once a

single command handler is invoked, then no other handlers will be called. So if the user

control handles the CanExecute method, the TextBox CanExecute implementation will no

longer be called.

Defining Commands

Both the ApplicationCommands.Save and the ApplicationCommands.Cut commands are two

of many commands provided by WPF. The five built-in command classes in WPF along with

some examples of the commands they contain are shown in Figure 4.

Figure 4 WPF Command Classes

Page 116: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 7 von 12

Command Class Example Commands

ApplicationCommands Close, Cut, Copy, Paste, Save, Print

NavigationCommands BrowseForward, BrowseBack, Zoom, Search

EditingCommands AlignXXX, MoveXXX, SelectXXX

MediaCommands Play, Pause, NextTrack, IncreaseVolume, Record, Stop

ComponentCommands MoveXXX, SelectXXX, ScrollXXX, ExtendSelectionXXX

The XXX indicates a collection of operations such as MoveNext and MovePrevious. The

commands in each class are defined as public static (Shared in Visual Basic®) properties so

that you can easily hook them up. You can define your own custom commands easily by

following the same approach. I'll show an example of this a little later.

You can also use these commands with a shorthand notation, like the following: <Button Command="Save">Save</Button>

When you use this shortened version, a type converter in WPF will try to locate the named

command against the collection of built-in commands. The net result in this case is exactly the

same. I prefer to use the long-named versions to make the code more explicit and

maintainable. Then there is no ambiguity as to where that command is defined. Even the built-

in commands have some duplication between the EditingCommands and

ComponentCommands classes.

Command Plumbing

Routed commands are a specific implementation of the ICommand interface defined by WPF.

ICommand has the following definition: public interface ICommand { event EventHandler CanExecuteChanged; bool

CanExecute(object parameter); void Execute(object parameter); }

The built-in WPF command types are RoutedCommand and RoutedUICommand. Both of

these classes implement the ICommand interface and use routed events I described earlier to

do the routing.

A command invoker is expected to call CanExecute to determine whether to enable or disable

any associated command invocation code. The command invoker can determine when to

invoke that method by subscribing to the CanExecuteChanged event. In the RoutedCommand

class, CanExecuteChanged is fired based on changes of state or focus in the UI. When the

command is invoked, the Executed method is called and gets dispatched to the handler

through a routed event along the visual tree.

Classes that support the Command property, such as the ButtonBase class, implement the

ICommandSource interface: public interface ICommandSource { ICommand Command { get; } object

CommandParameter { get; } IInputElement CommandTarget { get; } }

The Command property associates the invoker with the command it will invoke.

CommandParameter allows the invoker to pass some data along with the invocation of the

command. The CommandTarget property lets you override the default routing based on the

focus path and tell the commanding system to use the specified element as the handler of the

command, instead of leaving it up to the routed event and focus-based determination of the

command handler.

Routed Command Challenges

Routed commands work nicely for simple user interface scenarios, hooking up toolbar and

menu items, and handling those things that are inherently coupled to the keyboard focus (such

as clipboard operations). Where routed commands are insufficient, however, is when you start

building complex user interfaces, where your command handling logic is in supportng code

for your view definitions and your command invokers are not always inside of a toolbar or

Page 117: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 8 von 12

menu. This often comes up when using UI composition patterns such as Model View

Controller, or MVC (msdn.microsoft.com/magazine/cc337884), Model View Presenter, or

MVP (msdn.microsoft.com/magazine/cc188690), or Presentation Model, which is also known

as Model View ViewModel in WPF circles (msdn.microsoft.com/library/cc707885).

The problem when you get into that arena is that the enabling and handling logic for a

command may not be part of the visual tree directly; rather, it may be located in a presenter or

presentation model. Additionally, the state that determines whether the command should be

enabled may have no relation to where the command invokers and views are in the visual tree.

You may also have scenarios where more than one handler is valid at a given time for a

particular command.

To see where you might get yourself in trouble with routed commands, take a look at Figure

5. It is a simple window that contains a couple of user controls that represent views in an

MVP or MVC pattern. The main window contains a File menu and toolbar with Save

command buttons in them. The main window also has an input textbox at the top of the

window, along with a Button that has its Command set to the Save command.

Figure 5 Composite User

Interface (Click the image

for a larger view)

Tip: Hooking Up Anonymous

Methods

In the code in Figure 6 I use

a trick learned from my

colleague Juval Lowy—

hooking up an empty

anonymous method to the

delegate in its declaration: Action<string>

m_ExecuteTargets =

delegate { };

By doing so, you never have

to check for null before invoking the delegate because there will always be one no-op

subscriber in its invocation list. You also avoid a potential race condition with unsubscribing

in a multithreaded environment if you were doing null checking instead.

For more details on that trick, see Programming .NET Components, Second Edition, by Juval

Lowy.

The rest of the UI is provided by two views, and each is an instance of a simple user control.

The border color has been set differently for each user control instance in order to make it

easy to see what portion of the UI they provide. Each of the user control instances has a Save

button that has its Command property set to the Save command.

The challenge introduced by routed commands being tied to a location in the visual tree

becomes apparent in this simple example. In Figure 5, the window itself does not have a

CommandBinding for the Save command. However, it does contain two invokers (the menu

and toolbar) for that command. In this situation, I don't want the top-level window to have to

know what to do when the command is invoked. I want to leave it up to the child views,

represented by the user controls, to handle the command. The user control class in this

example has a CommandBinding for the Save command, and that CommandBinding returns

true for CanExecute.

Page 118: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 9 von 12

However, in Figure 5 you can see that while the focus is at the window level in the textbox at

the top, the command invokers at this level are disabled. Also, the Save buttons in the user

controls are enabled even though the focus is not yet inside the user control.

If you change the focus to one of the textboxes inside one of the user control instances, then

the command invokers in the menu and toolbar become enabled. But the Save button in the

window itself does not become enabled. In fact, in this case there is no way to get the Save

button next to the textbox at the top of the window to enable with normal routing.

The reason again has to do with the location of the individual controls. With no command

handlers at the window level, while the focus is outside of the user controls, there is no

command handler up the visual tree or on the focus path to enable the controls that are hooked

up as command invokers. The command is therefore disabled by default as far as those

controls are concerned. However, for the command invokers inside the user controls, the

command is enabled because the handler is up the visual tree from them.

Once you switch the focus to a textbox inside one of the user controls, the user control, which

is between the window and the textbox on the focus path, provides the command handler that

enables the command for the toolbar and menu, because they check the focus path as well as

the path from their location in the visual tree to the root. And there is no way to enable the

button at the window level because it will never have a handler between it and the root.

If trying to follow all that visual tree and focus-path mumbo jumbo made your head hurt for

this simple little example, imagine what you will have to go through to reason out command

enabling and invoking for a seriously complicated UI with command invokers and handlers in

many different locations in the visual tree. Let's just say the movie Scanners and exploding

heads come to mind.

Avoiding Command Problems

To avoid the potential visual tree location problems with routed commands, you'll need to

keep things simple. You will generally need to make sure your command handlers are on the

same element or further up the visual tree from the element that will invoke the command.

One way to do this is to inject a command binding at the window level by using the

CommandManager.RegisterClassCommandBinding method from the control contributing a

command handler.

The exception to this is if you implement a custom control that itself accepts keyboard focus

(like a textbox). In that case, if you want to embed command handling on the control itself

and that command handling is only relevant when the focus is on your control, then you can

do so and it will work out just like the Cut command example shown earlier.

You can also overcome focus issues by explicitly specifying a command handler through the

CommandTarget property. For example, for the window-level Save button that was never

enabled in Figure 5, you could change its command hookup to the following: <Button Command="Save" CommandTarget="{Binding ElementName=uc1}" Width="75"

Height="25">Save</Button>

In this code, the Button specifically sets its CommandTarget to a UIElement instance that has

a command handler in it. In this case, it is specifying the element named uc1, which happens

to be one of the two user control instances in the example. Because that element has a

command handler that always returns CanExecute = true, the Save button at the window level

becomes always enabled and will only call that control's command handler, regardless of

where the invoker is relative to the command handler.

Going beyond Routed Commands

As a result of the limitations of routed commands, a number of companies building complex

UIs with WPF have started using custom ICommand implementations that allow them to

Page 119: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 10 von 12

provide their own routing mechanisms, particularly ones that are not tied to the visual tree and

that can support multiple command handlers.

Creating a custom command implementation isn't hard. You implement the ICommand

interface on a class, provide a way for command handlers to hook up, and then do the routing

when the command is invoked. You also must decide what criteria you will use for

determining when to raise the CanExecuteChanged event.

A good starting point for creating a custom command is to use delegates. A delegate already

supports invocation of a target method, and it also supports multiple subscribers.

Figure 6 shows a custom command class called StringDelegateCommand that uses delegates

to allow multiple handlers to hook up. It supports passing a string argument to the handlers,

and it will use the CommandParameter of the invoker to determine what message is passed to

the handlers.

Figure 6 Custom Command Class public class StringDelegateCommand : ICommand

{

Action<string> m_ExecuteTargets = delegate { };

Func<bool> m_CanExecuteTargets = delegate { return false; };

bool m_Enabled = false;

public bool CanExecute(object parameter)

{

Delegate[] targets = m_CanExecuteTargets.GetInvocationList();

foreach (Func<bool> target in targets)

{

m_Enabled = false;

bool localenable = target.Invoke();

if (localenable)

{

m_Enabled = true;

break;

}

}

return m_Enabled;

}

public void Execute(object parameter)

{

if (m_Enabled)

m_ExecuteTargets(parameter != null ? parameter.ToString() : null);

}

public event EventHandler CanExecuteChanged = delegate { };

...

}

You can see I've chosen to use a Func<bool> delegate to hook up the handlers that will

determine whether the command is enabled or not. In the implementation of CanExecute, the

class loops through the handlers hooked up to the m_CanExecuteTargets delegate and sees

whether any one handler wants to execute. If so, it returns true for the

StringDelegateCommand to be enabled. When the Execute method is called, it simply checks

to see if the command is enabled and, if so, invokes all the handlers hooked up to the

m_ExecuteTargets Action<string> delegate.

To hook up handlers to the CanExecute and Execute methods, the StringDelegateCommand

class exposes the event accessors shown in Figure 7 to allow handlers to simply subscribe or

unsubscribe from the underlying delegates. Notice that the event accessor also gives you the

opportunity to trigger the CanExecuteChanged event whenever a handler subscribes or

unsubscribes.

Figure 7 Command Event Accessors public event Action<string> ExecuteTargets

{

add { m_ExecuteTargets += value; }

Page 120: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 11 von 12

remove { m_ExecuteTargets -= value; }

}

public event Func<bool> CanExecuteTargets

{

add

{

m_CanExecuteTargets += value;

CanExecuteChanged(this, EventArgs.Empty);

}

remove

{

m_CanExecuteTargets -= value;

CanExecuteChanged(this, EventArgs.Empty);

}

}

A Routed Handler Sample

I've got this class hooked up in a sample application in the code download. The sample has a

simple view with a presenter behind it (along the lines of MVP, but without the model). The

presenter exposes a presentation model to the view for data binding (you can think of a

presentation model as sitting between a presenter and a view, whereas the model of MVP sits

behind the presenter from the view). The presentation model typically exposes properties to

which the view can data bind. In this case, it just exposes a single property for the command

so that it can be easily hooked up in the XAML of the view through data binding: <Window x:Class="CustomCommandsDemo.SimpleView" ...> <Grid> <Button

Command="{Binding CookDinnerCommand}" CommandParameter="Dinner is

served!" ...>Cook Dinner</Button> <Button Click="OnAddHandler" ...>Add Cook

Dinner Handler</Button> </Grid> </Window>

The Binding statement will just look for a property on the current DataContext, named

CookDinnerCommand, and will try to cast that to an ICommand if it finds one. The

CommandParameter was mentioned before, and it is a way for the invoker to pass some data

along with the command. In this case, notice that I just pass the string that will be passed to

the handlers through the StringDelegateCommand.

The codebehind of the view (the Window class) is shown here: public partial class SimpleView : Window { SimpleViewPresenter m_Presenter

= new SimpleViewPresenter(); public SimpleView() { InitializeComponent();

DataContext = m_Presenter.Model; } private void OnAddHandler(object sender,

RoutedEventArgs e) { m_Presenter.AddCommandHandler(); } }

The view constructs its presenter, gets the presentation model from the presenter, and sets that

as the DataContext. It also has the button Click handler, which calls into the presenter, asking

it to add a handler for the command.

Composite Events and Commands

I've been working with the Microsoft patterns & practices group this year to help develop

Composite Application Guidance for WPF, which is a set of guidance for developing complex

composite applications in WPF. This set of guidance contains libraries, called the Composite

Application Libraries (CAL), offering services and helper classes for composite applications.

Read more about Composite Application Guidance for WPF in Glenn Block's article,

"Patterns for Building Composite Applications with WPF," at

msdn.microsoft.com/magazine/cc785479.

Figure 8 shows this application in action. The first window is in the initial state with no

command handlers hooked up. You can see that the first button (the invoker) is disabled

because there are no command handlers. Then when you press the second button, it calls into

the presenter and hooks up a new command handler. The first button is then enabled, and

when you click it, it invokes the command handler to which it is loosely coupled through a

data binding and the underlying command's subscriber list.

Page 121: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Understanding Routed Events and Commands In WPF Seite 12 von 12

Figure 8 Custom Command

Sample in Action (Click the

image for a larger view)

The presenter code is shown

in Figure 9. You can see that

the presenter constructs a

presentation model and

exposes it to the view through a Model property. When AddCommandHandler is called from

the view (in response to the second button Click event), it adds a subscriber to the

CanExecuteTargets and ExecuteTargets events on the model. These subscription methods are

simple methods located in the presenter that return true and display a MessageBox,

respectively.

Figure 9 Presenter Class public class SimpleViewPresenter { public SimpleViewPresenter() { Model =

new SimpleViewPresentationModel(); } public SimpleViewPresentationModel

Model { get; set; } public void AddCommandHandler()

{ Model.CookDinnerCommand.CanExecuteTargets += CanExecuteHandler;

Model.CookDinnerCommand.ExecuteTargets += ExecuteHandler; } bool

CanExecuteHandler() { return true; } void ExecuteHandler(string msg)

{ MessageBox.Show(msg); } }

This example shows that a combination of data binding, UI patterns, and custom commands

can give you a clean, decoupled approach for commands without being tied to the limitations

of routed commands. Because the command is hooked up in the XAML through a binding,

you can even extend this approach to define your views with XAML only (no codebehind),

use bound commands from the XAML to trigger actions in your presentation model, and

initiate the action you would have normally done in codebehind from your presentation model

instead.

You'll need a controller for constructing the views and providing them with their presentation

model, but you would be able to write interactive views without the need for codebehind. If

you have no need for codebehind, there is much less opportunity for you to add tightly

coupled and untestable spaghetti code in your codebehind file, as so often happens in UI apps.

This approach is just beginning to be explored in WPF. But it is definitely something to

consider, and you should be on the lookout for more examples.

Brian Noyes is chief architect of IDesign (www.idesign.net), a Microsoft Regional Director

(www.theregion.com), and a Microsoft MVP. He is the author of Developing Applications

with Windows Workflow Foundation, Smart Client Deployment with ClickOnce, and Data

Binding with Windows Forms 2.0. He is also a frequent speaker at industry conferences

worldwide. Contact Brian through his blog at briannoyes.net.

Page 122: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Contrasting the ADO.NET DataReader and DataSet Seite 1 von 6

Contrasting the ADO.NET DataReader and DataSet John Papa

http://msdn.microsoft.com/en-us/magazine/cc188717.aspx

Magazine Issues 2004 June

I am often asked by developers whether the ADO.NET DataReader or the DataSet is the

better tool. Some developers say the DataReader is better because it is lightweight, while still

others say they prefer the DataSet for its inherent flexibility. The truth is that both have their

place in Microsoft® .NET development as their usefulness depends on the situation.

The ADO 2.x recordset object is able to operate in either a connected or a disconnected mode.

It can remain connected to the underlying database while traversing a forward-only rowset or

it can retrieve a rowset into a client-side, in-memory cursor and disconnect itself from the

database. Among the hurdles you may well encounter in migrating from classic ADO to

ADO.NET is gaining a full understanding of how the operations that the ADO recordset

performed are now handled in ADO.NET.

Instead of a single rowset container, ADO.NET offers two distinctly separate data storage

objects: the DataReader and the DataSet. This month I'll focus on the purpose of these two

data retrieval classes in ADO.NET and help you to decide which is the best choice to use in a

particular situation. I will explore how to retrieve data into both the DataReader and the

DataSet, beginning by discussing the DataReader's unique capabilities. I will also compare the

connected DataReader to the disconnected DataSet, weighing the pros and cons of using each

in different scenarios.

Being Connected

Before deciding when to use a DataReader, it is smart to understand its features and

limitations. The DataReader has a defined set of operations that revolve around its connected,

forward-only, read-only nature (the read-only DataReader is also known as the firehose cursor

of ADO.NET). A DataReader is a stream of data that is returned from a database query. When

the query is executed, the first row is returned to the DataReader via the stream. The stream

then remains connected to the database, poised to retrieve the next record. The DataReader

reads one row at a time from the database and can only move forward, one record at a time.

As the DataReader reads the rows from the database, the values of the columns in each row

can be read and evaluated, but they cannot be edited.

Unlike the DataSet, the DataReader is not implemented directly in the System.Data

namespace. Rather, the DataReader is implemented through a specific managed provider's

namespace such as System.Data.SqlClient.SqlDataReader. Because all DataReaders,

including the OleDbDataReader, the SqlDataReader, and other managed provider's

DataReaders implement the same IDataReader interface, they should all provide the same

base set of functionality. Each DataReader is optimized for a specific data provider. If the

database you are developing against has a managed provider for ADO.NET, then you should

take advantage of it. Otherwise, you can use the System.Data.OleDb or the System.Data.Odbc

namespaces, which expose more generic managed providers that can access a variety of data

sources. If you are developing against SQL Server™ or Oracle, it would be more efficient to

use the provider that was made specifically for these databases. In this column, I will query

the SQL Server Northwind database using the System.Data.SqlClient namespace.

The fact that the SqlDataReader is part of a specific managed provider's feature set further

differentiates it from the DataSet. The SqlDataReader can only retrieve one row at a time

from the data source and in order for it to get the next record, it has to maintain its connection

to the data source. The DataSet, however, doesn't need to know about where it gets its data.

Page 123: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Contrasting the ADO.NET DataReader and DataSet Seite 2 von 6

The DataReader can only get its data from a data source through a managed provider. The

DataSet can also get its data from a data source via a managed provider, but the data source

can also be loaded manually, even from an XML file on a hard drive. If the .NET Framework

does not provide a managed provider that is specifically designed for your database, it is

certainly worth checking to see if the manufacturer or a third party has one available since

they should perform better than the generic OLE DB and ODBC providers.

In ASP.NET, DataReader objects can be used for more robust situations such as binding

themselves to an ASP.NET DataGrid or a DropDownList server control. The following code

demonstrates how to retrieve a list of products from the Northwind database using a

SqlDataReader object: string sSQL = "SELECT * FROM Products";

string sConnString =

"Server=(local);Database=Northwind;Integrated Security=SSPI;";

using (SqlConnection oCn = new SqlConnection(sConnString))

{

SqlCommand oSelCmd = new SqlCommand(sSQL, oCn);

oSelCmd.CommandType = CommandType.Text;

oCn.Open();

SqlDataReader oDr = oSelCmd.ExecuteReader();

DataGrid1.DataSource = oDr;

DataGrid1.DataBind();

}

Both a SqlConnection and a SqlCommand object are created. The SqlConnection is opened

and the SqlCommand object executes the SQL query, returning the first row to the

SqlDataReader. At this point the connection to the database is still open and associated with

the SqlDataReader. This code shows how a SqlDataReader can be bound to a bindable object

such as an ASP.NET DataGrid.

Alternatively, a DataReader could be used to retrieve the rows and then loop through them

manually, one by one. It can support several resultsets as well. For example, a list of products

and categories could be retrieved from a database. The following code retrieves a

SqlDataReader and loops through its rows, writing the first column's value for each row to the

console: SqlDataReader oDr = oCmd.ExecuteReader();

while(oDr.Read()) {

Console.WriteLine(oDr[0]);

}

Supporting Multiple Resultsets

The DataReader supports access to multiple resultsets, one at a time, in the order they are

retrieved. This code is easily modified to handle multiple resultsets. The following code

retrieves a SqlDataReader and loops through its rows, again writing the first column's value

for each row to the console: SqlDataReader oDr = oCmd.ExecuteReader();

do {

while(oDr.Read())

{

Console.WriteLine(oDr[0]);

}

Console.WriteLine(oDr[0]);

}

while(oDr.NextResult());

Once all of the rows from the first resultset are traversed, the rowset from the second query is

retrieved and its rows are traversed and written. This process can continue for several

resultsets using a single SqlDataReader.

Page 124: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Contrasting the ADO.NET DataReader and DataSet Seite 3 von 6

The Read method of the SqlDataReader loads the next record so that it can be accessed and

moves the position of the cursor ahead. It returns a Boolean value indicating the existence of

more records. This feature can help circumvent a common problem in classic ADO

development: the endless loop. In classic ADO, when looping through a recordset object

developers would often omit the MoveNext method and run the code only to remember a

second too late that this would cause the recordset to loop infinitely on the same record.

ADO.NET is kind to developers as its DataReader object's Read method automatically moves

the position to the next record so this situation can't occur. The NextResult method of the

SqlDataReader object retrieves the subsequent rowset and makes it accessible to the

SqlDataReader. It also returns a Boolean value indicating if there are additional resultsets to

traverse, like the Read method does.

The DataReader in the previous code sample shows how to get the value for a column from

the DataReader using its ordinal index position. Can the DataReader be indexed by the

column name or can the index of the column be retrieved? The answer to both of these

questions is yes. The code in Figure 1 shows how a DataReader retrieves data and displays

the CompanyName for each row in the diagnostics' output window.

Figure 1 Retrieving and Displaying Data string sConnString =

"Server=(local);Database=Northwind;Integrated Security=SSPI;";

using(SqlConnection oCn = new SqlConnection(sConnString))

{

SqlCommand oCmd = new SqlCommand("SELECT * FROM Customers", oCn);

oCn.Open();

SqlDataReader oDr =

oCmd.ExecuteReader(CommandBehavior.CloseConnection);

while(oDr.Read())

{

System.Diagnostics.Debug.WriteLine("Company Name " +

oDr["CompanyName"]);

System.Diagnostics.Debug.WriteLine("Company Name: name = " +

oDr.GetName(1));

System.Diagnostics.Debug.WriteLine("Company Name: index = " +

oDr.GetOrdinal("CompanyName"));

}

}

To demonstrate these techniques, this code displays the CompanyName value, column name,

and index. The first line in the loop writes the CompanyName using the value representing the

name of the CompanyName column. This could also have been accomplished by passing the

index of 1. I avoid this as it is less clear which column you are accessing, although using the

string value is slower than using the index. The second line in the loop writes the name of the

column at position 1 (CompanyName) using the GetName method. The third line gets the

index of the CompanyName column using the GetOrdinal method of the SqlDataReader. You

might also notice that the CommandBehavior is set to CloseConnection, ensuring that the

connection will be closed when the SqlDataReader is closed. These methods are simple but

they make the SqlDataReader a power tool.

The Disconnected Side

The DataSet is the main data storage tool in the ADO.NET disconnected architecture. Unlike

the DataReader, the DataSet is not connected directly to a database through a Connection

object when you populate it. Instead, to fill a DataSet from a database you first create a

DataAdapter object (such as a SqlDataAdapter) for the provider and associate it with a

SqlConnection object. Then the SqlDataAdapter can broker the data retrieval for the DataSet

Page 125: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Contrasting the ADO.NET DataReader and DataSet Seite 4 von 6

by issuing a SqlCommand against the database through the SqlConnection, retrieving the data,

and filling the DataSet.

You can think of the SqlDataAdapter as a bridge between the connected and disconnected

objects. One of its purposes is to serve as the route for a rowset to get from the database to the

DataSet. For example, when the SqlDataAdapter's Fill method is executed it opens its

associated SqlConnection object (if not already open) and issues its associated SqlCommand

object against the SqlConnection. Behind the scenes, a SqlDataReader is created implicitly

and the rowset is retrieved one row at a time in succession and sent to the DataSet. Once all of

the data is in the DataSet, the implicit SqlDataReader is destroyed and the SqlConnection is

closed.

The following code shows how a DataSet can be filled from the Products table of the

Northwind database. Notice that there is no explicit SqlDataReader object in this code

sample: string sSQL = "SELECT * FROM Products";

string sConnString =

"Server=(local);Database=Northwind;Integrated Security=SSPI;";

SqlDataAdapter oDa = new SqlDataAdapter();

DataSet oDs = new DataSet();

using(SqlConnection oCn = new SqlConnection(sConnString))

{

SqlCommand oSelCmd = new SqlCommand(sSQL, oCn);

oSelCmd.CommandType = CommandType.Text;

oDa.SelectCommand = oSelCmd;

oDa.Fill(oDs, "Products");

}

The DataSet can read and load itself from an XML document as well as export its rowset to

an XML document. Because the DataSet can be represented in XML, it can be easily

transported across processes, a network, or even the Internet via HTTP. Unlike the

DataReader, the DataSet is not read-only. A DataSet can be modified, and rows can be added

or deleted. Changes to a DataSet can be sent to the database via a managed provider's objects.

Another key difference between the DataSet and the DataReader is that the DataSet is fully

navigable. Its rows can be traversed forward or backward. The DataReader can be traversed

forward only. In addition, a DataSet is highly flexible in that its DataTable objects can be

filtered vertically or horizontally and they can be sorted or even searched. The DataSet is

independent of any one data provider as it relies on a DataAdapter specific to each provider to

broker the data between the DataSet and the database.

Not only can the DataSet be loaded from XML, it can also be loaded manually. Notice in

Figure 2 how a DataTable is created and its columns added manually. A primary key

constraint is established as well as an auto-incrementing value for the key field. Then the

DataTable is added to an empty DataSet (though is doesn't have to be empty) and the rows are

added one by one to the DataTable, all without ever connecting to a data source.

Figure 2 Creating a DataSet Manually //-- Create the table

DataTable oDt = new DataTable("Employees");

DataRow oRow;

oDt.Columns.Add("EmployeeID", System.Type.GetType("System.Int32"));

oDt.Columns.Add("FirstName", System.Type.GetType("System.String"));

oDt.Columns.Add("LastName", System.Type.GetType("System.String"));

oDt.Constraints.Add("PK_Employees", oDt.Columns["EmployeeID"], true);

oDt.Columns["EmployeeID"].AutoIncrement = true;

oDt.Columns["EmployeeID"].AutoIncrementSeed = -1000;

oDt.Columns["EmployeeID"].AutoIncrementStep = -1;

oDs.Tables.Add(oDt);

//-- Add the rows

oRow = oDs.Tables["Employees"].NewRow();

Page 126: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Contrasting the ADO.NET DataReader and DataSet Seite 5 von 6

oRow["FirstName"] = "Haley";

oRow["LastName"] = "Smith";

oDs.Tables["Employees"].Rows.Add(oRow);

oRow = oDs.Tables["Employees"].NewRow();

oRow["FirstName"] = "Madelyn";

oRow["LastName"] = "Jones";

oDs.Tables["Employees"].Rows.Add(oRow);

//-- Bind it to a DataGrid

grdEmployees.DataSource = oDs.Tables["Employees"];

Because the DataSet is disconnected, its use can reduce the demand on database servers. It

does, however, increase the memory footprint in the tier where it is stored, so be sure to

account for that when designing around a DataSet as your data store. Scaling up on the middle

tier using parallel servers and load balancing is a common way to handle the increased load so

that session-based information can be stored in objects such as the DataSet.

When to Use the DataSet

Your situation will dictate when and where you'll use a DataSet versus a DataReader. For

example, the DataSet's disconnected nature allows it to be transformed into XML and sent

over the wire via HTTP if appropriate. This makes it ideal as the return vehicle from business-

tier objects and Web services. A DataReader cannot be serialized and thus cannot be passed

between physical-tier boundaries where only string (XML) data can go.

DataSet objects are a good choice when the data must be edited or rows added or deleted from

the database. It is not the only choice, however, as a DataReader could be used to retrieve the

data and changes would be sent to the database via a SqlDataAdapter through a separate, self-

maintained DataRow array. That process can be quite messy because the SqlDataReader

cannot allow edits as the DataSet can. As mentioned earlier, the DataSet is also a good choice

when you need data manipulation such as filtering, sorting, or searching. Since the DataSet

can be traversed in any direction, all of these features are available. This flexibility also makes

the DataSet an easy choice when the situation calls for multiple iterations of the rowset. A

DataReader can move forward only, so to loop through it more than once, the DataReader

would be closed and reopened and the query would hit the database a second time.

If a rowset is intended to be bound to a single ASP.NET server control and the data is read-

only, the DataReader could suffice. If a rowset is intended to be bound to more than one read-

only ASP.NET server control, you should consider using a DataSet instead. If a DataReader

was bound to more than one control (such as three DropDownList controls), the same query

would hit the database three times since the DataReader can only move forward. The DataSet

also works well when a rowset must be persisted between page calls to the Session or Cache

objects.

Of course, because the DataReader is associated with a specific data source, it cannot be

created, filled, or traversed without a connection to the data source. Unlike the DataReader, a

DataSet can be created manually without a connection to the source. In a situation such as an

online shopping cart in which a custom data store is required, a DataSet could be created

manually and its rows added.

Another good use of the DataSet is the situation in which data must be retrieved and a

complex action performed on each row. For example, an application might retrieve a hundred

stock and mutual fund symbols from a 401k table that needs to be edited. This data might

have to include the stock and mutual fund prices on screen, as well. A DataSet could be used

to store the rowset and some code could loop through the DataSet and perform a lookup of

each stock's price through a third-party Web service. Finally, one of the more compelling

reasons to use a DataSet instead of a DataReader is that the DataSet can be serialized when

Page 127: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Contrasting the ADO.NET DataReader and DataSet Seite 6 von 6

the rowset needs to be passed around a network or the Internet. A DataReader cannot be

serialized to XML due to its connected nature.

The DataSet is not the best solution in every situation, and there are several situations in

which DataReaders should really be considered. One is when an application implements

a .NET architecture without data binding—situations in which manual updates to the database

are performed and controls are loaded by looping through rowsets. DataReaders are a good

choice when an application has to be sensitive to changes in the underlying database.

There are other times when a DataReader can be the right choice, such as when populating a

list or retrieving 10,000 records for a business rule. When a huge amount of data must be

retrieved to a business process, even on a middle tier, it can take a while to load a DataSet,

pass the data to it on the business tier from the database, and then store it in memory. The

footprint could be quite large and with numerous instances of it running (such as in a Web

application where hundreds of users may be connected), scalability would become a problem.

If this data is intended to be retrieved and then traversed for business rule processing, the

DataReader could speed up the process as it retrieves one row at a time and does not require

the memory resources that the DataSet requires.

When output or return parameters need to be retrieved, a DataReader will not allow you to get

them until the DataReader and its connection are closed. If data must be bound to a read-only

list in a Web Form, a DataReader is a very good choice. Binding a DataReader to a

DropDownList or even a read-only DataGrid in ASP.NET works well as the data can be

retrieved and displayed in the list but does not need to be persisted for editing. DataSets are

ideal if data needs to be edited, sorted, filtered, or searched.

As I have shown, when data must be passed without a connection to a database or when rich

features for manipulating the data are required, a DataSet fits the bill nicely. The DataReader

works well when a simpler purpose for the data exists such as populating a dropdown list or

retrieving tens of thousands of rows for processing. Your decision should be based on the

particular factors of the situation of the application.

Page 128: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 1 von 9

Configuration Overview: ASP.NET

By Brij

http://www.codeproject.com/KB/server-management/websitecofig.aspx

Introduction

Here in this article, I will be exploring the configuration files of a website. ASP.NET website

configuration is normally a combination of two files:

• machine.config

• web.config

Here, I'll concentrate on web.config and give an overview of machine.config.

Every time we install the .NET framework, there is a machine.config file that is created in

"C:\WINDOWS\Microsoft.NET\Framework\[Version]\CONFIG", which mainly defines:

• Supported configuration file sections,

• the ASP.NET worker process configuration, and

• registers different providers that are used for advanced features such as profiles,

membership, and role based security.

To explore the web.config might take a book, but here, I'll try to explore all the important

sections that play a pivotal role for an ASP.NET website and its deployment.

Every web application inherits settings from the machine.config file, and application level

setting is done in the web.config file. We can also override configurations in the

machine.config file in the web.config file. But, a few settings can not be overridden because

certain settings are process model settings and can't be changed on a per application basis.

The entire contents of a configuration file, whether it is machine.config or web.config, is

nested in a <configuration> element.

ASP.NET Multilayered Configuration system

ASP.NET uses a multilayered configuration system that allows for using different settings for

different parts of an application. For this, we must have an additional subdirectory inside the

virtual directory, and these subdirectories will contain their own config files with additional

settings. ASP.NET uses configuration inheritance so that each subdirectory acquires the

settings from the parent directory.

Page 129: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 2 von 9

Let's take an example. We have a web request http://localhost/X/Y/Z/page.aspx, where X is

the root directory of the application. In this case, multiple levels of settings come into the

picture.

1. The default machine.config settings are applied first.

2. Next, the web.config of the root level is applied. This web.config resides in the same

config directory as the machine.config file.

3. Now, if there is any config file in the application root X, these settings are applied.

4. If there is any config file in the subdirectory Y, these settings are now applied.

5. If there is any config file in the application root Z, those settings are then applied.

But here, there is a limitation: we can have unlimited number of subdirectories having

different settings, but the configuration at step 1 and 2 are more significant because some of

the settings can not be overridden, like the Windows account that is to be used to execute the

code, and other settings can be only overridden at the application root level, like the type of

authentication to be used etc.

Different config files are useful when we apply different security settings to different folders.

The files that need to be secured would then be placed in a separate folder with a separate

web.config file that defines the more stringent security settings to these files and vice versa.

In the web.config, under the <configuration> element, there is another element

<system.web>, which is used for ASP.NET settings and contains separate elements for each

aspect of the configuration.

Page 130: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 3 von 9

Important Configuration Tags

There are a lot of configuration tags that are provided by the web.config file, like

authentication, authorization, browserCaps, clientTarget etc., but all of these don't

have that much importance (and also can't be covered in a single article ), so here, I have only

concentrated on the main tags of the config file.

<authentication>

This element is used to verify the client's identity when the client requests a page from the

server. This is set at the application level. We have four types of authentication modes:

“None”, “Windows”, “Forms”, and “Passport”.

If we don't need any authentication, this is the setting we use:

<authentication mode="None"/>

Normally, Windows authentication is used, for which, we need to check the checkbox:

Integrated Windows Authentication.

<authentication mode="Windows"/>

This authentication is handled by IIS. When the user sends a request to the server, IIS

authenticates it and sends the authentication identity to the code.

IIS gives us four choices

for the authentication

modes: Anonymous, Basic,

Digest, and Windows

Integrated. If the user

selects Anonymous, then

IIS doesn't perform any

authentication. For Basic

authentication, the user has

to provide a username and

password. This

authentication is very

unsecure, because the user

credentials are sent in clear

text format over the

network. Digest

authentication is same as

Basic, except it hashes the

user's password and

transmits the hashed

version over the wire. So, it

is more secure than Basic.

For Windows Integrated

Page 131: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 4 von 9

authentication, passwords never cross the network. The user must still have a username and

password, but the application uses either the Kerberos or a challenge/response protocol to

authenticate the user.

Forms authentication uses web application forms to collect user credentials, and on the basis

of the credential, it takes action on a web application.

<authentication mode="Forms">

<forms name="Form" loginUrl="index.asp" />

</authentication>

Passport authentication is provided by Microsoft. A redirect URL should be specified, and

is used when the requested page is not authenticated, and then it redirects to this URL.

<authentication mode="Passport">

<passport redirectUrl="internal" />

</authentication>

Here, users are authenticated using the information in Microsoft's Passport database. The

advantage is, we can use existing user credentials (such as an email address and password)

without forcing users to go through a separate registration process. The disadvantage is we

need to go through the licensing agreement with Microsoft and pay a yearly fee based on the

use.

For using Passport authentication, you first install the Passport Software Development Kit

(SDK) on your server. The SDK can be downloaded from here. It includes full details of

implementing passport authentication in your own applications.

<authorization>

The <authorization> tag controls client access to web page resources. This element can be

declared at any level (machine, site, application, subdirectory, or page).

authorization>

<allow users="comma-separated list of users"

roles="comma-separated list of roles"

verbs="comma-separated list of verbs"/>

<deny users="comma-separated list of users"

roles="comma-separated list of roles"

verbs="comma-separated list of verbs"/>

</authorization>

<allow>: Using this tag, we can control access to resources on the basis of the following

verbs. In these attributes, we use symbols: ? and *.? means for anonymous users/resources,

and * means for all users.

• users: This contains the list of user names (comma separated) that are allowed to

access the resources.

• roles: This contains the list of roles (comma separated) that are allowed to access the

resources.

• verbs: This contains the list of HTTP verbs to which the action applies (comma

separated). It is used to create a rule that applies to a specific type of HTTP request

(GET, POST, HEAD, OR DEBUG).

Page 132: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 5 von 9

<deny>: Using this tag, we can control access to resources on the basis of the following verbs:

• users: This contains the list of users names (comma separated) that are denied access

to the resources.

• roles: This contains the list of roles (comma separated) that are denied access to the

resources.

• verbs: This contains the list of HTTP verbs to which the action applies (comma

separated). It is used to create a rule that applies to a specific type of HTTP request

(GET, POST, HEAD, OR DEBUG).

<compilation>

In this section, we can configure the settings of the compiler. Here, we can have lots of

attributes, but the most common ones are debug and defaultLanguage. Setting debug to

true means we want the debugging information in the browser, but it has a performance

tradeoff, so normally, it is set as false. And, defaultLanguage tells ASP.NET which

language compiler to use: VB or C#.

<customErrors>

This tags includes the error settings for the application, and is used to give custom error pages

(user-friendly error pages) to end users. In the case that an error occurs, the website is

redirected to the default URL. For enabling and disabling custom errors, we need to specify

the mode attribute.

<customErrors defaultRedirect="url" mode="Off">

<error statusCode="403" redirect="/accesdenied.html" />

<error statusCode="404" redirect="/pagenotfound.html" />

</customErrors>

• "On" means this settings is on, and if there is any error, the website is redirected to

the default URL.

• "Off" means the custom errors are disabled.

• "RemoteOnly" shows that custom errors will be shown to remote clients only.

<error statusCode="403" redirect="/accesdenied.html" />

<error statusCode="404" redirect="/pagenotfound.html" />

This means if there is an error of 403, then the website will redirected to the custom page

accessdenied.html. Similarly for 404 as defined above.

Note: If an error occurs in the custom error page itself, ASP.NET won't able to handle it. It

won't try to reforward the user to the same page. Instead, it'll show the normal default client

error page with a generic message.

<globalization>

This section is used when we want to use encoding or specify a culture for the application.

This is a very vast topic, and can take an article itself for explaining it. Here, we define the

character set for the server to send the response to the client, which is by default is UTF-8,

Page 133: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 6 von 9

and the settings of which the server should use to interpret and display culturally specific

strings, such as numbers and dates.

globalization requestEncoding="utf-8" responseEncoding="utf-8" />

<httpRuntime>

This section can be used to configure the general runtime settings of the application. The main

two are:

<httpRuntime appRequestQueueLimit="50" executionTimeout="300" />

As the name suggests, the attribute appRequestQueueLimit defines the number of requests

that can be queued up on the server for processing. If there are 51 or more requests, then

server would return the 503 error ("Server too busy").

The attribute executionTimeout defines the number of minutes ASP.NET will process a

request before it gets timeout.

<trace>

As the name suggestz, it is used for tracing the execution of an application. We have here two

levels of tracing: page level and application level. Application level enables the trace log of

the execution of every page available in the application. If pageOutput="true", trace

information will be displayed at the bottom of each page. Else, we can view the trace log in

the application root folder, under the name trace.axd.

<trace enabled="false" requestLimit="10" pageOutput="false"

traceMode="SortByTime" locaOnly="true" />

Set the attribute localOnly to false for not viewing the trace information from the client.

For enabling trace at page level, set Trace="True" in the Page tag (on the top of the page).

<identity>

Using this tag, we can control the identity of the application. By default, Impersonation is

disabled. Using Impersonation, an ASP.NET application can execute optionally with the

identity of a client on whose behalf they are operating.

<identity impersonate="false" userName="domain\username"

password="password" />

<sessionState>

In this section, we tell ASP.NET where to store the session. By default, it's inproc which

means storing the session values on the server. But we have four options:

• "Off" means session is not enabled for the application.

• "inproc" means storing the session values on the server.

Page 134: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 7 von 9

• "StateServer" means session states are stored in a remote server.

• "SQLServer" means session states are stored in a SQL Server database. For this, we

need to install the InstallSQLState.sql script in the SQL Server database. It is mainly

used when the we use web farms (an application deployed on multiple servers), but it

makes the performance slow as compared to "inproc".

Here are the other settings:

• "cookieless": when it is true, it means the session used is without cookies.

• “timeout” specifies after how much time the session would expire if the application is

not accessed during that period.

• "stateConnectionString" needs to be specified when the session mode is

StateServer.

• "sqlConnectionString" is the connection string of the SQL Server database if the

session mode is sqlserver.

• "stateNetworkTimeout" attribute, when using the StateServer mode to store

session state, specifies the number of seconds the TCP/IP network connection between

the web server and the state server can be idle before the session is abandoned. The

default is 10.

<sessionState mode="Off"

cookieless="true"

timeout="100"

stateConnectionString="tcpip=server:port"

sqlConnectionString="sql connection string"

stateNetworkTimeout="number of seconds"/>

<appSettings>

This section is used to store custom application configuration like database connection strings,

file paths etc. This also can be used for custom application-wide constants to store

information over multiple pages. It is based on the requirements of the application.

<appSettings>

<add key="Emailto" value="[email protected]" />

<add key="cssFile" value="CSS/text.css" />

</appSettings>

It can be accessed from code like:

ConfigurationSettings.AppSettings("Emailto");

All the values returned from it are strings.

Custom Configuration Sections

We might need some custom configuration sections based on the requirements. One of the

simplest ways we can do this is to create our own named sections, and we can use existing

NameValueSectionHandler components to parse them, and they can be used as key/value

pairs to be accessed at run-time.

Page 135: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 8 von 9

This can be read very easily accessed from the code-behind as:

private string ReadCustomSection()

{

string strKey = "mySectionKey1";

NameValueCollection nvcmySection = (NameValueCollection)

ConfigurationSettings.GetConfig("mySection");

string strValueofKey = nvcmySection[strKey];

return strValueofKey;

}

There are more ways for using custom configuration sections. Check this article:

CustomConfigurationSection.

Encrypting Configuration Sections

Some times, we put some sensitive data in the web.config file like connection strings, user

specific details etc. It is recommended to encrypt these sections. ASP.NET supports two

encryption techniques.

• RSA

• DPAPI

The way the operations perform is very simple. When retrieving information from a config

file, ASP.NET automatically decrypts it and gives the plain text to the code. Similarly, if we

do any updates on the config file from code, it is done the same way. We cannot update a

config file directly. But, we can use WAT for updating it.

Programmatic encryption techniques: If we want to do encryption programmatically, then

we need to retrieve the corresponding ConfigurationSection.SectionInformation object

and call the ProtectSection() method. If we want to decrypt a section, then we can call the

method UnprotectSetion(). Sample code is shown here:

Configuration myConfig =

WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);

Page 136: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Configuration Overview: ASP.NET Seite 9 von 9

ConfigurationSection mySettings = myConfig.GetSection("mySection");

if (mySettings.SectionInformation.IsProtected)

{

mySettings.SectionInformation.UnprotectSection();

}

else

{

mySettings.SectionInformation.ProtectSection("DataProtectionConfigurationPr

ovider"); ;

}

myConfig.Save();

Command line utilities: We can also use a command line utility like aspnet_regiis.exe for

encryption of a config file, which is a CAB file found in

C:\[WinDir]\Microsoft.NET\Framework\[Version]. For using this tool, we must create a

virtual directory for the application. You can refer my article, Deploying Website at IIS, for

more information.

When using aspnet_regiis to protect some sections of a config file, we need to specify some

command line arguments such as:

• The -pe switch specifies the configuration section to encrypt.

• The -app switch specifies our web application virtual path.

• The -prov switch specifies the provider name.

Here is the command line for an application located at http://localhost/MyApp:

A Few Important Points

• Some settings can not be encrypted because they are used outside ASP.NET (mainly

by the IIS web server), like <httpruntime>.

• Config files are case sensitive.

• The web.config file is protected by IIS, so it won't be accessible by a client system. So,

if a user tries to access it, anaccess denied message will be shown.

• If we change the config file at the server, we don't need to restart the web server

because IIS monitors the changes in the web.config, and for performance measures, it

cache it.

• Microsoft also provides a tool known as Website Administration Tool (WAT) that lets

us configure various part of the web.config using a web interface. To run it, select

Website->ASP.NET Configuration, in Visual Studio.

Page 137: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 1 von 14

Erstellen von Webanwendungen ohne Webformulare Chris Tavares MSDN Magazin, März 2008 http://msdn.microsoft.com/de-de/magazine/cc337884.aspx Dieser Artikel basiert auf der Vorabversion von ASP.NET MVC Framework. Änderungen der Details in diesem Artikel sind vorbehalten. Dieses neue Framework basiert auf dem Model View Controller (MVC)-Muster, daher der Name ASP.NET MVC. Das MVC-Muster wurde ursprünglich in den 70er Jahren als Teil von Smalltalk erfunden. Wie in diesem Artikel aufgezeigt wird, passt es hervorragend zur Funktionsweise des Internets. MVC teilt die Benutzeroberfläche in drei unterschiedliche Objekte ein: den Controller, der Eingaben empfängt und behandelt, das Modell, das die Domänenlogik enthält, und die Ansicht, die die Ausgabe generiert. Im Kontext des Internets ist die Eingabe eine HTTP-Anforderung, und der Anforderungsfluss sieht wie Abbildung 1 aus.

Abbildung 1 MVC-

Musteranforderungsfluss (Klicken Sie zum Vergrößern auf das Bild) Dies unterscheidet sich erheblich vom Prozess in Webformularen. Im Webformularmodell geht die Eingabe zur Seite (Ansicht), und die Ansicht ist sowohl für die Verarbeitung der Eingabe als auch das Generieren der Ausgabe verantwortlich. Bei MVC sind die Verantwortlichkeiten getrennt. Wahrscheinlich gehen Ihnen jetzt zwei Dinge durch den Kopf. Entweder: „Das ist großartig. Wie genau geht das?“ oder: „Warum sollte ich drei Objekte schreiben, wenn ich zuvor nur eins schreiben musste?“ Beides sind gute Fragen und können am besten anhand eines Beispiels erklärt werden. Um die Vorzüge diese Methode zu veranschaulichen, zeige ich Ihnen hier das Schreiben einer kleinen

Webanwendung mithilfe von MVC Framework auf. Erstellen eines Controllers Um folgen zu können, müssen Sie Visual Studio 2008 installieren und eine Kopie von MVC Framework abrufen. Zum Zeitpunkt dieses Artikels ist es als Teil des Community Technology Preview (CTP) der ASP.NET-Erweiterungen vom Dezember 2007 verfügbar (asp.net/downloads/3.5-extensions). Laden Sie die CTP-Erweiterungen und das MVC-Toolkit herunter, das einige nützliche Hilfsobjekte enthält. Nachdem Sie CTP heruntergeladen und installiert haben, wird im Dialogfeld für neue Projekte ein neues ASP.NET MVC Webanwendungsprojekt angezeigt. Durch Auswählen dieses MVC-Webanwendungsprojekts erhalten Sie eine Lösung, die etwas anders aussieht als eine normale Website oder Anwendung. Die Lösungsvorlage erstellt eine Webanwendung mit einigen neuen Verzeichnissen (siehe Abbildung 2). Vor allem enthält

Page 138: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 2 von 14

das Controllerverzeichnis die Controllerklassen, und das Ansichtenverzeichnis (und alle seine Unterverzeichnisse) enthält die Ansichten.

Abbildung 2 Die MVC-Projektstruktur Sie schreiben jetzt einen sehr einfachen Controller, der einen Namen zurückgibt, der auf der URL übergeben wird. Klicken Sie mit der rechten Maustaste auf den Ordner „Controller“. Durch Auswahl von „Element hinzufügen“ wird das bekannte Dialogfeld „Neues Element hinzufügen“ mit einigen neuen Elementen angezeigt, einschließlich einer MVC-Controllerklasse und verschiedenen MVC-Ansichtskomponenten. In diesem Fall fügen Sie eine Klasse namens „HelloController“ hinzu: using System;

using System.Web;

using System.Web.Mvc;

namespace HelloFromMVC.Controllers

{

public class HelloController : Controller

{

[ControllerAction]

public void Index()

{

...

}

}

}

Eine Controllerklasse ist viel leichter als eine Seite. Eigentlich sind nur das Ableiten von System.Web.Mvc.Controller und das Weitergeben des Attributs [ControllerAction] an die Aktionsmethoden wirklich erforderlich. Eine Aktion ist eine Methode, die als Reaktion auf eine Anforderung an eine bestimmte URL aufgerufen wird. Aktionen sind für die erforderliche Verarbeitung und das anschließende Rendern einer Ansicht verantwortlich. Zunächst schreiben Sie eine einfache Aktion, die den Namen an die Ansicht übergibt, wie Sie hier sehen: [ControllerAction]

public void HiThere(string id)

{

ViewData["Name"] = id;

RenderView("HiThere");

}

Die Aktionsmethode erhält den Namen von der URL über den id-Parameter (später genaueres über das Wie), speichert ihn in der ViewData-Sammlung und rendert dann eine Ansicht namens HiThere. Bevor das Aufrufen dieser Methode oder das Aussehen der Ansicht erläutert werden, soll näher auf Testfähigkeit eingegangen werden. Erinnern Sie sich an die vorherigen Anmerkungen darüber, wie schwer Webformularseitenklassen zu testen sind? Nun, Controller lassen sich viel leichter testen. In der Tat ist es ohne zusätzliche Infrastruktur möglich, einen Controller direkt zu instanziieren und Aktionsmethoden aufzurufen. Es sind kein HTTP-Kontext und kein Server erforderlich, lediglich eine Testumgebung. Als Beispiel habe ich für diese Klasse in Abbildung 3 einen Visual Studio Team System (VSTS)-Komponententest eingefügt. Figure 3 Controllerkomponententest namespace HelloFromMVC.Tests

{

[TestClass]

Page 139: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 3 von 14

public class HelloControllerFixture

{

[TestMethod]

public void HiThereShouldRenderCorrectView()

{

TestableHelloController controller = new

TestableHelloController();

controller.HiThere("Chris");

Assert.AreEqual("Chris", controller.Name);

Assert.AreEqual("HiThere", controller.ViewName);

}

}

class TestableHelloController : HelloController

{

public string Name;

public string ViewName;

protected override void RenderView(

string viewName, string master, object data)

{

this.ViewName = viewName;

this.Name = (string)ViewData["Name"];

}

}

}

Hier gehen verschiedene Dinge vor. Der eigentliche Test ist sehr einfach: Instanziieren Sie den Controller, rufen Sie die Methode mit den erwarteten Daten auf, und überprüfen Sie dann, ob die richtige Ansicht gerendert wurde. Führen Sie die Überprüfung durch Erstellen einer testspezifischen Unterklasse aus, die die RenderView-Methode überschreibt. Dadurch kann die eigentliche Erstellung von HTML umgangen werden. Achten Sie nur darauf, dass die richtigen Daten an die Ansicht gesendet wurden und dass die richtige Ansicht gerendert wurde. Für diesen Test werden die zugrunde liegenden Details der Ansicht außer Acht gelassen. Erstellen einer Ansicht

Selbstverständlich ist es letzten Endes erforderlich, etwas HTML zu erstellen, also erstellen Sie die HiThere-Ansicht. Dafür erstellen Sie zunächst im Ordner „Ansichten“ der Lösung einen neuen Ordner namens „Hello“. Standardmäßig wird eine Ansicht vom Controller im Ordner „Ansichten\<Controllerpräfix>“ gesucht (das Controllerpräfix ist der Name der Controllerklasse ohne das Wort „Controller“). Nach Ansichten, die von HelloController gerendert wurden, wird also in „Ansichten\Hello“ gesucht. Die Lösung sieht aus wie in Abbildung 4. Abbildung 4 Hinzufügen einer Ansicht zum

Projekt (Klicken Sie zum Vergrößern auf das Bild) Das HTML für die Ansicht sieht folgendermaßen aus: <html >

<head runat="server">

<title>Hi There!</title>

Page 140: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 4 von 14

</head>

<body>

<div>

<h1>Hello, <%= ViewData["Name"] %></h1>

</div>

</body>

</html>

Mehrere Dinge sollten Ihnen sofort auffallen. Es gibt keine runat ="Server"-Tags. Es gibt kein Formulartag. Es gibt keine Steuerelementdeklarationen. Eigentlich sieht das Ganze eher nach klassischem ASP als nach ASP.NET aus. Beachten Sie, dass MVC-Ansichten nur für das Generieren von Ausgaben verantwortlich sind. Es sind also keine Ereignisverarbeitung oder komplexen Steuerelemente erforderlich, wie es bei Webformularen der Fall ist. Das MVC Framework verwendet das .aspx-Dateiformat als hilfreiche Textvorlagensprache. Sie können sogar Codebehind verwenden, doch standardmäßig sieht die Codebehind-Datei folgendermaßen aus: using System;

using System.Web;

using System.Web.Mvc;

namespace HelloFromMVC.Views.Hello

{

public partial class HiThere : ViewPage

{

}

}

Es gibt keine Seiten-Init- oder Lademethoden, keine Ereignishandler, nichts außer der Deklaration der Basisklasse, die nicht Page sondern ViewPage ist. Für eine MVC-Ansicht reicht dies völlig aus. Führen Sie die Anwendung aus, navigieren Sie zu http://localhost:<Port>/Hello/HiThere/Chris, und Sie erhalten eine Anzeige wie in Abbildung 5.

Abbildung 5 Erfolgreiche MVC-Ansicht (Klicken Sie zum Vergrößern auf das Bild) Wenn Sie statt Abbildung 5 eine Ausnahme sehen, geraten Sie nicht in Panik. Wenn die Datei „HiThere.aspx“ in Visual Studio als aktives Dokument festgelegt ist, versucht Visual Studio nach Drücken auf F5 direkt auf die .aspx-Datei zuzugreifen. Da MVC-Ansichten erfordern, dass der Controller vor der Anzeige ausgeführt wird, können Sie nicht direkt zur Seite navigieren. Bearbeiten Sie einfach die URL, damit sie aussieht wie in Abbildung 5, und es müsste funktionieren. Woher wusste das MVC Framework, dass die Aktionsmethode aufgerufen werden soll? Es gab nicht einmal eine Dateierweiterung für diese URL. Die Antwort ist URL-Routing. Wenn Sie sich die Datei „global.asax.cs“ anschauen, sehen Sie den Code, der in Abbildung 6 dargestellt ist. Die globale RouteTable speichert eine Sammlung von Route-Objekten. Jede Route beschreibt ein URL-Formular und wozu es dient. Standardmäßig werden der Tabelle zwei Routen hinzugefügt. Die erste ist der eigentliche Trick. Sie bestimmt, dass für jede URL, die aus drei Teilen nach dem Servernamen besteht, der erste ein Controllername sein sollte, der zweite ein Aktionsname und der dritte der ID-Parameter: Figure 6 Route-Tabelle public class Global : System.Web.HttpApplication

{

Page 141: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 5 von 14

protected void Application_Start(object sender, EventArgs e)

{

// Change Url= to Url="[controller].mvc/[action]/[id]"

// to enable automatic support on IIS6

RouteTable.Routes.Add(new Route

{

Url = "[controller]/[action]/[id]",

Defaults = new { action = "Index", id = (string)null },

RouteHandler = typeof(MvcRouteHandler)

});

RouteTable.Routes.Add(new Route

{

Url = "Default.aspx",

Defaults = new {

controller = "Home",

action = "Index",

id = (string)null },

RouteHandler = typeof(MvcRouteHandler)

});

}

}

Url = "[controller]/[action]/[id]"

Durch diese Standardroute wurde die HiThere-Methode aufgerufen. Erinnern Sie sich an diese URL: http://localhost/Hello/HiThere/Chris? Diese Route hat „Hello“ dem Controller zugeordnet, „HiThere“ der Aktion und „Chris“ der ID. Das MVC Framework hat dann eine HelloController-Instanz erstellt, die HiThere-Methode aufgerufen und Chris als Wert des ID-Parameters übergeben. Diese Standardroute bietet viel, doch Sie können auch Ihre eigenen Routen hinzufügen. Sie möchten zum Beispiel eine wirklich freundliche Website erstellen, auf der die Benutzer einfach ihren Namen eingeben, um persönlich begrüßt zu werden. Wenn Sie diese Route am Anfang der Routingtabelle einfügen, RouteTable.Routes.Add(new Route

{

Url = "[id]",

Defaults = new {

controller = "Hello",

action = "HiThere" },

RouteHandler = typeof(MvcRouteHandler)

});

dann können Sie einfach zu „http://localhost/Chris“ navigieren, und die Aktion ist nach wie vor aufgerufen. Eine freundliche Begrüßung wird angezeigt. Woher wusste das System, welcher Controller und welche Aktion aufgerufen werden sollten? Die Antwort liegt im Standardparameter. Dieser verwendet die neue anonyme Typsyntax C# 3.0, um ein Pseudowörterbuch zu erstellen. Das Standardobjekt auf der Route kann willkürliche zusätzliche Informationen enthalten doch für MVC kann es auch einige bekannte Einträge umfassen: Controller und Aktion. Wenn in der URL kein Controller oder keine Aktion angegeben werden, dann wird der Name in den Standardeinstellungen verwendet. Daher müssen sie nicht in der URL vorhanden sein, damit die Anforderung dem richtigen Controller und der richtigen Aktion zugeordnet wird. Noch eine Anmerkung: Erinnern Sie sich daran, dass ich gesagt habe, „am Anfang der Tabelle einfügen“? Wenn dies am Ende der Tabelle eingefügt wird, erhalten Sie einen Fehler. Routing funktioniert nach dem Prinzip, wer zuerst kommt, mahlt zuerst. Bei der Verarbeitung von URLs arbeitet das Routingsystem die Tabelle von oben nach unten durch, und die erste passende Route gewinnt. In diesem Fall passt die Standardroute „[controller]/[action]/[id]“,

Page 142: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 6 von 14

weil es Standardwerte für die Aktion und die ID gibt. Folglich wird nach ChrisController gesucht, und da kein Controller vorhanden ist, wird ein Fehler angezeigt. Ein umfangreicheres Beispiel Nach dieser Ausführung zu den Grundlagen von MVC Framework soll im Folgenden ein umfangreicheres Beispiel erörtert werden, das nicht nur lediglich eine Zeichenfolge anzeigt. Ein Wiki ist eine Website, die im Browser bearbeitet werden kann. Seiten können problemlos hinzugefügt oder bearbeitet werden. Ich habe mithilfe des MVC Framework ein kleines Beispiel-Wiki geschrieben. Der Bildschirm „Diese Seite bearbeiten“ wird in Abbildung 7 gezeigt.

Abbildung 7 Bearbeiten der Homepage (Klicken Sie zum Vergrößern auf das Bild) Sehen Sie sich den Codedownload für diesen Artikel an, um die Implementierung der zugrunde liegenden Wiki-Logik nachzuvollziehen. Hier soll erläutert werden, wie MVC Framework das Platzieren des Wiki im Internet unterstützt hat. Zuerst habe ich die URL-Struktur entworfen. Ich hatte folgendes Ziel:

• /[pagename] zeigt die Seite mit diesem Namen an. • /[pagename]?Version=n zeigt die angeforderte Version der Seite an, wobei 0 = die

aktuelle Version, 1 = die vorherige ist und so weiter. • /Edit/[pagename] öffnet den Bearbeitungsbildschirm für diese Seite. • /CreateNewVersion/[pagename] ist die URL, an die gesendet wird, um eine

Bearbeitung einzureichen.

Sehen Sie sich zunächst die grundlegende Anzeige einer Wiki-Seite an. Dafür wurde eine neue Klasse namens „WikiPageController“ erstellt. Anschließend wurde eine Aktion namens „ShowPage“ hinzugefügt. Zunächst sah die WikiPageController-Klasse wie in Abbildung 8 aus. Die ShowPage-Methode ist ziemlich einfach. Die WikiSpace- und WikiPage-Klassen repräsentieren jeweils einen Satz Wiki-Seiten und eine spezielle Seite (sowie deren Überarbeitungen). Diese Aktion lädt das Modell hoch und ruft RenderView auf. Aber wozu dient die Zeile „new WikiPageViewData“? Figure 8 WikiPageController-Implementierung von ShowPage public class WikiPageController : Controller

{

ISpaceRepository repository;

public ISpaceRepository Repository

Page 143: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 7 von 14

{

get {

if (repository == null)

{

repository = new FileBasedSpaceRepository(

Request.MapPath("~/WikiPages"));

}

return repository;

}

set { repository = value; }

}

[ControllerAction]

public void ShowPage(string pageName, int? version)

{

WikiSpace space = new WikiSpace(Repository);

WikiPage page = space.GetPage(pageName);

RenderView("showpage",

new WikiPageViewData

{

Name = pageName,

Page = page,

Version = version ?? 0

});

}

}

Im vorherigen Beispiel wurde eine Möglichkeit gezeigt, Daten vom Controller zur Ansicht zu übergeben: das ViewData-Wörterbuch. Wörterbücher sind praktisch, aber auch gefährlich. Sie können beliebige Einträge enthalten, es wird kein IntelliSense® für die Inhalte bereitgestellt, und weil das ViewData-Wörterbuch vom Typ „Wörterbuch<zeichenfolge, Objekt>“ ist, muss alles konvertiert werden, um die Inhalte nutzen zu können. Wenn Sie wissen, welche Daten in der Ansicht benötigt werden, können Sie stattdessen ein stark typisiertes ViewData-Objekt verwenden. In diesem Fall wurde ein einfaches Objekt erstellt, WikiPageViewData, wie in Abbildung 9 zu sehen ist. Dieses Objekt übergibt die Wiki-Seiteninformationen zusammen mit einigen Dienstprogrammmethoden an die Ansicht, um beispielsweise die HTML-Version des Wiki-Markups abzurufen. Figure 9 WikiPageViewData-Objekt public class WikiPageViewData {

public string Name { get; set; }

public WikiPage Page { get; set; }

public int Version { get; set; }

public WikiPageViewData() {

Version = 0;

}

public string NewVersionUrl {

get {

return string.Format("/CreateNewVersion/{0}", Name);

}

}

public string Body {

get { return Page.Versions[Version].Body; }

}

Page 144: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 8 von 14

public string HtmlBody {

get { return Page.Versions[Version].BodyAsHtml(); }

}

public string Creator {

get { return Page.Versions[Version].Creator; }

}

public string Tags {

get { return string.Join(",", Page.Versions[Version].Tags); }

}

}

Die Ansichtsdaten sind nun definiert. Wie lassen sie sich verwenden? In „ShowPage.aspx.cs“ sehen Sie Folgendes: namespace MiniWiki.Views.WikiPage {

public partial class ShowPage : ViewPage<WikiPageViewData>

{

}

}

Beachten Sie, dass die Basisklasse nach dem Typ „ViewPage<WikiPageViewData>“ definiert wurde. Die ViewData-Eigenschaft der Seite ist also vom Typ „WikiPageViewData“ und kein Wörterbuch wie im vorangegangenen Beispiel. Das eigentliche Markup in der .aspx-Datei ist relativ einfach: <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"

AutoEventWireup="true" CodeBehind="ShowPage.aspx.cs"

Inherits="MiniWiki.Views.WikiPage.ShowPage" %>

<asp:Content

ID="Content1"

ContentPlaceHolderID="MainContentPlaceHolder"

runat="server">

<h1><%= ViewData.Name %></h1>

<div id="content" class="wikiContent">

<%= ViewData.HtmlBody %>

</div>

</asp:Content>

Beachten Sie, dass der Indizierungsoperator [] nicht verwendet wird, um auf ViewData zu verweisen. Da nun ein stark typisiertes ViewData-Objekt vorliegt, kann stattdessen einfach direkt auf die Eigenschaft zugegriffen werden. Es sind keine Datentypkonvertierungen erforderlich, und durch Visual Studio wird IntelliSense bereitgestellt. Aufmerksame Beobachter werden das Tag <asp:Content> in dieser Datei bemerkt haben. Masterseiten funktionieren tatsächlich mit MVC-Ansichten. Dazu können Masterseiten auch selbst Ansichten sein. Sehen Sie sich den Masterseiten-Codebehind an: namespace MiniWiki.Views.Layouts

{

public partial class Site :

System.Web.Mvc.ViewMasterPage<WikiPageViewData>

{

}

}

Das dazugehörige Markup ist in Abbildung 10 aufgeführt. Im Moment erhält die Masterseite genau das gleiche ViewData-Objekt wie die Ansicht. Die Basisklasse der Masterseite wurde als „ViewMasterPage<WikiPageViewData>“ deklariert, um die richtige Art ViewData zu erhalten. Von dort aus richten Sie die verschiedenen DIV-Tags ein, um die Seite anzulegen, füllen die Versionsliste aus und beenden mit dem üblichen Inhaltsplatzhalter. Figure 10 Site.Master <%@ Master Language="C#"

AutoEventWireup="true"

Page 145: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 9 von 14

CodeBehind="Site.master.cs"

Inherits="MiniWiki.Views.Layouts.Site" %>

<%@ Import Namespace="MiniWiki.Controllers" %>

<%@ Import Namespace="MiniWiki.DomainModel" %>

<%@ Import Namespace="System.Web.Mvc" %>

<html >

<head runat="server">

<title><%= ViewData.Name %></title>

<link href="http://../../Content/Site.css" rel="stylesheet"

type="text/css" />

</head>

<body>

<div id="inner">

<div id="top">

<div id="header">

<h1><%= ViewData.Name %></h1>

</div>

<div id="menu">

<ul>

<li><a href="http://Home">Home</a></li>

<li>

<%= Html.ActionLink("Edit this page",

new { controller = "WikiPage",

action = "EditPage",

pageName = ViewData.Name })%>

</ul>

</div>

</div>

<div id="main">

<div id="revisions">

Revision history:

<ul>

<%

int i = 0;

foreach (WikiPageVersion version in ViewData.Page.Versions)

{ %>

<li>

<a href="http://<%= ViewData.Name %>?version=<%= i %>">

<%= version.CreatedOn %>

by

<%= version.Creator %>

</a>

</li>

<% ++i;

} %>

</ul>

</div>

<div id="maincontent">

<asp:ContentPlaceHolder

ID="MainContentPlaceHolder"

runat="server">

</asp:ContentPlaceHolder>

</div>

</div>

</div>

</body>

</html>

Auch zu beachten ist der Aufruf an Html.ActionLink. Dies ist ein Beispiel für ein Renderinghilfsprogramm. Die verschiedenen Ansichtsklassen haben zwei Eigenschaften, Html und Url. Jede verfügt über nützliche Methoden, Teile des HTML auszugeben. In diesem Fall nimmt Html.ActionLink ein Objekt (hier eines anonymen Typs) und führt es zurück

Page 146: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 10 von 14

durch das Routingsystem. Dies erzeugt eine URL, die zum angegebenen Controller und zur Aktion weiterleitet. Auf diese Weise führt der Link „Diese Seite bearbeiten“ immer zum richtigen Ort, egal wie die Routen geändert werden. Sie haben wahrscheinlich auch bemerkt, dass ich auf das manuelle Erstellen eines Links zurückgreifen musste (die Links zu vorherigen Seitenversionen). Leider funktioniert das aktuelle Routingsystem nicht so gut zum Erzeugen von URLs, wenn Abfragezeichenfolgen verwendet werden. Dies dürfte in späteren Versionen des Frameworks behoben werden. Erstellen von Formularen und Zurücksenden Sehen Sie sich jetzt die EditPage-Aktion auf dem Controller an: [ControllerAction]

public void EditPage(string pageName)

{

WikiSpace space = new WikiSpace(Repository);

WikiPage page = space.GetPage(pageName);

RenderView("editpage",

new WikiPageViewData {

Name = pageName,

Page = page });

}

Wieder macht die Aktion nicht viel – sie rendert lediglich die Ansicht mit der speziellen Seite. In der Ansicht, die in Abbildung 11 zu sehen ist, wird es interessanter. Diese Datei erstellt ein HTML-Formular, aber es gibt kein Runat ="Server". Das Url.Action-Hilfsprogramm wird zum Erzeugen der URL verwendet, an die das Formular zurücksendet. Es gibt unterschiedliche Anwendungen verschiedener HTML-Hilfsprogramme, beispielsweise TextBox, TextArea und SubmitButton. Sie dienen erwartungsgemäß dazu, ein HTML für verschiedene Eingabefelder zu generieren. Figure 11 EditPage. aspx <%@ Page Language="C#"

MasterPageFile="~/Views/Shared/Site.Master"

AutoEventWireup="true"

CodeBehind="EditPage.aspx.cs"

Inherits="MiniWiki.Views.WikiPage.EditPage" %>

<%@ Import Namespace="System.Web.Mvc" %>

<%@ Import Namespace="MiniWiki.Controllers" %>

<asp:Content ID="Content1"

ContentPlaceHolderID="MainContentPlaceHolder"

runat="server">

<form action="<%= Url.Action(

new { controller = "WikiPage",

action = "NewVersion",

pageName = ViewData.Name })%>" method=post>

<%

if (ViewContext.TempData.ContainsKey("errors"))

{

%>

<div id="errorlist">

<ul>

<%

foreach (string error in

(string[])ViewContext.TempData["errors"])

{

%>

<li><%= error%></li>

<% } %>

</ul>

</div>

<% } %>

Page 147: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 11 von 14

Your name: <%= Html.TextBox("Creator",

ViewContext.TempData.ContainsKey("creator") ?

(string)ViewContext.TempData["creator"] :

ViewData.Creator)%>

<br />

Please enter your updates here:<br />

<%= Html.TextArea("Body", ViewContext.TempData.ContainsKey("body") ?

(string)ViewContext.TempData["body"] :

ViewData.Body, 30, 65)%>

<br />

Tags: <%= Html.TextBox(

"Tags", ViewContext.TempData.ContainsKey("tags") ?

(string)ViewContext.TempData["tags"] :

ViewData.Tags)%>

<br />

<%= Html.SubmitButton("SubmitAction", "OK")%>

<%= Html.SubmitButton("SubmitAction", "Cancel")%>

</form>

</asp:Content>

Lästiger bei der Webprogrammierung sind Fehler in einem Formular. Sie möchten Fehlermeldungen anzeigen, jedoch die zuvor eingegebenen Daten beibehalten. Es ist uns allen schon passiert, dass wir in einem Formular mit 35 Feldern einen Fehler gemacht haben, woraufhin eine Reihe von Fehlermeldungen und ein neues, leeres Formular angezeigt werden. MVC Framework bietet TempData als einen Ort zum Speichern der zuvor eingegebenen Informationen an, um das Formular erneut auszufüllen. Dadurch wurde ViewState in Webformularen sehr einfach, da das Speichern der Inhalte von Steuerelementen mehr oder weniger automatisch erfolgte. Das Gleiche soll in MVC erreicht werden, und dabei ist TempData von Nutzen. TempData ist ein Wörterbuch, ähnlich wie das nicht typisierte ViewData. Die Inhalte von TempData werden jedoch nur für eine einzige Anforderung gespeichert und anschließend gelöscht. In Abbildung 12, in der NewVersion-Aktion sehen Sie, wie dies funktioniert. Figure 12 NewVersion-Aktion [ControllerAction]

public void NewVersion(string pageName) {

NewVersionPostData postData = new NewVersionPostData();

postData.UpdateFrom(Request.Form);

if (postData.SubmitAction == "OK") {

if (postData.Errors.Length == 0) {

WikiSpace space = new WikiSpace(Repository);

WikiPage page = space.GetPage(pageName);

WikiPageVersion newVersion = new WikiPageVersion(

postData.Body, postData.Creator, postData.TagList);

page.Add(newVersion);

} else {

TempData["creator"] = postData.Creator;

TempData["body"] = postData.Body;

TempData["tags"] = postData.Tags;

TempData["errors"] = postData.Errors;

RedirectToAction(new {

controller = "WikiPage",

action = "EditPage",

pageName = pageName });

return;

}

}

RedirectToAction(new {

Page 148: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 12 von 14

controller = "WikiPage",

action = "ShowPage",

pageName = pageName });

}

Zunächst wird ein NewVersionPostData-Objekt erstellt. Dies ist ein weiteres Hilfsobjekt mit Eigenschaften und Methoden, die die Inhalte des Beitrags speichern, sowie einer Überprüfung. Um das postData-Objekt zu laden, wird eine Hilfsfunktion aus dem MVC Toolkit verwendet. UpdateFrom ist eigentlich eine Erweiterungsmethode, die vom Toolkit bereitgestellt wird, und es wird Reflektion verwendet, um die Namen der Formularfelder an die Namen der Eigenschaften auf dem Objekt anzupassen. Das Ergebnis ist, dass alle Feldwerte in das postData-Objekt geladen werden. Die Verwendung von UpdateFrom hat jedoch den Nachteil, dass Formulardaten direkt von HttpRequest abgerufen werden, wodurch Komponententests erschwert werden. NewVersion überprüft zunächst SubmitAction. Dies wird als „OK“ angezeigt, wenn der Benutzer auf die Schaltfläche „OK“ geklickt hat und die bearbeitete Seite senden will. Gibt es hier einen anderen Wert, leitet die Aktion zu ShowPage zurück, wodurch die ursprüngliche Seite erneut angezeigt wird. Wenn der Benutzer auf „OK“ geklickt hat, prüfen Sie die postData.Errors-Eigenschaft. Diese führt einige einfache Prüfungen der Beitragsinhalte aus. Sind keine Fehler vorhanden, wird die neue Version der Seite verarbeitet und in das Wiki geschrieben. Sind jedoch Fehler vorhanden, wird es interessant. In diesem Fall legen Sie die verschiedenen Felder des TempData-Wörterbuchs so fest, das sie die Inhalte von PostData enthalten. Dann leiten Sie zur Bearbeitungsseite zurück. Da TempData jetzt festgelegt ist, zeigt die Seite erneut das Formular an, diesmal initialisiert mit den vom Benutzer zuvor gesendeten Werten. Die Verarbeitung von Beiträgen, die Prüfung und TempData sind relativ arbeitsintensiv und erfordern mehr manuelle Arbeit als wirklich nötig ist. Zukünftige Versionen sollten Hilfsmethoden enthalten, die zumindest einen Teil der Überprüfung von TempData automatisieren. Ein letzter Hinweis zu TempData: Die Inhalte von TempData werden in der serverseitigen Benutzersitzung gespeichert. Wenn Sie die Sitzung ausschalten, funktioniert TempData nicht. Controllererstellung Die Grundlagen des Wiki funktionieren jetzt, doch es gibt einige Unklarheiten in der Implementierung, die noch verdeutlicht werden sollen. Zum Beispiel wird die Repository-Eigenschaft verwendet, um die Logik des Wiki vom physischen Speicher zu entkoppeln. Sie können Repositorys bereitstellen, die Inhalte im Dateisystem speichern (so wie in diesem Beispiel), in einer Datenbank oder wo immer Sie möchten. Dabei sind leider zwei Probleme zu lösen: Zunächst ist die Controllerklasse eng mit der konkreten FileBasedSpaceRepository-Klasse gekoppelt. Es ist ein Standard erforderlich, der verwendet werden kann, wenn die Eigenschaft nicht festgelegt ist. Was noch schlimmer ist, der Pfad zu den Dateien auf dem Datenträger ist hier ebenfalls hartcodiert. Dies sollte zumindest aus der Konfiguration hervorgehen. Zweitens ist das Repository tatsächlich eine erforderliche Abhängigkeit, ohne die das Objekt nicht ausgeführt wird. Ein guter Entwurf verdeutlicht, dass das Repository ein Konstruktorparameter sein sollte und keine Eigenschaft. Doch es kann dem Konstruktor nicht hinzugefügt werden, weil das MVC Framework einen argumentlosen Konstruktor auf Controllern erfordert. Glücklicherweise gibt es eine Erweiterungsmöglichkeit, die Ihnen aus der Klemme hilft: die Controller Factory. Eine Controller Factory dient zum Erstellen von Controllerinstanzen. Sie müssen lediglich eine Klasse erstellen, die die IControllerFactory-Schnittstelle implementiert

Page 149: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 13 von 14

und beim MVC System registrieren. Sie können Controller Factorys für alle Controller oder nur für spezielle Typen registrieren. Abbildung 13 zeigt eine Controller Factory für WikiPageController, die das Repository jetzt als Konstruktorparameter übergibt. Figure 13 Controller Factory public class WikiPageControllerFactory : IControllerFactory {

public IController CreateController(RequestContext context,

Type controllerType)

{

return new WikiPageController(

GetConfiguredRepository(context.HttpContext.Request));

}

private ISpaceRepository GetConfiguredRepository(IHttpRequest request)

{

return new FileBasedSpaceRepository(request.MapPath("~/WikiPages"));

}

}

In diesem Fall ist die Implementierung ziemlich einfach, dadurch können jedoch die Erstellungscontroller aktiviert werden, die erheblich leistungsfähigere Tools verwenden (in bestimmten Dependency Injection-Containern). Jetzt haben Sie alle Informationen, um die Abhängigkeiten für den Controller in ein Objekt zu trennen, das leichter verwaltet und gewartet werden kann. Als letzter Schritt muss die Factory beim Framework registriert werden. Sie erledigen dies über die Klasse ControllerBuilder, indem Sie in der Application_Start-Methode der Global.asax.cs-Klasse folgende Zeile hinzufügen (entweder vor oder nach den Routen): ControllerBuilder.Current.SetControllerFactory(

typeof(WikiPageController), typeof(WiliPageControllerFactory));

Dadurch wird eine Factory für WikiPageController registriert. Wenn es in diesem Projekt andere Controller gibt, wird diese Factory nicht verwendet, da sie nur für den WikiPageController-Typ registriert ist. Sie können auch SetDefaultControllerFactory aufrufen, wenn Sie eine Factory festlegen wollen, die für jeden Controller verwendet werden soll. Andere Erweiterungspunkte Die Controller Factory ist nur der Anfang der Frameworkerweiterbarkeit. In diesem Artikel kann nicht ins Detail gegangen werden, daher wird nur auf die Höhepunkte verwiesen. Wenn Ihre Ausgabe nicht HTML sein soll oder wenn Sie ein anderes Vorlagenmodul als Webformulare verwenden möchten, können Sie das ViewFactory-Objekt des Controllers anders einrichten. Sie können die IViewFactory-Schnittstelle implementieren und erhalten dann vollständige Kontrolle über die Generierung der Ausgabe. Dies ist zum Generieren von RSS, XML oder sogar Grafik nützlich. Wie Sie bereits gesehen haben, ist das Routingsystem ziemlich flexibel. Doch im Routingsystem ist nichts für MVC spezifisch. Jede Route verfügt über eine RouteHandler-Eigenschaft. Bisher habe ich diese immer auf MvcRouteHandler festgelegt. Aber es ist möglich, die IRouteHandler-Schnittstelle zu implementieren und das Routingsystem mit anderen Webtechnologien zu koppeln. Eine zukünftige Version des Frameworks soll einen WebFormsRouteHandler enthalten. Andere Technologien werden in Zukunft die Vorteile des generischen Routingsystems nutzen. Controller müssen nicht von System.Web.Mvc.Controller ableiten. Ein Controller muss lediglich die Schnittstelle IController implementieren, die nur über eine einzige Methode namens „Execute“ verfügt. Von da aus können Sie beliebig vorgehen. Wenn Sie andererseits nur einige Verhaltensweisen der grundlegenden Controller-Klasse anpassen möchten, bietet der Controller viele virtuelle Funktionen, die überschrieben werden können:

Page 150: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Erstellen von Webanwendungen ohne Webformulare Seite 14 von 14

• Mit OnPreAction, OnPostAction und OnError können Sie generische Vor- und Nachbearbeitung mit jeder Aktion koppeln, die ausgeführt wird. OnError bietet einen Controller-übergreifenden Fehlerbehandlungsmechanismus.

• HandleUnknownAction wird aufgerufen, wenn eine URL an den Controller weitergeleitet wird, dieser Controller jedoch die in der Route angeforderte Aktion nicht ausführt. Standardmäßig führt diese Methode zu einer Ausnahme, aber Sie können sie außer Kraft setzen und somit nach Belieben anpassen.

• InvokeAction ist die Methode, die herausfindet, welche Aktionsmethode aufgerufen werden soll, und sie aufruft. Wenn Sie den Prozess anpassen möchten (zum Beispiel um die Anforderung für die Attribute [ControllerAction] zu beseitigen), können Sie dies hier tun.

Es gibt weitere virtuelle Methoden auf dem Controller, doch dies sind vorrangig Testmöglichkeiten und keine Erweiterungspunkte. Zum Beispiel ist RedirectToAction virtuell, damit Sie eine abgeleitete Klasse erstellen können, die nicht wirklich umleitet. So können Sie Aktionen testen, die umleiten, ohne dass ein kompletter Webserver ausgeführt werden muss. Abschied von Webformularen? Jetzt fragen Sie sich vielleicht: „Was passiert mit den Webformularen? Werden sie durch MVC ersetzt?“ Die Antwort lautet „Nein“! Webformulare sind eine geläufige Technologie, und Microsoft wird sie weiter unterstützen und verbessern. Es gibt viele Anwendungen, bei denen Webformulare sehr gut funktionieren. Die Berichterstattungsanwendung für Intranetdatenbanken kann zum Beispiel mithilfe von Webformularen in einem Bruchteil der Zeit erstellt werden, die für das Schreiben in MVC erforderlich wäre. Zudem unterstützen Webformulare zahlreiche Steuerelemente, von denen viele hoch entwickelt sind und eine Menge Arbeit ersparen. Wann sollten Sie also MVC den Webformularen vorziehen? Häufig ist das abhängig von Ihren Anforderungen und Vorlieben. Haben Sie Schwierigkeiten, Ihre URLs nach Ihren Vorlieben anzupassen? Möchten Sie Komponententests auf Ihrer Benutzeroberfläche durchführen? Beide dieser Szenarios legen die Verwendung von MVC nahe. Arbeiten Sie vielleicht viel mit der Darstellung von Daten, mit bearbeitungsfähigen Rastern und aufwändigen Strukturansichtsteuerelementen? Dann sind wahrscheinlich im Moment Webformulare die bessere Wahl. Mit der Zeit wird MVC Framework wahrscheinlich im Bereich der Benutzeroberflächensteuerung aufholen, doch vermutlich wird der Einstieg nie so einfach sein wie mit Webformularen, bei denen zahlreiche Funktionen per Drag & Drop zugänglich sind. Doch bis dahin bietet ASP.NET MVC Framework Webentwicklern eine neue Möglichkeit, Webanwendungen in Microsoft .NET Framework zu erstellen. Das Framework wurde für Testfähigkeit entworfen, nutzt HTTP anstatt es zu abstrahieren und kann an fast jedem Punkt erweitert werden. Es ist eine notwendige Ergänzung zu Webformularen für die Entwickler, die vollständige Kontrolle über ihre Webanwendungen erhalten möchten.

Page 151: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Exploring Windows Communication Foundation Seite 1 von 6

Exploring Windows Communication

Foundation

By mastergaurav

http://www.codeproject.com/KB/WCF/edujini_wcf_scart_01.aspx

Case Study

We shall take a simple shopping cart as a case-study. We start with simple data-types and

slowly build on to work with custom, more complex data-types. In the sequence of articles,

we shall explore various key aspects of WCF.

Service Definition

Our shopping-cart service, to begin with, publishes the following operations:

bool CheckUserExists(string username);

DateTime? GetLastTransactionTime(string username);

The job of the first operation is to check whether or not the user is registered in our database

or not. The second operation returns the date and time of the last transaction done by the last

user, if any. Yes, you note the operation correctly, the return type is not System.DateTime

but System.DateTime?, the C# syntax for System.Nullable<System.DateTime>.

To publish any service in WCF, we require four steps in general:

1. Service contract definition

2. Contract implementation

3. Service configuration

4. Service hosting

Service Contract Definition

In WCF, service contract is the term used for the final service being published. Note that a

service may have one or more related operations, referred to as operation contracts in WCF.

If you are working in Visual Studio, create a new project of type "Class Library". Yes, you

read it right, it is "Class Library" and not "WCF Service Library".

We start by creating an interface IShoppingCartService as follows:

using DateTime;

namespace ShoppingCartLibrary

{

public interface IShoppingCartService

{

bool CheckUserExists(string username);

Page 152: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Exploring Windows Communication Foundation Seite 2 von 6

DateTime? GetLastTransactionTime(string username);

}

}

Contract Implementation

The next obvious step is to provide implementation to the service. We create a class

ShoppingCartImpl that implements the interface IShoppingCartService.

using DateTime;

namespace ShoppingCartLibrary

{

public class ShoppingCartImpl : IShoppingCartService

{

public bool CheckUserExists(string username)

{

if(username == "gvaish" || username == "edujini")

{

return true;

}

return false;

}

public DateTime? GetLastTransactionTime(string username)

{

if(username == "gvaish")

{

return DateTime.Now.AddDays(-3);

}

if(username == "edujini")

{

return DateTime.Now.AddHours(-3);

}

return null;

}

}

}

Service Configuration

We need configuration at two levels:

1. Using attributes, we need to tell WCF about the service contract and operation

contracts. The information would be used to define service, operations, messages and

types

2. Using configuration entries, we need to tell WCF about the implementation class to be

used and binding details. These details would be used for hosting purposes.

Contract Configuration

If you are using Visual Studio, add a reference to the assembly System.ServiceModel.dll from

GAC. Add the attribute System.ServiceModel.ServiceContractAttribute to the

interface. It tells the WCF that there is a service associated with the corresponding type. Add

the attribute System.ServiceModel.OperationContractAttribute to the methods in the

Page 153: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Exploring Windows Communication Foundation Seite 3 von 6

interface. It informs the WCF about the operations to be published in the service. It will take

care of messages and types associated on its own.

So, our final code looks as follows:

using DateTime;

using ServiceModel;

namespace ShoppingCartLibrary

{

[ServiceContract]

public interface IShoppingCartService

{

[OperationContract]

bool CheckUserExists(string username);

[OperationContract]

DateTime? GetLastTransactionTime(string username);

}

}

Service Configuration

We shall provide these details to our hosting application.

Compile the files to the assembly. I name it ShoppingCartLibrary.dll. If you are

compiling the code at the commandline, do not forget to add reference to

System.ServiceModel.dll.

Service Hosting

Having defined our service completely, we need a runtime (engine) to host the service - an

application that can allow the clients to connect and invoke the operations. For this, we shall

create a console application. The application has just one class, MainClass, with the Main

method.

For Visual Studio users, we name the project as ShoppingCartHost. Rename the class

Program to MainClass.

Add a reference to System.ServiceModel.dll and to the ShoppingCartLibrary.dll Write the

following code to the MainClass.

Collapse using System;

using System.ServiceModel;

using ShoppingCartLibrary;

namespace ShoppingCartHost

{

public class MainClass

{

public static void Main(string[] args)

{

Uri uri = new Uri("http://localhost:8080/ShoppingCartService");

Page 154: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Exploring Windows Communication Foundation Seite 4 von 6

using(ServiceHost host = new ServiceHost

(typeof(ShoppingCartImpl), uri))

{

host.Open();

Console.Write("Hit <Enter> to stop service...");

Console.ReadLine();

}

}

}

}

Now, we need the service configuration as mentioned earlier. Write the following code to the

application configuration file.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<behaviors>

<serviceBehaviors>

<behavior name="HttpGetBehaviour">

<serviceMetadata httpGetEnabled="true"/>

</behavior>

</serviceBehaviors>

</behaviors>

<services>

<service name="ShoppingCartLibrary.ShoppingCartImpl"

behaviorConfiguration="HttpGetBehaviour">

<endpoint binding="mexHttpBinding"

contract="ShoppingCartLibrary.IShoppingCartService"

address=""/>

</service>

</services>

</system.serviceModel>

</configuration>

Compile your code. Start the console application. Browse to the location

http://localhost:8080/ShoppingCartService.

Page 155: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Exploring Windows Communication Foundation Seite 5 von 6

Hurray! We are ready with our first service. Let's now design a client to consume the service.

Client Application

For our client, we need two items: the proxy client code and the configuration items. No no,

you don't write any of them. You can generate them using the WCF tool svcutil.exe. You can

locate this tool in the folder where your Visual Studio IDE is installed (default location

being %PROGRAMFILES%\Microsoft Visual Studio 8\Common7\IDE) or where the .NET

3.0 SDK is installed (default location being %PROGRAMFILES%\Microsoft Windows

SDK\v6.0\bin).

Ensure that the location to svcutil.exe is in your path. Invoke the following at commandline.

svcutil.exe /nologo /config:App.config

http://localhost:8080/ShoppingCartService?wsdl

It will generate two files: ShoppingCartImpl.cs and App.config. We just need a MainClass

with Main method on the client side to consume the service published. Write the following

code to the class:

using System;

namespace ShoppingCartConsumer

{

public class MainClass

{

public static void Main(string[] args)

{

Page 156: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Exploring Windows Communication Foundation Seite 6 von 6

ShoppingCartServiceClient client = new

ShoppingCartServiceClient();

string[] users = new string[] { "gvaish", "edujini", "other" };

foreach(string user in users)

{

Console.WriteLine("User: {0}", user);

Console.WriteLine("\tExists: {0}",

client.CheckUserExists(user));

DateTime? time = client.GetLastTransactionTime(user);

if(time.HasValue)

{

Console.WriteLine("\tLast Transaction: {0}",

time.Value);

} else

{

Console.WriteLine("\tNever transacted");

}

}

Console.WriteLine();

}

}

}

Don't forget to add a reference to System.ServiceModel.dll. Build your project. Execute the

client. You should get an output similar to that shown below:

Note that this is only a start... so, please have a little patience if you are looking for advanced

topics.

Notes

We chose to have an interface as our service contract. In general, we can have a class directly

as a service contract. However, it is always a good idea to have an interface and then provide

an implementation. In this way, we can keep and maintain the declaration of the contract and

implementation of the contract independently and modularly.

Summary

With WCF, it is possible to publish and consume web services in a very simple way.

Publishing a web service is not as simple as in ASP.NET. However, as may be seemingly

noticeable, the WCF may turn out to be much more flexible than ASP.NET service. One of

the flexibilities that we saw was to host it outside ASP.NET Runtime.

Page 157: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 1 von 8

Windows Workflow Foundation

Dino Esposito

http://msdn.microsoft.com/de-de/magazine/cc163640(en-us).aspx

In the January 2006 issue, Don Box and Dharma Shukla introduced Windows® Workflow

Foundation and discussed the overall architecture of the framework and its constituent

components ( WinFX Workflow: Simplify Development With The Declarative Model Of

Windows Workflow Foundation). It inspired me to take this topic one step further and discuss

how you can use Windows Workflow Foundation to address a common business scenario

where automatic processes intersect with human activity. It provides a framework for

developing and executing a wide range of applications based on complex processes. Typical

examples are document management, business-to-business, and consumer applications. You

can use Visual Studio® 2005 to help design the underlying workflow in addition to the top-

level applications and assemblies involved.

A Common Business Scenario

Organizations typically have a number of internal processes for tasks such as order processing,

purchase requests, travel expenses, and the like. Workflow brings order to these independent

processes in a transparent, dynamic, and robust fashion.

Let's consider a typical helpdesk workflow process. It begins when the helpdesk employee

gets a customer call and opens a ticket recording the name of the customer, the time of the call,

and a brief description of the issue. Once the ticket has been created, the employee forgets

about it and waits for another incoming call. At the end of the day, he logs off the computer

and goes home. At the same time, in another department, perhaps in another city, a bunch of

technicians are alerted to open issues. Each active technician picks up a request and either

solves it or escalates it to a second level of help. How would you write code to implement this

process?

You might have a Windows Forms application that collects input data about the call and

creates a record in a database—the ticket, with time, description, status, and a unique ID.

Users of a second Windows Forms application will see a real-time list of pending requests and

pick one up. The operator then does whatever is in his power to resolve the issue (call back

the customer, retrieve requested information, send e-mail, or perform some remote activity)

and indicates whether the problem was solved or will need to be investigated further. This

decision can be represented with an imperative action such as clicking a button to update the

ticket in the same underlying database. Finally, if there's another category of users to involve,

a custom made front end will give them a chance to indicate that the issue has been

successfully closed or aborted.

Although this process clearly represents a workflow with some human-driven decision points,

it can be easily implemented with traditional sequential code written with classic

programming languages and databases.

Applying Windows Workflow Foundation

When you have a workflow-based system composed of activities, like you have with

Windows Workflow Foundation, you can implement an application using a powerful mix of

imperative code and declarative maps of activities and the declarative rules that bind them.

The major benefit is that you can model (even visually) the solution and have Windows

Workflow embed a run-time server to interpret your graph and proceed along the links that

you defined among building blocks. The more complex the process, the simpler it is to devise

Page 158: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 2 von 8

and implement a flow for it. The more dynamically changeable the process, the less code

you'll need to write and maintain. Let's see how to implement a Windows Workflow

Foundation solution for the helpdesk scenario.

The Helpdesk Solution

The helpdesk workflow I've built begins by creating a ticket and then stop while waiting for a

response from a connected user or technician. Whether the ticket is closed or escalated, the

workflow gets an external event and updates the internal state of the application to track the

event. In light of this, the workflow requires an interaction with the outside world. These

kinds of asynchronous activities are one of the problems inherent in real-world workflow

processes that Windows Workflow Foundation addresses. Because interaction with an entity

outside the system is required, the host application and the workflow can define a contract for

any data exchange that proves necessary. The IHelpDeskService interface you see here

describes the communication interface that is established between the workflow and its host: [DataExchangeService]

public interface IHelpDeskService

{

event EventHandler<HelpDeskTicketEventArgs> TicketClosed;

event EventHandler<HelpDeskTicketEventArgs> TicketEscalated;

void CreateTicket( string description, string userRef, string createdBy);

void CloseTicket(string ticketID); void EscalateTicket(string ticketID);

}

Now let's begin by writing a service class named HelpDeskService, as shown in Figure 1.

The HelpDeskService class is added to the workflow runtime and represents the point of

contact between the host application and the workflow. The host application invokes the

public methods on the class to fire external events to the workflow. Fired events signal

domain-specific events that will guide the flow of operations. Methods on the

IHelpDeskService interface represent the only pieces of code that a workflow can invoke on

external components through the InvokeMethod activity. Factored out in this way, the logic of

the HelpDeskService adds a lot of flexibility to the workflow as it basically lets the workflow

call back the host for the actual implementation of basic actions. By including a class that

implements the IHelpDeskService interface different hosts can create, solve, or escalate

tickets using different algorithms and storage media while keeping the workflow logic intact.

You can compile the interface and the class in a distinct assembly or just keep these files

inside the same assembly where the workflow is defined.

Figure 1 Implementing Workflow with HelpDeskService using System;

using System.Threading;

using System.Workflow.ComponentModel;

using System.Workflow.Runtime;

using System.Workflow.Runtime.Messaging;

namespace MySamples

{

public class HelpDeskService : IHelpDeskService

{ // Implement events

public event EventHandler<HelpDeskTicketEventArgs> TicketClosed;

public event EventHandler<HelpDeskTicketEventArgs> TicketEscalated;

public void RaiseCloseTicketEvent(Guid instanceId)

{ // Raise the event to the workflow

HelpDeskTicketEventArgs args = new HelpDeskTicketEventArgs(instanceId,

"");

if (TicketClosed != null)

TicketClosed(this, args);

}

public void RaiseEscalateTicketEvent(Guid instanceId)

{ // Raise the event to the workflow

Page 159: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 3 von 8

HelpDeskTicketEventArgs args = new HelpDeskTicketEventArgs(instanceId,

"");

if (TicketEscalated != null)

TicketEscalated(this, args);

}

void IHelpDeskService.CreateTicket( string description, string userRef,

string createdBy)

{ // Fill up a ticket: same ID as the workflow

string ticketID = WorkFlowEnvironment.CurrentInstanceId.ToString();

// Create ticket on the DB

HelpDeskHelpers.CreateTicket( ticketID, description, userRef,

createdBy);

}

void IHelpDeskService.CloseTicket(string ticketID)

{ // Update ticket on the DB

HelpDeskHelpers.UpdateTicket(ticketID, TicketStatus.Closed);

}

void IHelpDeskService.EscalateTicket(string ticketID)

{ // Update ticket on the DB

HelpDeskHelpers.UpdateTicket(ticketID, TicketStatus.Escalated);

}

}

}

HelpDeskWorkflow is a sequential workflow hosted by a Windows Forms app. Figure 2

shows its graphical model. It begins with an InvokeMethod activity that causes the app to

create the ticket based on information provided by the user. After that, the ticket sits in a

database waiting for a technician to pick it up and either solve it or escalate it.

Figure 2 Helpdesk Workflow in Visual

Studio 2005 After creating the ticket, the workflow waits,

potentially for a long time—even hours or

days. What happens during this time? Should

the workflow instance stay loaded in

memory? If the workflow becomes idle, you

can unload it from memory—a process also

known as passivation. A local service, such

as the helpdesk service, remains the main

point of contact between the host application

and the sleeping workflow.

The Human Factor

When the human factor interacts with the

host application and does something to wake

up the workflow, the local service posts a

request to the runtime to resume the

passivated workflow. The Windows

Workflow Foundation toolbox contains an

activity, called Listen, that just idles the

workflow and listens for incoming wake-up calls. The block labeled WaitForSolution in

Figure 2 is an instance of the Listen activity.

Page 160: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 4 von 8

The Listen activity is basically useless without one or more child branches, each representing

a possible event that can be hooked up at this point. In the helpdesk example, the Listen

activity contains two branches—ticket closed and ticket escalated. Each branch is an

EventDriven activity. Both Listen and EventDriven activities require no special settings and

are mere containers of child activities. To catch an external event, the workflow needs an

EventSink component.

In Figure 2, the TicketClosed block is an EventSink bound to the TicketClosed event in the

local helpdesk service. Figure 3 lists the EventSink properties set here. To begin, you select

the interface of the service that exposes the event. Setting the InterfaceType property is a

requirement; in addition, you can only select an interface decorated with the

[DataExchangeService] attribute, as in Figure 1.

Figure 3 EventSink Properties

Once the data exchange interface is set, the

EventName property is prefilled with all

events found on the interface. You pick up

the event of choice and set its parameters. If

you want a further notification about when

the event has been processed, you set the

Invoked property, too.

The EventSink returns control to the

workflow after a period of idleness due to

waiting for human intervention. Following

the event, you proceed with any further activity that suits the workflow. For example, in the

helpdesk application I call another method on the local service to update the status of the

ticket in the ticket database.

In a real-world scenario, there's at least one database the workflow needs to interact with,

directly or indirectly. In this example, I'm using a SQL Server™ 2000 table, Tickets, that

stores a row for each ticket being processed. By design, the ID of the ticket matches the ID of

the workflow that is used to manage it.

The Helpdesk Front End

Once compiled, the workflow is nothing more than a reusable assembly and thus can be

referenced by any type of .NET-based application. Let's now imagine a front-end application

for helpdesk operators. The application is a Windows Forms program that allows users to fill

in a form and create a ticket to initiate the workflow, as shown in Figure 4.

Figure 4 HelpDesk

Front-End App The workflow calls into

the CreateTicket method

of the local service and

has the service add a

new record to the tickets

database. After the

operator has opened a

new ticket, he continues

Page 161: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 5 von 8

answering phone calls and replying to e-mails while the workflow is started and becomes idle

waiting for a human intervention. Each ticket is represented with a different instance of the

workflow. For simplicity, the ID of the workflow is also the ID of the ticket.

State Persistence

At the end of the day, a workflow is a tree of activities, so how is its lifetime managed? The

Windows Workflow Foundation run-time engine manages the execution of any workflow and

allows workflows to remain active for a long time and even survive machine reboots. The

run-time engine is powered by pluggable services that provide an overall rich execution

environment—transactions, persistence, tracking, timer, and threading.

It's unlikely that you want your workflow to stay in the memory of the host process until some

intervention causes it to proceed. As mentioned, many real-world human-based workflows

may take hours or longer for this to happen. As in the preceding example, the helpdesk

operator starts the workflow and loads the workflow class inside the front-end application

process. That operator may then shut down his machine and go home. However, the ticket

must remain active and available to other operators, even from within another application. For

this to happen, the workflow must support serialization to a durable medium.

A workflow may be a long-running operation and it is impractical for that object to remain

active in memory for days. First of all, a host process can't generally afford to cache all the

objects that accumulate in days of continued activity; second, the host process might shut

down or reboot more than once.

The solution is to configure the runtime to unload workflows as they go idle. In this case, the

host program will use the following code to initialize the runtime: WorkflowRuntime wr = new WorkflowRuntime();

wr.StartRuntime();

You can also set a few event handlers on the WorkflowRuntime class to run some code when

the workflow is idle, persisted, or unloaded. Configured in this way, the runtime will

automatically serialize the workflow to a storage medium as the workflow encounters a Listen

activity or enters a wait state. Another alternative is to instruct the host program to

programmatically persist the workflow at a well-known point of execution. In this case, you

call the EnqueueItemOnIdle method on the WorkflowInstance class. You get an instance of

the WorkflowInstance class as the return value of the CreateWorkflow method on the run-

time class: WorkflowInstance inst = theRuntime.CreateWorkflow(type); ...

inst.EnqueueItemOnIdle();

inst.Unload();

Just remember though that a call to EnqueueItemOnIdle doesn't necessarily persist the

workflow immediately. Persistence is immediate only if the workflow is idle or suspended.

Otherwise, the runtime takes note and persists the workflow instance later when the workflow

is suspended or idle. Note that EnqueueItemOnIdle is limited to saving the state of the

workflow instance and doesn't unload it from memory. To unload a workflow instance, you

need to call the Unload method. One of the standard run-time services supported by the

workflow runtime is the workflow persistence service. You turn it on through the code in

Figure 5.

Figure 5 Workflow Persistence Service private WorkflowRuntime InitWorkflowRuntime()

{ // Get a new workflow runtime

WorkflowRuntime wr = new WorkflowRuntime();

// Add custom HelpDesk

service theHelpDeskService = new HelpDeskService();

wr.AddService(theHelpDeskService);

// Add system SQL state service

Page 162: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 6 von 8

SqlWorkflowPersistenceService stateService = new

SqlWorkflowPersistenceService("Data Source=localhost;" + "Initial

Catalog=WFState;UID=...;");

wr.AddService(stateService);

// Start wr.StartRuntime();

return wr;

}

private void btnCreate_Click(object sender, EventArgs e)

{ // Fill the Parameters collection for this instance of the workflow

Dictionary<string, object> parameters = new Dictionary<string, object>();

parameters.Add("TicketDescription", txtDesc.Text);

parameters.Add("TicketUser", txtUser.Text);

parameters.Add("TicketCreatedBy", txtCreatedBy.Text);

// Get the type of the workflow

Type type = typeof(MySamples.HelpDeskWorkflow);

// Start the workflow instance

WorkflowInstance inst = theWorkflowRuntime.CreateWorkflow(type,

parameters);

// Feedback to the user

string msg = String.Format("The ticket '{0}' has been successfully " +

"created!", inst.InstanceId);

MessageBox.Show(msg, "HelpDesk", MessageBoxButtons.OK,

MessageBoxIcon.Information);

FillGrid();

}

The WorkflowPersistence service adds serialization capabilities to all the instances of

workflows that execute inside a given run-time engine. The workflow service core

functionality is represented by the WorkflowPersistenceService class. Windows Workflow

Foundation provides an implementation of it through the SqlWorkflowPersistenceService.

This class saves workflow data to a SQL Server 2000 or SQL Server 2005 database with a

well-known schema. There are a couple of scripts and dialog-based utilities that you can use

to easily create the target database. You can download everything you might need from

Windows Workflow Foundation Web. An instance of the SQLWorkflowPersistenceService

must be created and registered with the runtime before the runtime is actually started. The

constructor of the persistence service takes the connection string as its sole argument.

As mentioned, run-time services are a pluggable part of the overall Windows Workflow

Foundation architecture, so you can create your own services and replace standard services

with your own.

Resuming the Workflow Instance

In the scenario being discussed, another operator—the technician—is expected to pick up any

open tickets and do whatever he can to solve or escalate them. The technician will use another

application or, at least, another instance of the app shown in Figure 4. In both cases, another

instance of the workflow runtime must be created with persistence support and the right

workflow state must be loaded and resumed. As you can see, this can only happen if the ID of

the idled workflow has been saved. In the helpdesk sample app, by design, the ticket ID

matches the workflow ID and each ticket corresponds to an instance of the workflow created

to handle it.

Page 163: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 7 von 8

Figure 6 The Problem

Solver The Problem Solver

application (see Figure

6) initializes the

workflow runtime using

nearly the same code as

the HelpDesk front-end

application in Figure 4.

The only minor

difference is that in the

Problem Solver

application I register

handlers for the

WorkflowLoaded and

WorkflowCompleted

events on the runtime: WorkflowRuntime wr =

new

WorkflowRuntime(); wr.WorkflowLoaded += OnWorkflowLoaded;

wr.WorkflowCompleted += OnWorkflowCompleted;

The operator selects a ticket from the displayed list and works with the user who raised it.

When done, she clicks to solve or escalate the ticket, thus terminating the workflow. The

event handler should retrieve the saved instance of the workflow based on the ticket ID and

wake it up from the idle state. Here's the code you need: string workflowID = (string)ticketList.SelectedValue;

Guid id = new Guid(workflowID);

Type type = typeof(MySamples.HelpDeskWorkflow);

try {

WorkflowInstance inst = theWorkflowRuntime.GetLoadedWorkflow(id);

theHelpDeskService.RaiseCloseTicketEvent(inst.InstanceId);

}

catch { ... }

The core part of the preceding code is in the call to the GetLoadedWorkflow method.

GetLoadedWorkflow takes the GUID that represents the workflow instance and retrieves it

from the configured durable medium, if it is not currently in memory. In this case, the

workflow is loaded into memory and scheduled for execution. This happens even if the

workflow instance has been previously aborted. The WorkflowLoaded event fires when the

workflow is loaded back into memory.

GetLoadedWorkflow returns an object of type WorkflowInstance that you can use for

inspecting the current status of the workflow as well as its activities. At this point, you raise

one of the events the workflow was waiting for. The corresponding EventSink activity will

trap the event and proceed until the end of the workflow is reached or a subsequent wait point

is encountered.

When the workflow completes, it is automatically removed from the Windows Workflow

Foundation persistence database. The WorkflowCompleted event fires when the workflow has

reached its end. In the helpdesk scenario, the workflow completes after the operator clicks to

solve or escalate (see Figure 6).

From a development perspective, it is essential to note two things. First, to raise an event to

the workflow, you have to post the request to a pooled thread: public void RaiseCloseTicketEvent(Guid instanceId)

{

Page 164: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Windows Workflow Foundation Seite 8 von 8

ThreadPool.QueueUserWorkItem(JustCloseTheTicket, new

HelpDeskTicketEventArgs(instanceId, "Jim"));

}

public void JustCloseTheTicket(object o)

{

HelpDeskTicketEventArgs args = o as HelpDeskTicketEventArgs;

if (TicketClosed != null)

TicketClosed(null, args);

}

Second, the WorkflowCompleted event may not be raised on the main Windows Forms thread,

so you cannot directly update any of the UI controls. (This is due to the fact that Windows

Forms controls are not thread-safe.) Updating Windows Forms controls from within a thread

different from the one which created them is definitely possible, but requires an indirect call

to the method that updates controls: private delegate void UpdateListDelegate();

private void UpdateList()

{

this.Invoke(new UpdateListDelegate(FillList));

}

You call UpdateList from within the WorkflowCompleted event handler. The Invoke method

on the Form class guarantees that FillList gets called on the right thread so that any control

can be safely refreshed.

Conclusion

Windows Workflow Foundation allows you to visually design complex algorithms to solve

business problems and model processes. A workflow is a tool used to describe a flow of data

and actions. Therefore, any scenario where an IF or a WHILE statement are required can be a

workflow. However, nobody would ever use a workflow for just an IF statement; the

workflow runtime does have a cost that can be amortized as the complexity of the flow grows

beyond a given threshold.

Where's the boundary that delimits the cost effective use of a workflow? The helpdesk

scenario, as implemented in this column, is probably too simple for a workflow and could

have been implemented in an equivalent way by employing a tickets database—even the same

tickets database that the workflow deals with through the helpdesk service.

The real benefit of a workflow-based solution consists of making complex processes simpler

to model and implement, and, more importantly, evolve and extend. Windows Workflow

Foundation provides a managed execution environment for Windows Workflow programs. It

provides durability, robustness, suspension/resumption, and compensation characteristics to

the programs. In a sense, activities are analogous to intermediate language (IL) opcodes or

program statements, but contain domain-specific wisdom. In short, Windows Workflow

Foundation makes your program semantics declarative and explicit, and it lets you model

your applications close to the real-world process. It's the right tool for the job. You wouldn't

write a front-end visual application using IL, but rather you would use a RAD development

tool and a more human-readable language. The Windows Workflow Foundation SDK

provides an extensible programming language designed for modeling complex business

processes, especially when these processes may evolve over time. In this case, the key benefit

is that you add ad hoc activities to address the flow of work and use built-in behavioral

activities to control that flow. You focus on tasks, the runtime does the rest.

Page 165: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Garbage Collection in .NET Seite 1 von 6

Garbage Collection in .NET Quelle: http://www.codeproject.com/kb/dotnet/garbagecollection.aspx

.NET is the much hyped revolutionary technology gifted to the programmer's community by

Microsoft. Many factors make it a must use for most developers. In this article we would like

to discuss one of the primary advantages of .NET framework - the ease in memory and

resource management.

About garbage collection

Every program uses resources of one sort or another-memory buffers, network connections,

database resources, and so on. In fact, in an object-oriented environment, every type identifies

some resource available for a program's use. To use any of these resources, memory must be

allocated to represent the type.

The steps required to access a resource are as follows:

1. Allocate memory for the type that represents the resource.

2. Initialize the memory to set the initial state of the resource and to make the resource

usable.

3. Use the resource by accessing the instance members of the type (repeat as necessary).

4. Tear down the state of the resource to clean up.

5. Free the memory.

The garbage collector (GC) of .NET completely absolves the developer from tracking

memory usage and knowing when to free memory. The Microsoft -- .NET CLR (Common

Language Runtime) requires that all resources be allocated from the managed heap. You

never free objects from the managed heap-objects are automatically freed when they are no

longer needed by the application. Memory is not infinite. The garbage collector must perform

a collection in order to free some memory. The garbage collector's optimizing engine

determines the best time to perform a collection, (the exact criteria is guarded by Microsoft)

based upon the allocations being made. When the garbage collector performs a collection, it

checks for objects in the managed heap that are no longer being used by the application and

performs the necessary operations to reclaim their memory. However for automatic memory

management, the garbage collector has to know the location of the roots i.e. it should know

when an object is no longer in use by the application. This knowledge is made available to the

GC in .NET by the inclusion of a concept know as metadata. Every data type used in .NET

software includes metadata that describes it. With the help of metadata, the CLR knows the

layout of each of the objects in memory, which helps the Garbage Collector in the compaction

phase of Garbage collection. Without this knowledge the Garbage Collector wouldn't know

where one object instance ends and the next begins.

Garbage Collection Algorithm

Application Roots

Every application has a set of roots. Roots identify storage locations, which refer to objects on

the managed heap or to objects that are set to null.

Page 166: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Garbage Collection in .NET Seite 2 von 6

For example:

• All the global and static object pointers in an application.

• Any local variable/parameter object pointers on a thread's stack.

• Any CPU registers containing pointers to objects in the managed heap.

• Pointers to the objects from Freachable queue

• The list of active roots is maintained by the just-in-time (JIT) compiler and common

language runtime, and is made accessible to the garbage collector's algorithm.

Implementation

Garbage collection in .NET is done using tracing collection and specifically the CLR

implements the Mark/Compact collector.

This method consists of two phases as described below.

Phase I: Mark

Find memory that can be reclaimed. When the garbage collector starts running, it makes the

assumption that all objects in the heap are garbage. In other words, it assumes that none of the

application's roots refer to any objects in the heap.

The following steps are included in Phase I:

1. The GC identifies live object references or application roots.

2. It starts walking the roots and building a graph of all objects reachable from the roots.

3. If the GC attempts to add an object already present in the graph, then it stops walking

down that path. This serves two purposes. First, it helps performance significantly

since it doesn't walk through a set of objects more than once. Second, it prevents

infinite loops should you have any circular linked lists of objects. Thus cycles are

handles properly.

Once all the roots have been checked, the garbage collector's graph contains the set of all

objects that are somehow reachable from the application's roots; any objects that are not in the

graph are not accessible by the application, and are therefore considered garbage.

Phase II: Compact

Move all the live objects to the bottom of the heap, leaving free space at the top. Phase II

includes the following steps:

1. The garbage collector now walks through the heap linearly, looking for contiguous

blocks of garbage objects (now considered free space).

2. The garbage collector then shifts the non-garbage objects down in memory, removing

all of the gaps in the heap.

3. Moving the objects in memory invalidates all pointers to the objects. So the garbage

collector modifies the application's roots so that the pointers point to the objects' new

locations.

4. In addition, if any object contains a pointer to another object, the garbage collector is

responsible for correcting these pointers as well.

Page 167: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Garbage Collection in .NET Seite 3 von 6

After all the garbage has been identified, all the non-garbage has been compacted, and all the

non-garbage pointers have been fixed-up, a pointer is positioned just after the last non-

garbage object to indicate the position where the next object can be added.

Finalization

.NET Framework's garbage collection implicitly keeps track of the lifetime of the objects that

an application creates, but fails when it comes to the unmanaged resources (i.e. a file, a

window or a network connection) that objects encapsulate.

The unmanaged resources must be explicitly released once the application has finished using

them. .NET Framework provides the Object.Finalize method: a method that the garbage

collector must run on the object to clean up its unmanaged resources, prior to reclaiming the

memory used up by the object. Since Finalize method does nothing, by default, this method

must be overridden if explicit cleanup is required.

It would not be surprising if you will consider Finalize just another name for destructors in

C++. Though, both have been assigned the responsibility of freeing the resources used by the

objects, they have very different semantics. In C++, destructors are executed immediately

when the object goes out of scope whereas a finalize method is called once when Garbage

collection gets around to cleaning up an object.

The potential existence of finalizers complicates the job of garbage collection in .NET by

adding some extra steps before freeing an object.

Whenever a new object, having a Finalize method, is allocated on the heap a pointer to the

object is placed in an internal data structure called Finalization queue. When an object is not

reachable, the garbage collector considers the object garbage. The garbage collector scans the

finalization queue looking for pointers to these objects. When a pointer is found, the pointer is

removed from the finalization queue and appended to another internal data structure called

Freachable queue, making the object no longer a part of the garbage. At this point, the

garbage collector has finished identifying garbage. The garbage collector compacts the

reclaimable memory and the special runtime thread empties the freachable queue, executing

each object's Finalize method.

The next time the garbage collector is invoked, it sees that the finalized objects are truly

garbage and the memory for those objects is then, simply freed.

Thus when an object requires finalization, it dies, then lives (resurrects) and finally dies again.

It is recommended to avoid using Finalize method, unless required. Finalize methods increase

memory pressure by not letting the memory and the resources used by that object to be

released, until two garbage collections. Since you do not have control on the order in which

the finalize methods are executed, it may lead to unpredictable results.

Garbage Collection Performance Optimizations

Weak References

Weak references are a means of performance enhancement, used to reduce the pressure placed

on the managed heap by large objects.

Page 168: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Garbage Collection in .NET Seite 4 von 6

When a root points to an abject it's called a strong reference to the object and the object

cannot be collected because the application's code can reach the object.

When an object has a weak reference to it, it basically means that if there is a memory

requirement & the garbage collector runs, the object can be collected and when the

application later attempts to access the object, the access will fail. On the other hand, to access

a weakly referenced object, the application must obtain a strong reference to the object. If the

application obtains this strong reference before the garbage collector collects the object, then

the GC cannot collect the object because a strong reference to the object exists.

The managed heap contains two internal data structures whose sole purpose is to manage

weak references: the short weak reference table and the long weak reference table.

Weak references are of two types:

• A short weak reference doesn't track resurrection.

i.e. the object which has a short weak reference to itself is collected immediately

without running its finalization method.

• A long weak reference tracks resurrection.

i.e. the garbage collector collects object pointed to by the long weak reference table

only after determining that the object's storage is reclaimable. If the object has a

Finalize method, the Finalize method has been called and the object was not

resurrected.

These two tables simply contain pointers to objects allocated within the managed heap.

Initially, both tables are empty. When you create a WeakReference object, an object is not

allocated from the managed heap. Instead, an empty slot in one of the weak reference tables is

located; short weak references use the short weak reference table and long weak references

use the long weak reference table.

Consider an example of what happens when the garbage collector runs. The diagrams (Figure

1 & 2) below show the state of all the internal data structures before and after the GC runs.

Now, here's what happens when a garbage collection (GC) runs:

Page 169: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Garbage Collection in .NET Seite 5 von 6

1. The garbage collector builds a graph of all the reachable objects. In the above example,

the graph will include objects B, C, E, G.

2. The garbage collector scans the short weak reference table. If a pointer in the table

refers to an object that is not part of the graph, then the pointer identifies an

unreachable object and the slot in the short weak reference table is set to null. In the

above example, slot of object D is set to null since it is not a part of the graph.

3. The garbage collector scans the finalization queue. If a pointer in the queue refers to

an object that is not part of the graph, then the pointer identifies an unreachable object

and the pointer is moved from the finalization queue to the freachable queue. At this

point, the object is added to the graph since the object is now considered reachable. In

the above example, though objects A, D, F are not included in the graph they are

treated as reachable objects because they are part of the finalization queue.

Finalization queue thus gets emptied.

4. The garbage collector scans the long weak reference table. If a pointer in the table

refers to an object that is not part of the graph (which now contains the objects pointed

to by entries in the freachable queue), then the pointer identifies an unreachable object

and the slot is set to null. Since both the objects C and F are a part of the graph (of the

previous step), none of them are set to null in the long reference table.

5. The garbage collector compacts the memory, squeezing out the holes left by the

unreachable objects. In the above example, object H is the only object that gets

removed from the heap and it's memory is reclaimed.

Generations

Since garbage collection cannot complete without stopping the entire program, they can cause

arbitrarily long pauses at arbitrary times during the execution of the program. Garbage

collection pauses can also prevent programs from responding to events quickly enough to

satisfy the requirements of real-time systems.

Page 170: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010

Anhang

Garbage Collection in .NET Seite 6 von 6

One feature of the garbage collector that exists purely to improve performance is called

generations. A generational garbage collector takes into account two facts that have been

empirically observed in most programs in a variety of languages:

1. Newly created objects tend to have short lives.

2. The older an object is, the longer it will survive.

Generational collectors group objects by age and collect younger objects more often than

older objects. When initialized, the managed heap contains no objects. All new objects added

to the heap can be said to be in generation 0, until the heap gets filled up which invokes

garbage collection. As most objects are short-lived, only a small percentage of young objects

are likely to survive their first collection. Once an object survives the first garbage collection,

it gets promoted to generation 1.Newer objects after GC can then be said to be in generation

0.The garbage collector gets invoked next only when the sub-heap of generation 0 gets filled

up. All objects in generation 1 that survive get compacted and promoted to generation 2. All

survivors in generation 0 also get compacted and promoted to generation 1. Generation 0 then

contains no objects, but all newer objects after GC go into generation 0.

Thus, as objects "mature" (survive multiple garbage collections) in their current generation,

they are moved to the next older generation. Generation 2 is the maximum generation

supported by the runtime's garbage collector. When future collections occur, any surviving

objects currently in generation 2 simply stay in generation 2.

Thus, dividing the heap into generations of objects and collecting and compacting younger

generation objects improves the efficiency of the basic underlying garbage collection

algorithm by reclaiming a significant amount of space from the heap and also being faster

than if the collector had examined the objects in all generations.

A garbage collector that can perform generational collections, each of which is guaranteed (or

at least very likely) to require less than a certain maximum amount of time, can help make

runtime suitable for real-time environment and also prevent pauses that are noticeable to the

user.

Page 171: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Wie funktioniert der Java Garbage Collector Seite 1 von 3

Wie funktioniert der Java Garbage Collector

(Link zum Artikel: http://www.it-republik.de/jaxenter/artikel/2452)

Garbage Collection und Monitoring von Webanwendungen

Text: Kai Jäger Eine Eigenschaft von Java ist, dass man sich nicht selbst um das Speichermanagement kümmern muss. Denn der Garbage Collector der Java Virtual Machine (JVM) erledigt dies zufriedenstellend. Jedoch kommt es vor, dass Java-Anwendungen im Produktivbetrieb mit einer OutOfMemoryError terminieren. Im Rahmen des Monitorings ist es nützlich, dies vorher zu erfassen, um Gegenmaßnahmen einleiten zu können. Dieser Artikel illustriert die Funktionsweise des Garbage Collectors und geht auf Metriken ein, die beim Monitoring unterstützen.

Grundlegendes

Im Gegensatz zu C oder C++, die keine Garbage Collection (GC) verwenden, muss in Java dynamisch zugewiesener Speicher nicht explizit freigegeben werden. Nicht mehr referenzierte Objekte werden vom Garbage Collector automatisch erkannt und entfernt. Somit werden einige prominente Fehlerquellen eliminiert, die noch referenziert werden, z. B. das vorzeitige Freigeben von Objekten oder das Vergessen nicht mehr referenzierter Objekte. Jedoch erzeugt das automatisierte Einsammeln durch die Laufzeitumgebung einen gewissen Overhead.

Die Organisation des Heap-Speichers in Java, hier am Beispiel der Sun-Implementierung, lässt sich vereinfacht wie folgt wiedergeben (Abb. 1): Bei der Allokation eines Objekts wird es zunächst in der Young Generation (Nursery) angelegt. "Überlebt" es einige Zeit, wird es noch referenziert, also in die Old Generation (Tenured) "befördert". Da ein Großteil der erzeugten Objekte nicht referenziert ist, kann der Garbage Collector in der Young Generation mit einem schnellen Algorithmus alle nicht referenzierten Objekte entfernen. In der Old Generation, die normalerweise den Hauptbestandteil des Heaps ausmacht, wird ein langsamerer, aber speicherplatzeffizienterer Algorithmus verwendet.

Abb. 1: Interner Aufbau des Heaps

Die Permanent Generation (PermGen) enthält Objekte, die gemäß Vorgabe der Java Virtual Machine vom Garbage Collector verwaltet werden sollen. Das sind z. B. Objekte, die Klassen und Methoden beschreiben, sowie die Klassen und Methoden selbst. Der PermGen gehört nicht zum Heap.

Es existieren also zwei unterschiedliche Varianten der Garbage Collection: Die schnelle GC in der Young Generation, die sehr häufig ausgeführt wird (Minor Collection) und die langsamere GC in der Old Generation (Full Collection). Wichtig ist hierbei, dass sowohl bei den Minor Collections als auch bei den Full Collections eine "stop the world"-Pause eingelegt wird, also alles andere

Page 172: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Wie funktioniert der Java Garbage Collector Seite 2 von 3

außer der Garbage Collection angehalten und nach Beendigung der Operation fortgesetzt wird. Dies dauert je nach Collector-Art unterschiedlich lange.

Garbage-Collection-Algorithmen

Die folgenden Abschnitte geben einen Überblick über die unterschiedlichen GC-Algorithmen der Young, Old und Permanent Generation. Dabei wird nicht auf alle GC-Algorithmen eingegangen. Der Speicherbereich der Young Generation ist unterteilt in Eden und zwei gleich große Survivor Spaces, oft From-Survivor und To-Survivor genannt. Der Eden Space macht hierbei den Großteil des Speichers aus. Neue Objekte werden im Eden Space generiert. Sind sie zu groß für diesen, werden sie direkt in der Old Generation erzeugt (Abb. 2).

Abb. 2: Interner Aufbau der Young Generation

Beginnt der Garbage Collector seine Arbeit, werden alle noch referenzierten Objekte im Eden Space und From-Survivor in den To-Survivor kopiert. Objekte, die sich schon einige Zeit in den Survivor Spaces befanden, werden in die Old Generation verschoben. Anschließend werden der Eden Space und der From-Survivor geleert und die beiden Survivor Spaces "tauschen" die Funktion: Aus dem From-Survivor wird der To-Survivor und umgekehrt. Denn der Großteil aller Objekte im

Eden Space wird nicht oder nur kurz referenziert, z. B. Iterator-Objekte oder sonstige Schleifenvariablen. Durch das Kopieren in die Survivor Spaces wird sichergestellt, dass solche Objekte nicht direkt in die Old Generation verschoben werden, nur weil sie zum Zeitpunkt der Garbage Collection noch eine Referenz trugen.

In einem einfachen Beispiel lässt sich der Vorgang wie folgt darstellen (Abb. 3): Das erste Bild zeigt symbolisch die Speicherbelegung der Young Generation. Die Objekte werden durch ihrer Größe entsprechende Blöcke angezeigt. Die grünen Objekte werden momentan referenziert, die roten sind nicht mehr referenziert. Wird nun ein neues Objekt angelegt, ist kein Platz mehr im Eden Space vorhanden. Der Garbage Collector nimmt seine Arbeit auf. Er verschiebt alle Objekte, die noch referenziert sind (hier 1, 3, 5 und 9), in den To-Survivor Space. In diesem Beispiel wird das Objekt 5 – aufgrund der Zeit, die es in den Survivor Spaces "überlebt" hat – in die Old Generation verschoben. Sind mehr Objekte referenziert, als in den To-Survivor Space passen, werden die restlichen direkt in die Old Generation verschoben. Abschließend werden der Eden Space und der From-Survivor Space geleert, also die Objekte 2, 4, 6, 7 und 8 endgültig gelöscht. Nach Abschluss einer Garbage Collection in der Young Generation ist daher der gesamte Speicher bis auf einen der beiden Survivor Spaces leer.

Page 173: Net Framework und C# SS 2010 - fbi.h-da.de · PDF file.Net Framework und C# SS 2010 Dr. Ute Blechschmidt-Trapp Darmstadt, 20. März 2010

Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2010 Anhang

Wie funktioniert der Java Garbage Collector Seite 3 von 3

Abb. 3: Ablauf der Garbage Collection in der Young Generation

Old Generation/Permanent Generation

Die Bereinigung der Old Generation und der Permanent Generation erfolgt durch einen mark-sweep-compact-Algorithmus. In der mark-Phase werden alle Objekte markiert, die von den Wurzelzeigern erreichbar sind (z. B. aktive Thread-Objekte, Systemklassen, lokale Variablen, hängige Exceptions, Referenzen vom Native Stack). Das Entfernen der nicht markierten Objekte erfolgt in der sweep-Phase. Abschließend werden die Markierungen von den Objekten wieder entfernt. In der compact-Phase erfolgt eine Verschiebung der Objekte an den Anfang des Speicherbereichs, um eine möglichst große, zusammenhängende Speicherregion zu erhalten. Das wird durch Abbildung 4 verdeutlicht.

Das erste Bild zeigt symbolisch den Old-Generation- oder Permanent-Generation-Speicherbereich. Die einzelnen Blöcke stellen Objekte dar, die Pfeile visualisieren die Verweise untereinander. Im Beispiel hat das Objekt 1 einen Wurzelzeiger und eine Referenz auf das Objekt 3, Objekt 2 hat eine Referenz auf Objekt 4 und so weiter.

Die Markierung aller vom Wurzelknoten aus erreichbaren Objekte (grün) erfolgt in der mark-Phase. Objekt 4 wird zwar vom Objekt 2 referenziert, hat jedoch keine Verbindung zum Wurzelknoten, ebenso wie Objekt 5. Anschließend werden in der sweep-Phase die nicht markierten Objekte gelöscht und die Markierungen entfernt. Der Speicher ist nun fragmentiert.

Die abschließende compact-Phase verschiebt alle Objekte an den Anfang des Speicherbereichs, sodass sich der frei verfügbare Speicher in einem großen Block hinter dem letzten Objekt befindet. Dies erleichtert die Speicherzuteilung für Objekte.

Weiterführende Informationen – insbesondere zu den verwendeten Algorithmen (copy from to bzw. mark-sweep-compact) – gibt ein Whitepaper von Sun zu diesem Thema. Außerdem wurde der Garbage Collector in der Ausgabe 08/2009 im Java Magazin thematisiert.

Abb. 4: Ablauf der Garbage Collection in der Old Generation/Permanent Generation