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.
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.
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();
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.
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.
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.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
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.
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
}
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.
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.
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;
}
}
}
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.
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.
Dienstag, 31. Juli 2012
Juli 2012 / Projektmethoden
In der Schule hatten wir ein Projekt-Modul in dem es hauptsächlich darum ging, Erfahrungen zu sammeln, wie man ein Projekt am besten plant & realisiert. Es ging also nicht wirklich um den Inhalt des Projekts, sondern vielmehr um die Organisation.
Um ein Projekt sinnvoll abwickeln zu können, verwendet man normalerweise eine Projektmethode. Davon gibt es viele verschiedene, welche für unterschiedliche Projekttypen mehr bzw. weniger geeignet sind.
HERMES basiert auf dem sogenannten Phasenmodell. Es ist daher in 6 Phasen unterteilt, damit das Projekt strukturiert abgewickelt werden kann. In jeder dieser Phasen ist klar definiert, welche Punkte erreicht werden müssen, um die nächste Phase beginnen zu können.
Es wird die Ausgangslage analysiert, Ziele und Lösungen des Projekts grob definiert, der Mittelbedarf geklärt, die Planung und Organisation (Projektteam, Rollen usw.) geklärt und die Risiken und Konsequenzen analysiert.
Das Ganze wird dann in ein Dokument zusammengefasst und als Projektantrag dem Auftraggeber abgegeben.
Es wird eine Ist-Aufnahme bzw. Ist-Analyse gemacht, bei der z.B. die Konkurrenz analysiert wird.
Die Systemziele werden klar definiert und es werden Prioritäten gesetzt (Muss-/Kann-Ziele).
Der Fachliche Soll-Zustand wird analysiert und definiert: Entitäten werden wörtlich beschrieben, Hauptaufgaben und Anforderungen bezüglich Informationssicherheit und Datenschutz definiert.
Es werden mehrere Lösungsvarianten erarbeitet und der Entscheid über die Lösungsvarianten begründet.
Ebenfalls wird der Mittelbedarf sowie die Risikofaktoren und die Konsequenzen nochmals überarbeitet.
Falls wie in unserem Fall z.B. eine Webanwendung realisiert werden soll, werden die einzelnen Views und deren Aussehen bereits definiert.
Die Anwendungsfälle werden analysiert und ihre Realisierung klar definiert.
Ebenfalls werden Benutzerschnittstellen, Systemarchitektur Datenmodell definiert und womöglich bereits erste Prototypen verschiedener Komponenten entwickelt.
Auch hier wird alles Dokumentiert, ein Benutzer- und ein Systemhandbuch erstellt.
Hier ist entscheidend, dass das Konzept genug detailliert erstellt wurde, damit man sich nicht allzu viele Gedanken machen muss oder zusätzliche Probleme auftreten, welche man bereits beim erstellen des Konzepts hätte bemerken müssen.
Ebenfalls wird ein Ausbildungsplan für die Mitarbeiter erstellt, ein Akzeptanztest durchgeführt sowie der effektive Mittelbedarf, die Planung und Organisation und Wirtschaftlichkeit überprüft.
Ebenfalls war unser Konzept zu wenig genau wodurch wir unser Projekt nicht ganz wie geplant realisieren konnten.
1. Festlegen der Ziele
2. Beurteilen von Alternativen, Risikoanalyse
3. Entwicklung und Test
4. Planung des Nächsten Zyklus
1. Anforderungsanalyse und -spezifikation
2. Systemdesign und -spezifikation
3. Programmierung und Modultests
4. Integrations- und Systemtest
5. Auslieferung, Einsatz und Wartung
Hier hat man im Gegensatz zu HERMES die Möglichkeit einzelne Phasen zurück zu springen.
Um ein Projekt sinnvoll abwickeln zu können, verwendet man normalerweise eine Projektmethode. Davon gibt es viele verschiedene, welche für unterschiedliche Projekttypen mehr bzw. weniger geeignet sind.
HERMES
In unserem Fall mussten wir mit der Projektmethode HERMES arbeiten. HERMES ist eine Projektmethode zum Abwickeln von Projekten in der Informations- und Kommunikationstechnik, also genau für unser Gebiet. Jedoch wurde sie in der Schweiz entwickelt und wird nur hier eingesetzt, daher ist sie im Ausland unbedeutend.HERMES basiert auf dem sogenannten Phasenmodell. Es ist daher in 6 Phasen unterteilt, damit das Projekt strukturiert abgewickelt werden kann. In jeder dieser Phasen ist klar definiert, welche Punkte erreicht werden müssen, um die nächste Phase beginnen zu können.
Initialisierung
In der Phase Initialisierung wird zuerst der Sachverhalt geklärt.Es wird die Ausgangslage analysiert, Ziele und Lösungen des Projekts grob definiert, der Mittelbedarf geklärt, die Planung und Organisation (Projektteam, Rollen usw.) geklärt und die Risiken und Konsequenzen analysiert.
Das Ganze wird dann in ein Dokument zusammengefasst und als Projektantrag dem Auftraggeber abgegeben.
Voranalyse
In der Phase Voranalyse wird das Projekt an sich genauer analysiert.Es wird eine Ist-Aufnahme bzw. Ist-Analyse gemacht, bei der z.B. die Konkurrenz analysiert wird.
Die Systemziele werden klar definiert und es werden Prioritäten gesetzt (Muss-/Kann-Ziele).
Der Fachliche Soll-Zustand wird analysiert und definiert: Entitäten werden wörtlich beschrieben, Hauptaufgaben und Anforderungen bezüglich Informationssicherheit und Datenschutz definiert.
Es werden mehrere Lösungsvarianten erarbeitet und der Entscheid über die Lösungsvarianten begründet.
Ebenfalls wird der Mittelbedarf sowie die Risikofaktoren und die Konsequenzen nochmals überarbeitet.
Konzept
In der Phase Konzept wird das Projekt klar bis ins kleinste Detail definiert, sollte es zumindest.Falls wie in unserem Fall z.B. eine Webanwendung realisiert werden soll, werden die einzelnen Views und deren Aussehen bereits definiert.
Die Anwendungsfälle werden analysiert und ihre Realisierung klar definiert.
Ebenfalls werden Benutzerschnittstellen, Systemarchitektur Datenmodell definiert und womöglich bereits erste Prototypen verschiedener Komponenten entwickelt.
Realisierung
In dieser Phase ist klar was zu tun ist. Das geplante in die Tat umsetzen und das Projekt realisieren.Auch hier wird alles Dokumentiert, ein Benutzer- und ein Systemhandbuch erstellt.
Hier ist entscheidend, dass das Konzept genug detailliert erstellt wurde, damit man sich nicht allzu viele Gedanken machen muss oder zusätzliche Probleme auftreten, welche man bereits beim erstellen des Konzepts hätte bemerken müssen.
Einführung
In der Phase Einführung wird das realisierte Projekt in den Betrieb genommen bzw. Migriert.Ebenfalls wird ein Ausbildungsplan für die Mitarbeiter erstellt, ein Akzeptanztest durchgeführt sowie der effektive Mittelbedarf, die Planung und Organisation und Wirtschaftlichkeit überprüft.
Abschluss
Im Abschlussbericht werden die Zielerreichungen festgehalten, die Erfahrungen dokumentiert und in unserem Fall die persönlichen Vorsätze für die IPA schriftlich festgehalten.Meine Erfahrung mit HERMES
Es ist zwar eine nützliche Projektmethode, jedoch fehlt mir die Iteration, wie sie in anderen Projektmethoden vorhanden ist. Hat man einmal etwas definiert wird es so gemacht und es gibt keine Möglichkeit das nochmals zu überprüfen und gegebenenfalls abzuändern.Ebenfalls war unser Konzept zu wenig genau wodurch wir unser Projekt nicht ganz wie geplant realisieren konnten.
Weitere Projektmethoden
Es gibt natürlich noch viele andere Projektmethoden und wie mir zu Ohren gekommen ist, sollte man diese besonders für die IPA kennen, da sie gerne von den Experten abgefragt werden.Spiralmodell
Das Spiralmodell ist eine Iterative Projektmethode, was bedeutet, dass die einzelnen Phasen immer wieder durchloffen werden. Die 4 Phasen in diesem Modell sind:1. Festlegen der Ziele
2. Beurteilen von Alternativen, Risikoanalyse
3. Entwicklung und Test
4. Planung des Nächsten Zyklus
Wasserfallmodell / V-Modell
Das Wasserfallmodell ist ähnlich wie HERMES und besteht aus 5 oder 6 Phasen.1. Anforderungsanalyse und -spezifikation
2. Systemdesign und -spezifikation
3. Programmierung und Modultests
4. Integrations- und Systemtest
5. Auslieferung, Einsatz und Wartung
Hier hat man im Gegensatz zu HERMES die Möglichkeit einzelne Phasen zurück zu springen.
Freitag, 29. Juni 2012
Juni 2012 / Objektorientiertes PHP & Datenbankzugriff
In einem Früheren Blogeintrag vom Oktober 2010 habe ich bereits einmal über PHP und den Datenbankzugriff geschrieben.
Hier werde ich das ganze jedoch objektorientiert erläutern.
Wie auch schon damals werde ich ab und zu Bilder verwenden, da der Blog HTML interpretieren will.
Als Datenbank wird die MySQL-Datenbank von XAMPP verwendet. Mit phpMyAdmin hat man auch gleich ein grafisches Userinterface und kann seine Datenbanken und Tabellen sehr einfach erstellen.
Wie die Beziehungen genau funktionieren, habe ich aus Zeitgründen nicht angeschaut, sondern in PHP Methoden geschrieben, in denen die Daten aus den dazugehörigen Tabellen automatisch geholt werden.
Mit PHP kann objektorientiert programmiert werden und seit PHP 5 wird es sogar richtig gut unterstützt.
Um eine Klasse verwenden zu können, muss natürlich zuerst eine erstellt werden.
Erstellen wir einmal eine Klasse namens Person:
Dazu wird eine Datei Namens Person.Class.php erstellt und folgender Inhalt eingefügt:
Class Person{
}
Wie in jeder anderen Objektorientierten Sprache, werden Objekte ebenfalls durch Variablen referenziert. Zuerst muss sie jedoch ins aktuelle PHP-File eingebunden werden:
include("Person.Class.php");
Der Parameter von include muss dabei dem Dateinamen bzw. Dateipfad der einzubindenden .php-Datei darstellen. So funktioniert es, wenn sich die beiden Dateien im selben Ordner befinden.
Ein neues Objekt mit der Klasse Person kann nun ganz einfach erstellt werden.
$person = new Person();
Eine Klasse ohne Inhalt bringt uns aber natürlich nichts. Daher können nun Klassenvariablen, Methoden und einen Konstruktor definiert werden, genau gleich wie z.B. in C#:
Variable:
public $vorname;
public $nachname;
public $alter;
Konstruktor:
public function __construct($vorname, $nachname, $alter)
{
$this->vorhname = $vorname
$this->nachname = $nachname
$this->alter = $alter
}
Methode:
Die Variablen sind wohl selbsterklärend.
Dem Konstruktor müssen nun 3 Werte mitgegeben werden.
Im Konstruktor werden diese dann an die Klassenvariablen $vorname, $nachname und $alter übergeben:
Mit $this rufe ich, wie in C# auch, das Objekt selbst auf, also die aktuelle Instanz der Klasse Person. Dieser "Pfeil" "->" ist mit dem "." in C# zu vergleichen. Damit kann man Methoden bzw. Variablen auf- bzw. abrufen.
Eine neue Instanz der Klasse Person müsste nun also so erstellt werden:
$person = new Person("Till", "Schnegg", 18);
Die Methode ist gibt einfach eine Tabelle mit den Werten des Objekts aus.
das würde dann ca. so aussehen:
<><>
Um diese Methode aber auch sinnvoll nützen zu können, muss man zuerst eine Lösungsarchitektur erarbeiten. Es ist jedoch zu viel Code, um euch hier ein ganzes Beispiel zu zeigen.
Wichtig zu wissen ist jedoch, dass immer die index.php Datei gelesen wird und daher auch dessen Konstruktor als erstes ausgeführt. Dort kann man dann beginnen mit den nötigen Methodenaufrufen, um die Seite generieren zu lassen.
Um auf eine objektorientiert auf eine Datenbank zuzugreifen, muss die mysqli-Klasse verwendet werden:
$db = @new mysqli('localhost', 'root', '', 'personenerfassung');
So greife ich auf die Datenbank "personenerfassung" auf dem localhost zu, wenn ich kein Passwort gesetzt habe.
Um zu überprüfen, ob die Verbindung erfolgreich aufgebaut wurde, kann man folgende Abfrage einfügen:
if(mysqli_connect_errno())
{
die('Fehler beim Aufbauen der Verbindung zur Datenbank: ' . mysqli_connect_error() . ' (' . mysqli_connect_errno() . ')');
}
Konnte die Verbindung hergestellt werden, kann nun eine SQL-Abfrage gemacht werden:
$result = $db->query("SELECT * FROM `person`")
Und anschliessend überprüft, ob das query korrekt ausgeführt wurde, wenn ja kann auf die Daten zugegriffen werden:
if (!$result)
{
echo ("Fehler");
}
else
{
while($row = $result->fetch_assoc())
{
echo($row["name"]);
}
}
Mit der Zeile while($row = $result->fetch_assoc()) wird durch alle aus der Datenbank geholten, in der Variable $result gespeicherten Einträge rotiert.
Das ganze Datenbank-Handling kann natürlich wieder in eine eigene Klasse geschrieben werden, wenn wir es schon objektorientiert machen wollen.
Hier werde ich das ganze jedoch objektorientiert erläutern.
Wie auch schon damals werde ich ab und zu Bilder verwenden, da der Blog HTML interpretieren will.
Datenbank
Als Datenbank wird die MySQL-Datenbank von XAMPP verwendet. Mit phpMyAdmin hat man auch gleich ein grafisches Userinterface und kann seine Datenbanken und Tabellen sehr einfach erstellen.
Wie die Beziehungen genau funktionieren, habe ich aus Zeitgründen nicht angeschaut, sondern in PHP Methoden geschrieben, in denen die Daten aus den dazugehörigen Tabellen automatisch geholt werden.
Objektorientiertes PHP
Mit PHP kann objektorientiert programmiert werden und seit PHP 5 wird es sogar richtig gut unterstützt.
Um eine Klasse verwenden zu können, muss natürlich zuerst eine erstellt werden.
Erstellen wir einmal eine Klasse namens Person:
Dazu wird eine Datei Namens Person.Class.php erstellt und folgender Inhalt eingefügt:
Class Person{
}
Wie in jeder anderen Objektorientierten Sprache, werden Objekte ebenfalls durch Variablen referenziert. Zuerst muss sie jedoch ins aktuelle PHP-File eingebunden werden:
include("Person.Class.php");
Der Parameter von include muss dabei dem Dateinamen bzw. Dateipfad der einzubindenden .php-Datei darstellen. So funktioniert es, wenn sich die beiden Dateien im selben Ordner befinden.
Ein neues Objekt mit der Klasse Person kann nun ganz einfach erstellt werden.
$person = new Person();
Eine Klasse ohne Inhalt bringt uns aber natürlich nichts. Daher können nun Klassenvariablen, Methoden und einen Konstruktor definiert werden, genau gleich wie z.B. in C#:
Variable:
public $vorname;
public $nachname;
public $alter;
Konstruktor:
public function __construct($vorname, $nachname, $alter)
{
$this->vorhname = $vorname
$this->nachname = $nachname
$this->alter = $alter
}
Methode:
Die Variablen sind wohl selbsterklärend.
Dem Konstruktor müssen nun 3 Werte mitgegeben werden.
Im Konstruktor werden diese dann an die Klassenvariablen $vorname, $nachname und $alter übergeben:
Mit $this rufe ich, wie in C# auch, das Objekt selbst auf, also die aktuelle Instanz der Klasse Person. Dieser "Pfeil" "->" ist mit dem "." in C# zu vergleichen. Damit kann man Methoden bzw. Variablen auf- bzw. abrufen.
Eine neue Instanz der Klasse Person müsste nun also so erstellt werden:
$person = new Person("Till", "Schnegg", 18);
Die Methode ist gibt einfach eine Tabelle mit den Werten des Objekts aus.
das würde dann ca. so aussehen:
| Name: | Till Schnegg |
| Alter: | 18 |
Um diese Methode aber auch sinnvoll nützen zu können, muss man zuerst eine Lösungsarchitektur erarbeiten. Es ist jedoch zu viel Code, um euch hier ein ganzes Beispiel zu zeigen.
Wichtig zu wissen ist jedoch, dass immer die index.php Datei gelesen wird und daher auch dessen Konstruktor als erstes ausgeführt. Dort kann man dann beginnen mit den nötigen Methodenaufrufen, um die Seite generieren zu lassen.
Objektorientierter Datenbankzugriff
Um auf eine objektorientiert auf eine Datenbank zuzugreifen, muss die mysqli-Klasse verwendet werden:
$db = @new mysqli('localhost', 'root', '', 'personenerfassung');
So greife ich auf die Datenbank "personenerfassung" auf dem localhost zu, wenn ich kein Passwort gesetzt habe.
Um zu überprüfen, ob die Verbindung erfolgreich aufgebaut wurde, kann man folgende Abfrage einfügen:
if(mysqli_connect_errno())
{
die('Fehler beim Aufbauen der Verbindung zur Datenbank: ' . mysqli_connect_error() . ' (' . mysqli_connect_errno() . ')');
}
Konnte die Verbindung hergestellt werden, kann nun eine SQL-Abfrage gemacht werden:
$result = $db->query("SELECT * FROM `person`")
Und anschliessend überprüft, ob das query korrekt ausgeführt wurde, wenn ja kann auf die Daten zugegriffen werden:
if (!$result)
{
echo ("Fehler");
}
else
{
while($row = $result->fetch_assoc())
{
echo($row["name"]);
}
}
Mit der Zeile while($row = $result->fetch_assoc()) wird durch alle aus der Datenbank geholten, in der Variable $result gespeicherten Einträge rotiert.
Das ganze Datenbank-Handling kann natürlich wieder in eine eigene Klasse geschrieben werden, wenn wir es schon objektorientiert machen wollen.
Mittwoch, 30. Mai 2012
Mai 2012 / Joomla
Nutzen eines CMS
Ein CMS (Content-Management-System) ist eine Software um den Inhalt einer Medienform, meistens einer Website zu gestalten. Im falle einer Website ist der Vorteil eines CMS, dass der Ersteller keinerlei kenntnisse von HTML oder PHP haben muss. Er kann den Inhalt einfach zusammenklicken, zumindest fast.Nachteil für HTML-Kenner
Da ich mich selbst mit HTML, PHP und JS zumindest ein bischen auskenne, ist es nicht ganz einfach ohne Grossen Zeitaufwand den Überblick im Code zu behalten.Möchte man z.B. eine kleine Funktion einbauen, muss man zuerst einmal herausfinden, wie das ganze Datei-System dahinter aufgebaut ist, wie die eigenen Funktionen genutzt werden können usw.
Weitere Funktionen können auch durch vorhandene Plugins integriert werden.
Den Code innerhalb dieser Plugins ist zwar schnell gefunden, vorhandenen Code zu verstehen ist für mich jedoch schwieriger als meine eigene kleine Funktion zu schreiben.
Verwendung von Joomla / Unserer Website
Joomla ist genau ein solches CMS für Websites, welches wir für unsere neue Website verwenden.Es ist ziemlich einfach aufgebaut. Sobald man sich eingeloggt hat findet man eine Funktions-Übersicht mit passenden Icons:
Diese kann man direkt nutzen um die gewünschte Aktion durchzuführen.
Es ist jedoch auch eine Normale Navigation, welche ich bevorzuge vorhanden:
An unserer Website müssen grundsätzlich bloss die 2 Menüs oder der Inhalt geändert werden.
Menüs:
Um die Menüs zu bearbeiten, muss über den Navigationspunkt "Menüs" das zu bearbeitende Menu ausgewählt werden:Das Main Menu ist die Haupt-Navigation unserer Website, das Head Menu befindet sich oben rechts und hat bloss 2 Einträge, wird also selten geändert.
Nach dem Auswählen des Menüs, werden alle Menu-Punkte in einer Art Baum-Struktur dargestellt:
"SPS-Steuerungen" ist ein Untermenu-Punkt, der ebenfalls als Menüalias definiert ist.
"Siemens" und "Saia" sind beides Untermenu-Punkte von "SPS-Steuerungen" und sind als Beiträge -> einzelner Beitrag definiert. Das bedeutet, dass sie einen Beitrag als Inhalt anzeigen.
Alle weiteren Punkte sind wie "SPS-Steuerungen" ein Untermenu-Punkt. Sie sind jedoch als Beiträge definiert.
Um einen neuen Menupunkt einzufügen muss auf den Neu-Button geklickt werden:
Anschliessend muss ein Menutyp gewählt und der Menutitel angegeben werden:
Ebenfalls muss der Übergeordnete Menu-Punkt ausgewählt werden:
So wird definiert, wo sich der Menupunkt befindet, ob es ein Hauptmenu- oder Untermenu-Punkt ist.
Um nun einen Beitrag anzuzeigen, muss als Menutyp "Einzelner Beitrag" ausgewählt werden. Dann erscheint auf der rechten Seite eine Option "Beitrag wählen", wo man den anzuzeigenden Beitrag auswählen kann:
Möchte man jedoch nichts anzeigen, sondern bloss auf einen anderen Menupunkt verweisen, muss man als Menutyp "Menüalias" auswählen. Dann erscheint wieder an derselben stelle die Option Menüeintrag, wo der anzuzeigende Menupunkt ausgewählt werden kann:
Beiträge:
Um die vorhandenen Beiträge zu bearbeiten oder einen neuen zu erstellen, muss über den Navigationspunkt "Inhalt" Beiträge bzw. neuer Beitrag ausgewählt werden:Will man einen vorhandenen Beitrag bearbeiten, muss man diesen bloss aus der angezeigten Liste auswählen:
Will man einen neuen Beitrag erstellen, muss man nur einen Titel und einen Alias vergeben. Bei uns wird der Titel normalerweise Gross und der Alias genau gleich nur klein geschrieben:
Anschliessend kann man den Beitrag bearbeiten bzw. komplett neu erstellen.
Dazu bietet Joomla standardmässig einen Editor namens TinyMCE an. Ich habe jedoch einen etwas benutzerfreundlicheren Editor namens JCE installiert. So sieht die Maske aus:
Das Ganze ist ähnlich aufgebaut wie z.B. im Word. Man kann den Text ausrichten, Schriftart, -grösse & -farbe einstellen, Tabellen erstellen, Bilder einfügen usw.
Für HTML- und CSS-Kenner gibt es natürlich die Code-Ansicht, in welcher der Code für den aktuellen Beitrag angezeigt wird. Die Umschaltung findet man direkt oberhalb der JCE-Maske bei [Toggle Editor]:
Um die Änderungen zu speichern, muss man oben rechts im Bild auf Speichern bzw. auf Speichern & Schliessen klicken:
Abonnieren
Posts (Atom)









