Donnerstag, 28. Februar 2013

Februar 2013 / Doxygen

Doxygen ist ein Tool, geschrieben in C++ zum automatischen Generieren von Code-Dokumentationen.
Das bedeutet, das Tool liest alle Dateien eines Code-Projekts. Es speichert alle Klassen und Methoden sowie die dazugehörigen Kommentare und generiert daraus ein Klassenverzeichnis, welches der Benutzer anschliessend anschauen kann.
Dieses Tool kann verschiedenste Sprachen lesen sowie die Dokumentation in verschiedenen Formaten ausgeben.
Ich werde als Programmiersprache wie meistens C# verwenden, das Ausgabeformat ist HTML.

So wird Doxygen benutzt

Das Tool kann man hier gratis downloaden.
Nach der Installation findet man unter "Alle Programme/doxygen" das Tool "Doxywizard", welches einem beim Erstellen der Konfiguration hilft.
Das GUI ist ziemlich selbsterklärend, jedoch gibt es sehr viele Konfigurationsmöglichkeiten.
Hier das Wichtigste:
Im Tab "Wizard" und dem Knoten "Project" muss das korrekte "Source code directory", also der Projektordner, sowie das "Destination directory", also der Ausgabeordner angegeben werden.
Ausserdem sollte das Häkchen "Scan recursively" gesetzt werden, damit alle Unterordner des ausgewählten Projektordners durchsucht werden.

Dem Projekt den korrekten Namen und die richtige Versionsnummer zu geben ist natürlich auch sinnvoll.
Im Knoten "Mode" empfehle ich noch unter "Select the desired extraction mode:" "All Entities" auszuwählen, damit auch wirklich alles vorhanden ist. Ausserdem kann noch die Optimierung für die genutzte Sprache auswählen.

Im Knoten "Output" kann noch an der Ausgabe-Datei herum geschraubt werden.
Hier kann z.B. das Farbschema geändert, eine Navigation sowie ein Suchfeld hinzufügen werden usw.
Im Tab "Expert" kann alles Mögliche eingestellt werden. Die Sprache der ausgegeben Datei kann man unter "OUTPUT_LANGUAGE" anpassen, was womöglich gewünscht ist.
Im Tab "Run" kann die Generierung der Dokumentation gestartet werden, indem man den Button "Run doxygen" benutzt. Mithilfe des Buttons "Show HTML output" kann die erstellte Dokumentation auch gleich angezeigt werden.

Code Dokumentieren

Wichtig ist, dass der Code richtig dokumentiert ist.
Ohne korrekte Dokumentation des Codes sieht man zwar, welche Klasse welche Methoden hat und welche Parameter gefordert werden, jedoch ist keine Beschreibung vorhanden.
Je nach dem welche Programmiersprache genutzt wird, müssen auch die Kommentare anders gekennzeichnet werden.
Wenn man das Visual Studio nutzt und mit C# programmiert, kann man einfach direkt vor der Klasse bzw. vor der Methode 3 Slashs einfügen, was dann einen Summery-Eintrag erstellt.
Der sieht folgendermassen aus:
Hier kann nun die Klasse bzw. die Methode beschrieben werden.
Das Wichtigste: Wozu wird sie verwendet? / Was kann sie?
 
Ebenfalls sollten die Parameter von den Methoden beschrieben werden. Hat eine Methode einen oder mehrere Parameter, wird für jeden Parameter unter die letzte Summary-Zeile diese Zeile angefügt:
Hier kann nun der entsprechende Parameter beschrieben werden.
Der Name wird vom gegebenen Namen für den Parameter und nicht von der Parameterklasse abgeleitet.
 
Hat man den Code sauber dokumentiert, generiert Doxygen eine sehr saubere Code-Doku. Am Anfang ist es etwas kompliziert, die Übersicht zu wahren, besonders wenn man ein grösseres Projekt dokumentieren liess. Hat man jedoch einmal den Durchblick mit der Navigation, ist es kein Problem mehr, das Gesuchte zu finden.


Donnerstag, 31. Januar 2013

Januar 2013 / Microsoft SQL-Server

Um die Daten einer Applikation irgendwo sinnvoll speichern zu können, verwenden wir Datenbanksysteme. Ein Datenbanksystem besteht aus einer Verwaltungssoftware (Datenbankmanagementsystem) und der effektiven Datenbank, welche die gesamten Daten speichert.
Es gibt verschiedenste Datenbanksysteme, welche man verwenden kann.
Ich habe bis jetzt nur mit den folgenden drei gearbeitet:
  • Microsoft Access
  • Microsoft SQL Server
  • MySQL mithilfe von xampp (phpMyAdmin)
Während Microsoft Access neben den herkömmlichen Möglichkeiten der Datenbankverwaltung auch noch die Möglichkeit bietet, direkt eine Applikation mit einem UI zu entwickeln, bieten die anderen beiden Datenbanksysteme vor allem Verwaltungsmöglichkeiten.
MySQL von Xampp kann mithilfe von phpMyAdmin ohne Probleme verwaltet werden. Dieses Datenbanksystem habe ich bisher nur für Websites und Java-Applikationen genutzt.
Der Microsoft SQL Server kommt mit einem Management Studio, welches ähnlich wie phpMyAdmin die Verwaltung der Datenbanken ermöglicht. Dieses Datenbanksystem nutze ich für meine C# Applikationen am liebsten, egal ob Desktop oder Webanwendung.
Die Möglichkeiten, welche man mit diesem Datenbanksystem hat, sind mir jedoch noch nicht wirklich bekannt. Ich habe es bis jetzt einfach so gebraucht, mich jedoch nie wirklich damit beschäftigt.
Im Zusammenhang mit diesem Post werde ich das nun tun und einige nützliche und wichtige Funktionen aufzeigen.

Microsoft SQL Server Management Studio

Wie bereits erwähnt, wird mit dem Microsoft SQL Server ein passendes Verwaltungstool namens "Microsoft SQL Server Management Studio" mitgeliefert.
Nach der erfolgreichen Installation des SQL-Servers kann das Management Studio gestartet werden. Nach dem Start wird eine Art "Login"-Screen angezeigt.
Hier muss der gewünschte SQL-Server angegeben bzw. ausgewählt werden. Ebenfalls muss man Windows-Authentifizierung, also mit dem eingeloggten Benutzer anmelden, oder SQL Server-Authentifizierung auswählen. Bei der SQL Server-Authentifizierung muss man den Benutzernamen sowie das passende Kennwort eines eingerichteten Benutzers angeben.
Das führt mich auch zum 1. Schwerpunkt, dem Erstellen eines Benutzers und der anschliessenden Vergabe der Rechte.
Nach dem erfolgreichen Verbinden werden standardmässig folgende Menübar sowie der Objekt-Explorer angezeigt:
 
Mithilfe des Objekt-Explorers kann man nun z.B. eine Datenbank erstellen und Tabellen hinzufügen.
Das ist jedoch total simpel und ich werde dieshier nicht aufzeigen.

Anmeldenamen erstellen

Wie bereits erwähnt können, neue Benutzer erstellt werden, mit denen man anschliessend auf den Datenbankserver zugreifen kann.
Wichtig ist dabei, dass die SQL-Authentifizierung auch freigeschaltet ist. Einstellen kann man das mit einem Rechtsklick auf das Server-Objekt und dann unter "Eigenschaften" --> "Sicherheit".
Um den Benutzer zu erstellen, wird ein sogenannter "Anmeldename" erstellt. Dazu muss ein Rechtsklick auf den Ordner "Sicherheit" gemacht werden und anschliessend "Neu" --> "Anmeldung" ausgewählt werden.
 
Nun kann hier die SQL Server-Authentifizierung ausgewählt sowie ein Anmeldename und ein Kennwort angegeben werden.
Wichtig ist es, dem Benutzer die richtigen Serverrollen zuzuweisen, damit er z.B. auch eine DB erstellen kann. Ebenfalls müssen unter Status die Berechtigung zum Herstellen einer Verbindung mit dem Datenbankmodul auf "Erteilt" und der Anmeldename "Aktiviert" sein.
Unter "Benutzerzuordnung" können die Berechtigungen auf die einzelnen bereits vorhandenen Datenbanken geregelt werden. Dabei kann genau angegeben werden, ob der Benutzer überhaupt Zugriff hat, ob er nur lesen, auch schreiben oder sogar Tabellen erstellen/modifizieren kann.
Die restlichen Häkchen und Optionen sind selbsterklärend oder brauche ich aktuell nicht. Anschliessend kann der Vorgang mit einem Klick auf "OK" abgeschlossen werden. Voraussetzung dafür sind natürlich die passenden Berechtigungen.
 

Freitag, 21. Dezember 2012

Dezember 2012 / WPF

WPF steht für Windows Presentation Foundation und ist ein von Microsoft entwickeltes Grafik-Framework. Es ist ein Teil des .NET-Frameworks ab der Version 3.0 und wird mit Windows Vista,
Windows 7 und Windows 8 ausgeliefert. Auch bei Windows XP ab Service Pack 2 kann es nachinstalliert werden.
Es ist eine Alternative zu den herkömmlichen "Windows Forms" und bietet den Vorteil, dass die Präsentationsschicht und die Logik dahinter von Anfang an getrennt werden, da das Design in einer xaml-Datei gespeichert wird, in welcher gar kein C# Code verwendet werden kann.
Somit wird immer automatisch eine zur xaml-Datei gehörende .xaml.cs Datei erstellt, in welche die Logik hinter der Präsentation programmiert werden kann.

XAML

WPF verwendet die sogenannte "Auszeichnungssprache" XAML, welche auf XML basiert und eigens von Microsoft für die Oberflächengestaltung von Anwendungen entwickelt wurde.
Auf ihr basieren abgesehen von WPF auch die Windows Runtime (für die neuen Windows 8-Apps), sowie Silverlight, welches eine Erweiterung für Webbrowser ist und die Ausführung von Rich Internet Applications ermöglicht.
XAML selbst ist jedoch unabhängig von allen 3 Technologien.
Je nach dem welche Technologie wir verwenden, wird XAML auch unterschiedliche Elemente aus den verschiedenen Namespaces verwenden.

WPF mit Visual Studio

Um eine WPF-Applikation zu erstellen, wählt man beim Projekt-Typ anstelle von "Windows Forms-Anwendung" einfach "WPF-Anwendung" aus.
Sobald das Projekt erstellt wurde, fallen auch schon die ersten Unterschiede zu einer normalen "Windows Forms-Anwendung" auf.
Anstelle der "Program.cs"-Klasse, welche jede Windows Forms-Applikation hat und die Main-Methode bereitstellt, welche immer als erstes ausgeführt wird und die 1. Form (die vorhandene Form1) öffnet, befindet sich nun eine App.xaml Datei sowie eine dazugehörige App.xaml.cs Datei.
Ebenfalls ist keine normale Form vorhanden, sondern eine MainWindow.xaml, welche im WPF-Projekt eine "Form" darstellt, sowie die dazugehörige MainWindow.xaml.cs Datei.

In der App.xaml-Datei befindet sich folgender Code:
App.xaml
Hier ist ein neues "Application"-Element definiert, welches als "StartupUri" die Datei MainWindow.xaml" definiert hat. Das ist der Verweis auf das zu öffnende Fenster, nachdem die Applikation gestartet wurde.

Die MainWindow.xaml-Datei enthält folgenden Code:

MainWindow.xaml
Das ist der Standard-Code für eine WPF-Form, bzw. ein WPF-Fenster.

Die Ansicht eines WPF-Fensters im Visual-Studio ist folgendermassen aufgebaut:
Im oberen Bereich sieht man, wie das Fenster aktuell aussieht. Direkt darunter ist der dazugehörige xaml-Code. Möchte man nun etwas ändern, hinzufügen oder löschen, kann man dies entweder direkt im Code machen oder mithilfe der Toolbox bzw. des Designers. Man hat ebenfalls ein Eigenschaften-Fenster, in welchem die Eigenschaften der einzelnen Steuerobjekte angepasst werden kkönnen.
Das Designen an sich ist sehr ähnlich wie in den herkömmlichen Windows Forms-Applikationen. Den Vorteil sehe ich jedoch darin, dass man direkt auch den Design-Code sieht und sehr einfach anpassen kann. Da es ähnlich aufgebaut ist wie die CSS-Eigenschaften, welche man direkt in den HTML-Code schreiben kann, hatte ich keine Probleme, den Aufbau zu verstehen.
Ein grosser Unterschied ist, dass die Ereignisse nun immer in der dazugehörigen .xaml.cs Datei verarbeitet werden und somit von der Präsentationsschicht getrennt sind.

Freitag, 30. November 2012

November 2012 / LINQ to SQL

In meinem letzten Post habe ich LINQ-Abfragen & die Lambda-Ausdrücke beschrieben. Um hier weiterzufahren werde ich nun LINQ to SQL beschreiben, also die Möglichkeit auf einen SQL-Server(in meinem Fall Microsoft SQL Server 2008 R2) zuzugreifen.

Vorbereitung

Um die Funktionen zeigen zu können erstelle ich zunächst eine neue Datenbank namens "TestDB" mithilfe des Management Studios. In dieser Datenbank erstelle ich 2 Tabellen Namens "Projekt" und "Kunde", die eine Beziehung haben.
Hier ein ERM davon:

LINQ to SQL-Klasse

Die LINQ to SQL-Klasse arbeitet als OR-Mapper, was bedeutet, dass wir mit ihr komplett objektorientiert auf eine eigentlich Relationale Datenbank zugreifen können.Sie generiert uns Klassen direkt aus unseren Tabellen und stellt eine Datenquelle zur Verfügung. Um nun auf die vorhandene Datenbank zugreifen zu können, verwende ich die vorhandene LINQ to SQL-Klasse. Rechtsklick auf das Projekt, "Hinzufügen", "neues Element", "LINQ to SQL-Klassen" auswählen, sinnvollen Namen geben und hinzufügen:
Nun Befindet sich im Projekt eine neue .dmbl Datei:
Wenn Sie geöffnet wird hat man zuerst diese Ansicht:
 
Nun müssen Sie die Datenbank auswählen, welche verwendet werde soll.
Klicken Sie dazu auf "Server-Explorer" bzw. öffnen Sie diesen und wählen Sie "Mit Datenbank verbinden":
Wählen Sie nun "Microsoft SQL Server" aus, da wir ja auch solch eine Datenbank zugreifen werden:
Geben Sie den SQL-Server Namen an und wählen Sie die gewünschte Datenbank aus:
Wählen Sie im Server-Explorer nun die gewünschten Tabellen, in meinem Fall "Projekt" und "Kunde" auf die linke Seite der weissen fläche, welche angezeigt wird.
 
Nun ist alles vorbereitet und man kann LINQ benutzen um über die erstellte Verbindung Daten zu holen, zu manipulieren und zu löschen. 

Arbeiten mit dem DataContext

Die LINQ to SQL-Klasse hat nun eine DataContext-Klasse erstellt mit der wir objektorientiert auf die Datenbank zugreifen können. Der Name der Klasse besteht aus dem Namen der LINQ to SQL-Klasse gefolgt von DataContext. In meinem Fall heisst er DatenbankDataContext und so wird er erstellt:

DatenbankDataContext dbContext = new DatenbankDataContext();

Nun können wir mit LINQ oder den Methodenaufrufen, welche ich bevorzuge, auf diesen DataContext zugreifen. Möchte ich z.B. alle Kunden in eine Liste abrufen geht das folgendermassen:
dbContext.Kunde.ToList();

Zuerst sollte man jedoch etwas in die Datenbank einfügen. Auch das geht über den DataContext. Möchte ich einen neuen Kunden erstellen gehe ich folgendermassen vor:

using(DatenbankDataContext dbContext = new DatenbankDataContext)
{
    //Wir erstellen einen neuen Kunden
    Kunde obj = new Kunde();
    //Wir setzen die "Kunde_Name" Eigenschaft
    obj.Kunde_Name = "TestKunde";
    //Fügt das Kunde-Objekt bei einem Aufruf von SubmitChanges in die DB ein
    dbContext.Kunde.InsertOnSubmit(obj);
    //Speichert die Änderungen, also den neuen Datensatz
    dbContext.SubmitChanges();
}

Um einen vorhandenen Datensatz zu editieren muss dieser zuerst ausgelesen werden. Wenn ich nun z.B. die ID des Kunden weiss kann ich das folgendermassen tun:

using(DatenbankDataContext dbContext = new DatenbankDataContext)
{
     //Wir holen den Kunden mit der ID 1
     Kunde obj = dbContext.Kunde.Single(k => k.Kunde_ID == 1);
     //Wir Ändern die "Kunde_Name" Eigenschaft auf "indico"
     obj.Kunde_Name = "indico";
     //Speichert die Änderungen
     dbContext.SubmitChanges();
}

Möchte ich einen vorhandenen Datensatz löschen muss ich auch diesen zuerst auslesen. Wenn ich ebenfalls die ID des Kunden kenne kann ich das folgendermassen tun:

using(DatenbankDataContext dbContext = new DatenbankDataContext)
{
     //Wir holen den Kunden mit der ID 1
     Kunde obj = dbContext.Kunde.Single(k => k.Kunde_ID == 1);
     //Löscht das Kunde-Objekt bei einem Aufruf von SubmitChanges aus der DB
     dbContext.Kunde.DeleteOnSubmit(obj);
     //Speichert die Änderungen
     dbContext.SubmitChanges();
}

Nun kann man eigentlich alles machen was man kann.

Auch mit Beziehungen funktioniert das ganze sehr einfach. Erstelle ich ein neues Projekt, welches unbedingt einem Kunden zugewiesen werden muss, bevor es gespeichert werden kann, kann ich der Eigenschaft "Kunde" entweder einen vorhandenen Kunden zuweisen oder sogar einen neuen Erstellen und dann zuweisen. LINQ to SQL erkennt so den neuen Kunden und speichert den bei SubmitChanges() auch gleich in die Datenbank.

Mittwoch, 31. Oktober 2012

Oktober 2012 / LINQ-Abfragen & Lambda-Ausdrücke

Language-Integrated Query (LINQ), was auf Deutsch Sprachintegrierte Abfrage bedeutet, wurden dazu geschaffen, dem Benutzer unabhängig von seiner Datenquelle eine einheitliche Objektorientierte Abfragemöglichkeit zu bieten.
Anstatt für die relationalen Datenbanken SQL oder für XML XQuery zu erlernen, kann ein Entwickler mit LINQ einheitlich auf die meisten Datenquellen zuzugreifen. Das funktioniert auch mit Listen oder ADO.NET-Datasets.

LINQ-Abfrage

Die LINQ-Abfrage besteht aus 3 Teilen.
1. Die Datenquelle muss bekannt sein/gemacht werden.
2. Die Abfrage wird erstellt.
3. Die Abfrage wird ausgeführt.
Hier ein kleines Beispiel mit C#:
Zuerst wird eine neue Klasse "Person" definiert mit welcher wir arbeiten werden:

class Person
{
    public Person(string Vorname, string Nachname, string Wohnort)
    {
        this.Vorname = Vorname;
        this.Nachname = Nachname;
        this.Wohnort = Wohnort;
    }
    public string Vorname
    {
        set;
        get;
    }
    public string Nachname
    {
        set;
        get;
    }
    public string Wohnort
    {
        set;
        get;
    }
}

Nun wird wie bereits erwähnt eine Datenquelle definiert.
Wir erstellen hier eine List mit der Klasse Person als Objekte und fügen ihr gleich einige Personen hinzu:

List datenquelle = new List();
datenquelle.Add(new Person("Till", "Schnegg", "Wabern"));
datenquelle.Add(new Person("Max", "Muster", "Zürich"));
datenquelle.Add(new Person("Lorenz", "Müller", "Zürich"));
datenquelle.Add(new Person("C", "Sharp", "Bern"));

Dann wird die Abfrage definiert. Sie besteht aus mindestens 2 Teilen.
Der 1. Teil ist die from-Klausel, welche die Datenquelle angibt.
Der 2. Teil ist die select-Klausel, welche den Typ der zurückgegebenen Elemente angibt.
Ich werde hier gleich noch 2 weitere Teile zeigen.
Die where-Klausel welche zum filtern der Daten verwendet werden kann und die orderby-Klausel mit welcher man die Daten sortieren kann.
Die Abfrage muss noch in eine Variable gespeichert werden, was dann so aussieht:

var abfrage = from person in datenquelle
                      where person.Wohnort == "Zürich"
                      orderby person.Vorname
                      select person;

Hier werden nun alle "Person"-Objekte in der Liste datenquelle, welche als Wohnort "Zürich" eingetragen haben herausgefiltert und nach ihren Vornamen sortiert.
In der where-Klausel können auch die AND(&&) und OR(||) Operatoren verwendet werden um mehrere Bedingungen zu prüfen.

Im Moment enthält die Variable abfrage nur die Abfrage selbst und noch keine Daten. Erst wenn die Abfrage ausgeführt wird, werden die Daten effektiv ausgelesen.
Dazu wird z.B. eine foreach-Schlaufe verwendet:

foreach (Person person in abfrage)
{
    MessageBox.Show(person.Vorname + " " + person.Nachname + "
    Wohnhaft in: " + person.Wohnort);
}

Mit dieser Art werden jedes Mal, wenn auf die variable abfrage zugegriffen wird, die Daten neu aus der Datenquelle gelesen.
Möchte ich das jedoch nur 1-mal tun und die Daten anschliessend nicht neu Laden, kann ich das mit der Methode ToList() erreichen. Dazu wird die ganze Abfrage in eine Klammer gepackt und am Ende die Methode aufgerufen:

List abfrage = (from person in datenquelle
                        where person.Wohnort == "Zürich"
                        orderby person.Vorname
                        select person)
                        .ToList();

 LINQ Methodensyntax (Lambda-Ausdrücke)

In der .NET Common Language Runtime (CLR) kann diese Abfragesyntax nicht direkt verwendet werden. Deshalb werden sie beim Kompilieren in Methodenaufrufe übersetzt, welche die CLR verwenden kann.
Der Entwickler selbst kann diese Methodenaufrufe jedoch auch selbst von Anfang an verwenden, falls er das möchte. In einzelnen Fällen ist es meines Wissens sogar notwendig.
Sie ist nicht ganz so simpel wie die normale Abfragesyntax und ist daher nicht ganz so einfach lesbar.
Die oben gezeigte Abfrage wird z.B. mit Methodenaufrufen so dargestellt:

var abfrage = datenquelle
                     .Where(person => person.Wohnort == "Zürich")
                     .OrderBy(person => person.Vorname);

Diese Abfrage besteht schon aus weniger Teilen, bewirkt jedoch genau das gleiche.
Diese Ausdrücke, welche die Parameter für die Methoden Where() und OrderBy() bereitstellen, nennen sich Lambda-Ausdrücke. Wie genau man diese Ausdrücke ohne die Lambda-Syntax programmieren müsste, weiss ich nicht. Die Lambda-Syntax selbst, ist jedoch leicht zu verstehen und erklärt sich fast von selbst.
Auf der linken Seite kann eine beliebige Zeichenfolge eingegeben werden. Anschliessend muss der Lambda-Operator => verwendet werden. Mit der links eingegebenen Zeichenfolge, kann nun auf die Klasse, welche in der Datenquelle verwendet wird zugegriffen werden. So kann nun in der Where() Methode eine Bedingung definiert oder in der OrderBy() Methode eine Eigenschaft angegeben werden.

Möchte ich hier genau gleich wie oben die Daten direkt holen und nicht jedes Mal neu abfragen, kann ich das auch mit der .ToList() Methode machen. Da ich hier jedoch bereits mit der normalen Objektorientierten Syntax programmiere brauche ich keine Klammern:

var abfrage = datenquelle
                      .Where(person => person.Wohnort == "Zürich")
                      .OrderBy(person => person.Vorname)
                      .ToList();

Es gibt weitere solche Methoden, welche direkt Daten auslesen.
Mit First() kann ich z.B. direkt das erste Objekt aus der Liste nehmen. Würde ich z.B. mit einer Datenbank arbeiten und mit der ID eines Objekts suchen, so wäre klar dass ich nur 1 Objekt zurückbekommen kann.

LINQ Datentransformationen

Mit LINQ kann man neue Typen bzw. Klassen erstellen. Dies kann direkt mit der select-Klausel erstellt werden. Dadurch ist sie auch nicht so nutzlos wie sie einem zuerst erschien.
Angenommen ich möchte jetzt nicht alle Personen welche in Zürich leben sondern bloss die Nachnamen.
Um das zu erreichen muss ich anstelle von "select person" einfach "select person.Nachname" angeben.
Dadurch bekomme ich eine List zurück, welche Strings enthält.

Noch interessanter wird es, wenn ich verschiedene Datenquellen habe, welche verschiedene Objekte speichern.
Habe ich z.B. zusätzlich zu meiner Personenliste noch eine Liste in der ich Tiere speichere.
Die Eigenschaften dieser Klasse definiere ich jetzt einfach mal so:
- Name
- Tierart
- Wohnort
Möchte ich nun die Namen aller Tiere und Menschen auslesen, welche in Zürich wohnen, kann ich das ganz einfach mit der Methode Concat() machen:

var abfrage = (from person in datenquelle
                       where person.Wohnort == "Zürich"
                       select person.Vorname)
                       .Concat
                       (from tier in datenquelle2
                       where tier.Wohnort == "Zürich"
                       select tier.Name);

Diese Abfrage wird ebenfalls eine List mit Strings zurückgeben.

Möchte ich nun z.B. nicht nur den Namen wissen, sondern ebenfalls welche Tierart das Tier hat und auch die Menschen erkennen will so kann ich nicht nur vorhandene Klassen verwenden sondern auch neue kreieren:

var abfrage = (from person in datenquelle
                       where person.Wohnort == "Zürich"
                       select new { Art = "Mensch", Name = person.Vorname + " " + person.Nachname })
                       .Concat
                       (from tier in datenquelle2
                       where tier.Wohnort == "Zürich"
                       select new { Art = tier.Tierart, Name = tier.Name });

Nun wird in dieser Liste eine Klasse ohne Namen aber mit den Eigenschaften Art und Name zurückgegeben.
Die Art ist beim Menschen dabei immer "Mensch" und beim Tier die gespeicherte Tierart.
Der Name setzt sich beim Menschen aus Vor- und Nachname zusammen und beim Tier wird einfach die Eigenschaft Name übernommen.

Mittwoch, 26. September 2012

September 2012 / TCP-Kommunikation

Letzten Monat habe ich beschrieben, wie 2 Programme auf demselben Computer per Interprozesskommunikation kommunizieren können.
Nun beschreibe ich, wie 2 Programme auf 2 verschiedenen Computern kommunizieren können.
Theoretisch könnten so auch 2 Programme auf demselben Computer kommunizieren, jedoch macht das nicht viel sinn, da andere Möglichkeiten für diesen Anwendungsfall bestehen.

Netzwerk-Kommunikation: TCP-Protokoll

Auch bei dieser Kommunikationsart braucht es eine Server-Applikation, welche die Daten empfängt und eine Client-Applikation, welche die Daten sendet.

Server-Applikation

Um auch hier einen übersichtlicheren und besser wiederverwendbaren Code zu programmieren, habe ich eine Klasse erstellt und sie "Listener" genannt.
Die Klasse hat 3 Klassenvariablen:

public TcpListener listener;
public delegate void dMessage(object sender, MessageEventArgs e);
public event dMessage Message;

Die Klasse TcpListener stellt den eigentlichen TCP-Listener bereit.
Das Delegate und der Event sind dazu da, um ein Event auszulösen, wenn eine Nachricht empfangen wurde.
Die Klasse MessageEventArgs, welche als Parameter verwendet wird ist identisch mit jener im Interprozesskommunikations-Beispiel und enthält lediglich einen public string der die gesendeten Daten übergeben soll:

public class MessageEventArgs
{
     public MessageEventArgs(string message)
     {
         Message = message;
     }
     public string Message
     {
         set;
         get;
     }
}

Im Konstruktor der Listener-Klasse wird ein neuer Tcp-Listener instanziiert, jedoch wird er noch nicht verwendet.
Der TcpListener muss auf einen Port hören können. Daher wird ihm dieser als int übergeben.

public Listener(int port)
{
    listener = new TcpListener(IPAddress.Parse("127.0.0.1"), port);
}

In einer eigenen Methode kann der TcpListener nun gestartet werden, damit er Daten empfangen kann.
Der Grund, weshalb der Server nicht direkt im Konstruktor gestartet wird ist, dass dieser somit in einen eigenen Thread ausgelagert werden kann und der Event im Objekt registriert werden kann, in dem es erstellt wurde.
Diese Start-Methode startet den Server bloss einmal, hört also bloss auf einen Client. Wenn dieser die Verbindung schliesst wird der Server beendet. Mit einer While-Schlaufe kann das ganze natürlich angepasst werden.

public void Start()
{
    //Erstellt einen TCPListener, der auf den Port 4711 hört
    //Startet den Listener
    listener.Start();
    //Wartet bis ein Client eine Verbindung aufbaut
    TcpClient c = listener.AcceptTcpClient();
    NetworkStream clientStream = c.GetStream();
    byte[] message = new byte[4096];
    int bytesRead;
    while (true)
    {
        bytesRead = 0;
        try
        {
            //Wartet bis der Client Daten sendet
            bytesRead = clientStream.Read(message, 0, 4096);
        }
        catch
        {
            //Fehler ist aufgetreten
            break;
        }
        if (bytesRead == 0)
        {
             //Der Client hat die Verbindung geschlossen
            break;
        }
        //Nachricht wurde empfangen
        ASCIIEncoding encoder = new ASCIIEncoding();
        //Löst das Message-Event mit dem gesendeten Inhalt als Message aus
        Message(this, new MessageEventArgs(encoder.GetString(message, 0, bytesRead)));
    }
    //Verwirft den Client & Schliesst den Listener
    c.Close();
    listener.Stop();
}

Den Server kann man nun aus einer anderen Klasse z.B. so verwenden:

//Erstellt einen Listener der auf das Port 11993 hört
Listener tcpListener = new Listener(11993);
//Fängt das Message-Event des Listeners ab
tcpListener.Message += new Listener.dMessage(tcpListener_Message);
//Startet den Listener in einem neuen Thread
listener = new Thread(tcpListener.Start);
listener.Start();

Natürlich wird noch die Methode tcpListener_Message gebraucht, mit welcher wir in diesem Fall den Message-Event abarbeiten.

void tcpListener_Message(object sender, MessageEventArgs e)
{
    //Nachricht verarbeiten
}

Client-Applikation

Bei der Client Applikation muss man eigentlich nur 2 Sachen beachten.
1. Dass die richtige IP-Adresse als Ziel verwendet wird und 2. dass der selbe Port verwendet wird.
Auch hier habe ich wieder eine eigene Klasse Namens "Sender" erstellt.
Sie hat 1 Klassenvariablen:

private IPEndPoint serverEndPoint;

Die Klasse IPEndPoint stellt die Verbindungsdaten zum Server bereit und müssen daher nur 1 mal gesetzt werden.

Im Konstruktor der Sender-Klasse wird ein neuer IPEndPoint instanziiert.
Als Parameter werden die IP-Adresse des Servers und das passende Port übergeben.

public Sender(IPAddress ipAddress, int port)
{
    serverEndPoint = new IPEndPoint(ipAddress, port);
}

In einer eigenen Methode können nun Nachrichten an den Server gesendet werden.
Als Parameter wird nur die Nachricht, welche gesendet werden soll übergeben.

public void Send(string message)
{
    //Erstellt einen neuen TcpClient, der die Verbindung aufbauen
    //und die Daten senden wird
    TcpClient client = new TcpClient();
    try
    {
        // Verbindung zum Server wird aufgebaut
        client.Connect(serverEndPoint);
        NetworkStream clientStream = client.GetStream();
        try
        {
            //Codiert den String um, damit dieser gesendet werden kann
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer = encoder.GetBytes(message);
            //Schreibt die Nachricht in den Stream
            clientStream.Write(buffer, 0, buffer.Length);
            //Schickt die Nachricht
            clientStream.Flush();
        }
        catch
        {
            //Fehler ist aufgetreten
            MessageBox.Show("Nachricht konnte nicht gesendet werden");
        }
        //Schliesst die Verbindung zum Server
        client.Close();
    }
    catch
    {
        //Verbindung konnte nicht aufgebaut werden
        MessageBox.Show("Es konnte keine Verbindung Server hergestellt werden!");
    }
}

Den Sender kann man nun so verwenden:

//Erstellt einen Sender der auf die Loopback-IP, also die
//IP des eigenen Computers und das Port 11993 sendet
Sender sender = new Sender(IPAddress.Parse("127.0.0.1", 11993);
//Sendet die Nachricht "Test";
sender.send("Test");

So kann immer wieder die Methode send aufgerufen werde um Daten zu senden.

Freitag, 31. August 2012

August 2012 / Interprozesskommunikation

Damit mehrere Programme miteinander kommunizieren können, stellt das .NET Framework verschiedene Möglichkeiten bereit.
Programme, welche auf demselben Computer laufen, können die sogenannte "Interprozesskommunikation" nutzen um Daten auszutauschen.
Laufen die Programme jedoch auf verschiedenen Computern, muss ein Netzwerkprotokoll genutzt werden, um Daten auszutauschen.
Dieses Mal werde ich die Interprozesskommunikation mithilfe von Pipes beschreiben, beim nächsten Mal die über das Netzwerkkommunikation mithilfe des TCP-Protokolls.

Interprozesskommunikation: Pipes

Mithilfe von benannten Pipes, kann man zwischen 2 Programmen eine Kommunikation aufbauen. Dazu braucht es eine Server-Applikation, welche die Daten empfängt und eine Client-Applikation, welche die Daten sendet.

Server-Applikation

Um einen übersichtlicheren und besser wiederverwendbaren Code zu programmieren, habe ich eine Klasse namens PipeServer erstellt.
Die Klasse hat 3 Klassenvariablen:

private NamedPipeServerStream serverstream;
public delegate void dMessage(object sender, MessageEventArgs e);
public event dMessage Message;

Die Klasse NamedPipeServerStream stellt den eigentlichen Pipe-Server bereit.
Das Delegate und der Event sind dazu da, um ein Event auszulösen, wenn eine Nachricht empfangen wurde.
Die Klasse MessageEventArgs, welche als Parameter verwendet wird habe ich ebenfalls selbst erstellt und enthält lediglich einen public string der die gesendeten Daten übergeben soll:

public class MessageEventArgs
{
    public MessageEventArgs(string message)
    {
        Message = message;
    }
    public string Message
    {
        set;
        get;
    }
}

Im Konstruktor der PipeServer-Klasse wird ein neuer NamedPipeServerStream instanziiert, jedoch wird er noch nicht verwendet.
Da dieser PipeServer ja über einen Namen läuft, wird dieser beim Konstruktor angegeben:

public PipeServer(string name)
{
    serverStream = new NamedPipeServerStream(name);
}

In einer eigenen Methode kann der PipeServer nun gestartet werden, damit er Daten empfangen kann.
Der Grund, weshalb der Server nicht direkt im Konstruktor gestartet wird ist, dass dieser somit in einen eigenen Thread ausgelagert werden kann und der Event im Objekt registriert werden kann, in dem es erstellt wurde.
Diese Start-Methode startet den Server bloss einmal, hört also bloss auf einen Client. Wenn dieser die Verbindung schliesst wird der Server beendet. Mit einer While-Schlaufe kann das ganze natürlich angepasst werden.

public void Start()
{
    //Startet den Server worauf er auf eine Verbindung durch den Client wartet.
    serverStream.WaitForConnection();

    //Sobald sich ein Programm verbunden hat, wird ein StreamReader für den PipeServer instanziiert
    StreamReader streamReader = new StreamReader(serverStream);

    //Schleife wird durchloffen, bis die Verbindung vom Client getrennt wurde
    while(true)
    {
        //Liest gesendete Daten bei jedem Flush vom Client
        var data = streamReader.ReadLine();

        //Überprüft ob die Verbindung offen ist und Daten gesendet wurden
        if (data != null)
        {
            //Wenn ja wird der Message-Event ausgelöst und die erhaltenen Daten übergeben
            Message(this, new MessageEventArgs(data));
        }
        else
        {
            //Wenn nein wird der Server geschlossen und die Schlaufe verlassen
            serverStream.Close();
            break;
        }
    }
}

Client-Applikation

Die Client-Applikation ist sehr simpel, jedoch muss man wissen, wie das Flush-System des StreamWriters funktioniert.
Zuerst muss ein NamedPipeClientStream aufgebaut werden. Wichtig ist dabei dass er denselben Namen hat wie der NamedPipeServerStream der Server-Applikation. Ansonsten  können Sie keine Verbindung aufbauen:

NamedPipeClientStream clientStream = new NamedPipeClientStream(name);

Anschliessend muss wird versucht die Verbindung zum Server aufzubauen:

clientStream.Connect();

Nun muss ein StreamWriter erstellt werden.

StreamWriter streamWriter = new StreamWriter(clientStream);

Wichtig ist nun ob man mit AutoFlush arbeiten will, oder nicht, der AutoFlush-Wert ist standardmässig auf false gesetzt.
Ohne AutoFlush wird nichts gesendet, bis man die Methode Flush des StreamWriters aufruft.
Mit AutoFlush wird jede abgeschlossene Zeile gesendet:

streamWriter.AutoFlush = true;

Nun können die Daten mit der Write bzw. der WriteLine Methode des StreamWriters gesendet werden:

streamWriter.Write("Das ist");
streamWriter.WriteLine(" ein Test");
streamWriter.Flush(); //Nur nötig wenn AutoFlush deaktiviert ist

Der Server würde nun "Das ist ein Test" erhalten.