Was stellt uns das Framework zur Verfügung ?
Technologien und die Tools für
skalierbare Anwendungen mit Fokus Intranet / Internet,
aber nicht nur.
Unterstützt werden plattformneutrale Technologien und
Protokolle wie
SOAP = HTTP + XML. Das .Net-Framework - mehr als eine
Klassenbibliothek.
Die Common-Language-Runtime,
ihre Aufgaben :
Garbage Collection, eingebautes Typsystem, dadurch
sprachübergreifend,
einfache Installation, Versionierung vorhanden und
vor allem Security.
"Hello World" in C# , unsere erste Console Anwendung mit dem Notepad erstellt.
In .Net gibt es keinen Code außerhalb von Klassen.
Bei mehreren tausend
Klassen bedarf es einer gewissen Ordnung - der Namespace.
Ein Namespace
verschafft uns Zugriff auf Namen, nicht auf Code.
"Using Namespace" erspart
Schreibarbeit.
Hat der Compiler Probleme damit, dann bitte voll
qualifizieren.
Wie wird der Code im Managed Execution Environment
ausgeführt ?
Code just in time oder komplett kompilieren mit Ngen ?
Wie wird das Programm kompiliert ?
Was bekommen wir ? Ein Assembly, eine
ausführbare Einheit.
Was enthält ein Assembly ? Code und viel Information
dazu - die Metadaten.
Der "Missel" (MSIL) Code arbeitet wie
eine Stackmaschine.
MSIL ist einfach, es gibt auch gar nicht so viele
Befehle.
Den können wir uns anschauen, dafür gibt es den Disassembler ILDASM.
Wir reden auch noch über AppDomains - Miniprozesse, und warum es so etwas gibt.
Wie erstellt man wieder verwendbare Komponenten in .Net ?
Die Erstellung in
.Net ist trivial. Sie reduziert sich auf das Schreiben von Klassen.
Diese Klassen werden in eine ClassLibrary gepackt.
Dafür gibt es im Visual
Studio ein Template.
Die Komponente kann dann entsprechend referenziert
in Console,
Windows Forms und Web Forms Anwendungen eingebunden
werden.
Sie entspricht einem Inprocess Server in COM.
Wie werden
Komponenten erstellt ?
Im NotePad und kompilieren auf der Kommando Zeile.
Im Visual Studio.
Wir erstellen 3 gleiche Komponenénten in C# , VB.NET und C++ mit Extensions.
Wir erstellen verschiedene Clients: Console-, Windows Forms- und Web Forms
Client .
Module sind allein nicht lauffähig, sie können aber bei Bedarf zur Laufzeit
nachgeladen werden.
Assemblys sind ausführbar und enthalten ein
Manifest, ein Inhaltsverzeichnis über
alle referenzierten Dateien und Typen.
Assemblys können aus einer oder auch aus mehreren Dateien bestehen.
Sie sind
versionierbar, sofern sie einen “Strong Name” haben.
Welche Arten von
Anwendungen gibt es in .NET ?
CTS, ein gemeinsames Typsystem für alle Sprachen, die Basis für das
Sprachübergreifende Programmieren.
Alle Typen in .Net sind Objekte, auch
einfache Typen wie short oder int. Diese
Objekte werden aber als Wert-Typen auf dem Stack und nicht auf dem Heap
angelegt.
Auch Strukturen sind Werttypen. Wie werden sie deklariert und
verwendet ?
Enums gehören ebenfalls dazu, sie können durch beliebige Typen implementiert
werden,
außer durch char.
Namensgebung: Pascal und camelCasing, wo
bleibt die wirklich sinnvolle
ungarische Notation ?
In C# gibt es 2 Arten Zahlen zu formatieren: mit speziellen Format-Strings
oder mit der Custom - Formatierung
Der Compiler erzeugt keinen Fehler bei
Overflow, noch nicht einmal eine Warnung.
Dafür gibt es aber ein "checked"
- Keyword, das eine Exception erzeugt,
diese kann man behandeln.
Enthält Unicode Zeichen ( 2 Byte ). String ist ein Alias für "System.String"
Strings sind nicht veränderbar, daher "Thread-Save", Methoden, die einen String
zurückgeben, erzeugen einen neuen String.
Für den Referenz Typ String,
sie werden auf dem Heap angelegt, ist die Equals-Methode
innerhalb der
String Klasse auf Wert-Vergleich überschrieben worden.
Die Klasse
StringBuilder erzeugt bei Änderungen im String nicht jedes Mal ein
neues Objekt, sie ist gerade in Schleifen der Klasse String vorzuziehen.
Alle Klassen in .Net sind von der Klasse "System.Object" abgeleitet.
Die
Klasse "System.Object" liefert eine gewisse Grundfunktionalität, einige Methoden
sollten überschrieben werden.
ToString () liefert per Default den Namen
der Klasse, sie sollte für einen
Werte-Vergleich überschrieben werden.
GetHashCode() liefert per Default eine fortlaufende Nummer und sollte eine
eindeutige Zahl liefern.
Wenn Methoden wie Equals überschrieben
werden, so müssen auch Operatoren
wie "==" und "!=" überschrieben
werden.
Wie schaut die Implementierung einer Klasse, aus wenn diese Methoden
und die
Operatoren entsprechend überschrieben werden ?
In C# wird ein Array anders deklariert als in C oder C++.
Die Größe des Arrays
ist nicht Bestandteil des Arrays.
Arrays werden immer mit "new" angelegt,
auch wenn man es nicht so schreibt.
Arrays sind Objekte in .Net, sie
werden auf dem Heap angelegt.
Arrays haben Methoden.
Mehrdimensionale Arrays.
Array Member enthalten selbst ein Array: Das "Jagged Array".
Der Zugriff
bei Arrays erfolgt über den Index.
Nützlich: das Length Property und die
GetLength Methode.
Wie übergebe ich Arrays in Funktionen, wie greife
ich darauf zu ?
Der Zugriff auf die Command Line Argumente.
Warum Exceptions ? Die Realisierung mit try catch.
Vorsicht! Exception Objekte
sind nur im Catch Block gültig.
Verschiedene Exception Klassen in .Net.
Mehrfache Catch Blöcke : zuerst spezifische Exceptions dann allgemeine
Exceptions abfangen. Abgeleitete Exception Klassen dürfen nicht hinter Basis
Klassen stehen.
Bitte Exceptions auch nur für Ausnahmen verwenden, nicht
für den normalen
Programmablauf !
Mit "throw" Exceptions auslösen.
Wir können auch eigene Exception-Klassen erstellen.
Der Finally Block wird
immer angesprungen, ein guter Platz zum Aufräumen.
Mit checked das
Overflow Problem behandeln.
Ein Interface hat eine "Feature Charakteristik", implementiert eine Klasse ein
Interface
so kann es z.B. Sortieren, Enumerieren, ein Kopie von sich
erstellen etc.
Ein "Hello World" Interface : Das Interface legt den
Prototyp der Methoden fest,
die davon abgeleitete Klasse muss alle Methoden
des Interfaces implementieren.
In .Net gibt es keine Mehrfachvererbung
wie in C++, abgeleitete Klassen können
nur von einer Basis Klasse Code
erben, sie können aber von beliebig vielen
Interfaces abgeleitet sein. Das
funktioniert, da hier kein Code sondern nur Prototypen
von Funktionen
vererbt werden, und die enthalten keinen Code.
Es sollte vor der
Verwendung des Interfaces grundsätzlich geprüft werden, ob die
Klasse dieses
auch implementiert hat, dafür gibt es Operatoren.
Neben dem Polymorhismus über die Vererbung gibt es auch den Polymorhismus
über Interfaces. Dieser ist oft passender, da er unabhängig von einer
Vererbungshierarchie arbeitet: Auf diese Weise können eigene Klassen von der
Klassenbibliothek profitieren, wenn sie bestimmte Interfaces implementieren.
Implementiert z.B. die eigene Klasse die beiden Enumerations-Interfaces so kann
man
eine "foreach" - Anweisung über die eigenen Klassen machen.
Die
Implementierung des Interfaces kann sich auch in einer Basisklasse befinden.
Vorsicht bei Vererbung : Eine abgeleitete Klasse enthält eine Methode, die den
gleichen Namen hat wie eine vererbte Interface Methode.
Das ist auch der
Grund, warum man bei Interfaces stets über das Interface und
nicht über das
Objekt auf die Interface Methoden zugreifen sollte.
Das .Net Framework liefert eine Implementierung von Internetdiensten
die
mehrschichtig und erweiterbar ist. Man kann sie einfach in eigene Anwendungen
einbinden.
Die Namespaces System.Net and System.Net.Sockets Namespaces
enthalten Klassen zum Senden und Emfangen von Daten über das Internet.
Ihre
Funktionalität ähnelt der von WinInet API.
Angewendet wird das Request /
Response-Model. Wie ist der Vorgang ?
Der Client macht einen Request mit
folgenden Angaben: Die Internet Ressource,
das Protokoll wie http, https.
Falls erforderlich werden noch zusätzliche Daten wie
Proxy Server und
Informationen zur Authentifizierung verwendet.
Die Antwort wird an die
Client-Anwendung zurückgeschickt. Sie enthält die
eigentliche Information in Text- oder XML Form und zusätzliche Information wie
Status,
Type of Content...
Das .NET Framework enthält Klassen zur Implementierung des Request / Response
Modells.
Die WebRequest-Klasse enthält die Anfrage für eine Internet
Resource, die
WebResponse-Klasse stellt einen Container für ankommende
Antworten dar.
Zusätzlich wird eine Uri-Klasse verwendet, sie enthält den
Uniform Resource Identifier (URI ).
Die NetworkStream-Klasse wird verwendet
zum Schreiben und Lesen der Daten.
Wir schreiben eine Console Anwendung zu Lesen einer Internet Seite.
Welche Klassen gibt es ?
Die NetworkStream-Klasse:
Ermöglicht Web
Daten in verschiedenen Formaten (HTML,XML..) zu senden und zu
empfangen. Es
werden die Methoden Stream.Read() zum Lesen und Stream.Write()
zum Schreiben
von Bytes verwendet. Sie enthält Methoden, die kompatibel zu
anderen Streams
von .Net sind.
Nur wenige Zeilen Code reichen aus, damit eine Anwendung statt
XML Daten aus
einem FileStream, Daten von einem Network Stream liest. Sie
kann die Daten schon
beim Eintreffen verarbeiten.
Sockets:
Der
System.Net.Sockets Namespace enthält eine Implementierung der
Windows
Sockets Schnittstelle. Alle anderen Klassen für den Netzwerkzugriff setzen
auf dieser Socket Implementierung auf. Die Socket Klasse des .NET Frameworks
ist eine in Managed Code geschriebene Version der Socket Dienste die vom
WinSock32 API zur Verfügung gestellt werden. In den meisten Fällen
marshaln
die Methoden die Daten für die Win32 Aufrufe und führen die
notwendigen
Security Checks durch.
TCP and UDP Services:
Anwendung
können TCP and UDP Services verwenden mit : TcpClient, TcpListener
und den
UdpClient Klassen. Diese setzen auf den Socket Klassen auf, sie
repräsentieren die Daten als Network Streams. Sie kümmern sich auch um
Details beim Verbindungsaufbau.
Die TcpClient Klasse:
Die Methoden und
Propertys abstrahieren die Details der Socket Erstellung
Die
Verbindung zum Internet wird durch einen Stream repräsentiert
Somit können
Daten in einer allgemeinen Weise gelesen und geschrieben werden.
Die
TcpListener Klasse:
Sie überwacht den TCP Port für eingehende Anfragen und
erzeugt das Socket
Objekt, das die Verbindung zum Client verwaltet. Die
Start-Methode enablet
das Horchen, die Stop-Methode disablet das Abhorchen
des Ports, die
AcceptSocket-Methode nimmt eingehende Verbindungsrequests
entgegen
und erstellt den Socket, der den Request behandelt.
Möchte man Objekte auf Platte abspeichern bedient man sich in .NET der
Serialisierung. Auch zum Transportieren in einen anderen Prozess, beim
Remoting, bedient man sich der Serialisierung.
Über die DeSerialisierung aus der abgespeicherten Bytefolge werden Instanzen
mit dem gleichen Zustand wieder hergestellt.
Wenige Daten
persistent zu speichern ist kein großer Aufwand, auch ohne
Serialisierung. Der Aufwand ist jedoch erheblich wenn eine Objekthierarchie
abspeichert werden soll, dann wenn zirkulare Referenzen auftreten.
Die CLR verwaltet das Layout von Objekten im Speicher anhand der Metadaten.
Dazu stellt sie einen automatischen Serialisierungs-Mechanismus zur Verfügung,
sie bedient sich der Reflection. Bei der Serialisierung werden folgende Daten
abgespeichert: Name der Klasse, das Assembly und alle Daten Member.
Viele Klassen in der Klassenbibliothek lassen sich serialisieren.
Wie lässt
sich z.B. der Inhalt einer ArrayList persistent abspeichern ?
Wie kann ich eigene Klassen serialisierbar machen.
das wird mit Attributen
gesteuert. Was ist dabei zu beachten ?
Wenn Daten berechnet werden, können Attribute nicht verwendet werden.
Dann greife ich zur Custom Serialisierung durch Implementieren des
ISerialize Interfaces. Wie funktioniert das ?
Was ist Remoting ?
Remoting wird für verteilte Anwendungen eingesetzt
und ist ein Ersatz für DCOM.
Ermöglicht die Kommunikation: zwischen
Rechnern, Prozessen und AppDomains
AppDomains sind Miniprozesse innerhalb eines Prozesses. Die CLR dient als
Remoting Infrastruktur. Sie enthält viele Klassen und verkapselt die
Komplexität.
Was sind die Bestandteile beim Remoting ?
Channels, Messages und Formatter.
Channels transportieren Messages
Die Message enthält den Funktionsaufruf oder Return Wert.
Verschickt wird ein Stream über ein Transport Protokoll, folgende Protokolle
werden
unterstützt: HTTP, TCP (Sockets), SMTP und eigene Protokolle.
Formatter kodieren und decodieren Messages in einen Stream.
Soap Formatter:
Die Message wird in XML kodiert und mit Soap Headern
in einen Byte Stream serialisiert.
Binary Formatter: Message in einen
binären Byte Stream serialisieren
Es sind auch eigene Formatter möglich.
Wer sind die
Teilnehmer beim Remoting ?
Üblicherweise besteht eine Remoting Anwendung aus
3 Teilen:
Dem Remote Objekt selbst, üblicherweise verpackt in eine DLL, eine
Server
Anwendung, die das Remote Objekt hostet und ein Client der die
Funktionalität
des Remote Objekts verwenden möchte.
Wie wird das ganze gestartet ?
Das Remote Objekt muss zunächst aktiviert
werden, bevor es verwendet werden
kann, das findet im Host statt. Es gibt 2
Möglichkeiten, die Server- und
Client Aktivierung.
Die Server
Aktivierung wird verwendet, wenn die Anzahl der Clients hoch ist, der
Funktionsaufruf erzeugt jedesmal ein neues Remote Objekt das nach dem Aufruf
wieder entsorgt wird. Das entspricht der statuslosen Programmierung in COM+.
Vorteil: sehr gut skalierbar, Nachteil ein Increment über mehrere Aufrufe
funktioniert
so natürlich nicht.
Ein Statusbehaftetes Verhalten ist bei der Server Aktivierung dennoch
möglich
sofern nur 1 Objekt erzeugt wird, z.B. für ein Singleton. Alle Clients arbeiten
dann mit
dem gleichen Objekt.
Bei der Client Aktivierung setzt der Client
zunächst eine Aktivierungsanforderung
an das Remoting Framework ab. Vorteil: jeder Client kann sein Objekt so
erstellen
wie er möchte, er kann also Konstruktor Parameter
übergeben, außerdem behält das
Objekt zwischen mehreren Funktionsaufrufen
seinen Status. Nachteil, das kostet
Performance und ist nicht so gut
skalierbar.
Das Verhalten entsprecht dem von DCOM, die Lebensdauer wird hier
über einen
Lease Mechanismus geregelt.
Zu allen Aktivierungsmodi erarbeiten wir Beispiele.
Das .NET Framework liefert ein Design Pattern für die asynchrone Programmierung.
Es basiert auf den Delegate Klassen. Sie stellen neben den synchronen Methoden
auch asynchrone Methoden zur Verfügung.
Eigene Klassen können sich der
Musterlösung bedienen und somit auf einfache Art
eine asynchrone
Verarbeitung realisieren.
Asynchroner Support wird von vielen Klassen unterstützt wie IO, Sockets,
Networking,
XML Web Services, Messaging etc.
Die asynchrone Verarbeitung beginnt mit dem Aufruf "BeginInvoke (...)"
Zurückgegeben wird ein "IAsyncResult" - Interface. Danach gibt es mehrere
Möglichkeiten auf die Beendigung des asynchronen Aufrufs zu reagieren.
Der Aufruf von EndInvoke() suspendiert den aufrufenden Thread solange bis
das
Ergebnis vorliegt. Danach wird mit EndOperation() das Ergebnis abgeholt.
Die eleganteste Art an das Ergebnis zu kommen, ist eine Reaktion in einem
Eventhandler. Dazu übergeben wir dem Worker Thread die Adresse eines
Eventhandlers.
Der Client kann also nach Absetzen des asynchronen Aufrufs
sofort weiterarbeiten.
Ist das Ergebnis vorhanden informiert uns der Worker
Thread und wir können das
Ergebnis im Eventhandler abholen.
Die dritte Möglichkeit ist das Pollen, dazu stellt das IAsyncResult Interface ein
Property
"IsCompleted" zur Verfügung.
Wir sprechen noch über Timer
Events und wie nicht Thread-Safe Windows-Forms
Controls behandelt werden müssen.
Zum Schluss erstellen wir einen
asynchronen WebService und steuern ihn an.
Wir haben hier ein neues Konzept und keine Weiterentwicklung von ADO
Es ermöglicht skalierbare Anwendungen für Intranet und Internet.
Es gibt
2 Betriebsarten, den verbundenen Betrieb, realisiert mit einem
clientseitigen Vorwärts-Lese-Cursor, dem DataReader und dem nicht
verbundenen Betrieb mit einer "InMemory" - Datenbank, dem DataSet.
Anhand
von Anwendungs-Szenarien werden wir beide Betriebsarten behandeln.
Innerhalb des Visual Studio ist es ganz einfach auf den SQL-Server zuzugreifen.
Das erspart ein ständiges Wechseln vom Enterprise - Manager zum Visual Studio.
Für unsere Arbeiten erstellen wir uns zunächst eigene Tabellen im Visual Studio.
Der DataReader empfiehlt sich für Datenanzeige in List- und ComboBoxen,
die Daten sollen nur gelesen werden.
Welche Klassen sind im verbundenen
Betrieb erforderlich ?
Welche Provider stehen uns für die
unterschiedlichen Datenbanken zur Verfügung ?
Möchte man neben der
Anzeige von Daten auch die Daten verändern und
rückwärts scrollen,
dann bietet sich der DataSet an.
DataSets enthalten neben Tabellen
auch Constraints und Relationen und
ermöglichen auf diese Weise schon
eine Vorabprüfung bei bestimmten
Veränderungen der Daten innerhalb des
DataSets. Sie ersparen dadurch einen
fehlerhaften Zugriff auf die Datenbank.
DataSets arbeiten völlig
losgelöst von ihrer Datenquelle, d.h. sie
müssen nicht ihre
Daten immer von Datenbanken beziehen. Andere Quellen sind
z.B. XML -
Files und Web-Services.
Wie arbeiten wir mit einem DataSet ohne Datenbank
?
Welche Klassen haben wir zur Verfügung um ein eigenes Schema zu erstellen ?
Der XSD-Designer, ein nützliches Tool. Darin erstellen wir unser Schema.
Wir stellen Daten im DataSet mit Hilfe eines DataGrid dar, die Daten
werden
per Programm hinzugefügt.
Wie schreiben wir DataSets in
XML-Files, welche Schreib-Modi stehen zur Verfügung ?
Nachdem wir gesehen
haben, wie ein DataSet funktioniert, arbeiten wir mit dem
Data-Adapter-Wizard vom Visual Studio und erzeugen mit minimalen Code eine
funktionierende Anwendung.
Der Wizard erzeugt einen Typed DataSet und den
erforderlichen Code für den
DataAdapter. Über den DataAdapter erfolgt der
Query auf die Datenbank.
Mit einem Befehl können wir auch Änderungen im
DataSet wieder in der Datenbank
speichern.
Die dazu nötigen Command - Objekte sind im DataAdapter Objekt
enthalten.
Der DataAdapter ist das Verbindungsglied zwischen Datenbank
und DataSet.
Hierüber erfolgt der Zugriff zum Lesen und zum Update.