Start
Unternehmen
Buch-Katalog
Seminare
Leserservice
Comelio-Blog
Datenbanken
SQL
MS SQL Server

Programmierbarkeit

Funktionen

Verwaltungsarbeiten bei Modulen

Programmierung mit T-SQL

XML in T-SQL zerlegen

Variablen mit XML-Inhalt

XML mit XPath zerlegen

Datentypmethoden für XML-Bearbeitung

XPath

Definition von Webservices

XSLT

Technologien von Webservices

Webservices im MS SQL Server

SOAP-Webservices

Nachrichtenstruktur von Webservices

Batch-Einsatz bei Webservices

Oracle
PHP
UML
C#.NET
XML Schema
XSLT
ERP / PPS / Prozesse
Business Intelligence
Server-Technologien
Software-Technologien
Technologie-Beratung
Individual-Software
Produkte

Comelio GmbH
Rellinghauser Straße 10
D-45128 Essen
Deutschland
Fon: 0201-437517-0
Fax: 0201-437517-10
info@comelio.com

Comelio GmbH
Goethestraße 34
D-13086 Berlin
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Glockengießerwall 17
D-20095 Hamburg
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Mainzer Landstraße 27-31
D-60329 Frankfurt
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Stiglmaierplatz/Dachauer Str. 37
D-80335 München
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Liebknechtstr. 33
D-70565 Stuttgart
Deutschland
info@comelio.com

Comelio GmbH
Nevinghoff 16
D-48147 Münster
Deutschland

Comelio GmbH
Friedrich - List - Platz 1
D-04103 Leipzig
Deutschland

Comelio GmbH
St. Johanner Strasse 41-43
D-66111 Saarbrücken
Deutschland

Comelio GmbH
Kaiser-Wilhem-Ring 27–29
D-50672 Köln
Deutschland

Comelio GmbH
Münsterstraße 248
D-40470 Düsseldorf
Deutschland

Comelio GmbH
Fürther Strasse
D-90429 Nürnberg
Deutschland

Comelio GmbH

Bremen
Deutschland

Comelio-Blog > MS SQL Server > Programmierung mit T-SQL

Programmierung

Nach der ausführlichen Einführung zur Webservice-Technologie, die eine Vielzahl von Unter-Technologien enthält, folgen nun in diesem Kapitel Beispiele zur Erstellung und Verwendung von Webservices, die vom MS SQL Server 2005 bereitgestellt werden.

Kontakt

Anrede* Herr Frau
Vorname*
Nachname*
Firma
E-Mail*
Tel-Nr.
Bereich*
Freitext

Webservice-Programmierung

Nach den vielen verschiedenen allgemeinen Informationen und Darstellungen, wie die Technologie in der Theorie gedacht und im MS SQL Server umgesetzt wird, folgen nun Beispiele, in denen eine Reihe von Webservices auf Basis von Prozeduren und Funktionen eingerichtet werden.

Eine Prozedur als Webservice anbieten

Zunächst benötigt man eine Prozedur, die sich als Webservice eignet. Sie zeichnet sich dabei weniger durch ihre tatsächliche Funktionsweise aus, sondern durch die Art und Weise, wie Eingabe und Ausgabeparameter verarbeitet und erzeugt werden. In diesem ersten Beispiel handelt es sich um eine Prozedur namens GetProductInfo, die auf Basis einer eingegebenen Produktnummer Informationen zu einem Produkt ermittelt. Um den Quelltext nicht unnötig zu verlängern, ist die eigentliche Funktionalität dieser Prozedur (wie auch bei allen anderen Beispielen) sehr kurz gehalten. Theoretisch kann natürlich jeder beliebige T-SQL-Quelltext geschrieben werden, der für die Lösung einer Aufgabenstellung notwendig ist. Als besonderes Merkmal führt diese Prozedur nicht einfach nur eine SQL-Abfrage auf Basis der übergebenen Produktnummer aus, sondern erstellt eine XML-Antwort mit Hilfe der FOR XML-Klausel. Es ist – wie in einem späteren Beispiel gezeigt wird – durchaus möglich, eine relationale Ergebnismenge zurückzuliefern, die dann automatisch in eine XML-Darstellung überführt wird. Ihr Aufbau ist allerdings vorgelegt und wird automatisch auf Basis der Spaltennamen erzeugt. In diesem Fall erstellt man ein eigenes Format.

USE AdventureWorks
GO
CREATE PROCEDURE Production.GetProductInfo (
@ProductNumber nvarchar(25)
) AS BEGIN
SELECT ProductNumber AS "@P-Nr",
Name AS "Name",
StandardCost AS "Prices/Standard",
ListPrice AS "Prices/List",
Size AS "Details/Size",
Color AS "Details/Color"
FROM Production.Product
WHERE ProductNumber = @ProductNumber
ORDER BY ProductID DESC
FOR XML PATH('Product'), ROOT('Product-List')
END

Mit Hilfe der der Anweisung CREATE ENDPOINT lässt sich nun diese Prozedur als Endpunkt und damit auch als Webservice anlagen. Sie soll unmittelbar nach Erstellung auch abrufbar sein, sodass STATE = STARTED angegeben wird. Der Pfad, unter dem man sie aufrufen kann, lautet /sql, die Sicherung gelingt über die integrierte Authentifizierung (ein späteres Beispiel zeigt eine andere Sicherheitstechnik), und über localhost soll der Endpunkt unter dem angegeben Pfad gefunden werden. Die Prozedur wird als SOAP-Webdienst angeboten, gehört zur AW-Datenbank, liefert die WSDL-Datei automatisch zurück (WSDL = DEFAULT) und liegt im Namensraum http://Adventure-Works/Products. Batch-Anweisungen sind unter diesem Webservice nicht zulässig (BATCHES = DISABLED).

CREATE ENDPOINT epProduct
STATE = STARTED
AS HTTP(PATH = '/sql',
AUTHENTICATION = (INTEGRATED ),
PORTS = ( CLEAR ),
SITE = 'localhost')
FOR SOAP (
WEBMETHOD 'http://tempUri.org/'.'GetProductInfo'
(name='AdventureWorks.Production.GetProductInfo',
schema=STANDARD ),
BATCHES = DISABLED,
WSDL = DEFAULT,
DATABASE = 'AdventureWorks',
NAMESPACE = 'http://Adventure-Works/Products')

Während der vorherige Quelltext nur den Endpunkt als solchen einrichtet, benötigt man noch in der master-Datenbank verschiedene weitere Einstellungen, die über Verwaltungsprozeduren für Webservices eingerichtet werden können.

USE master
EXEC sp_grantlogin @loginame='NOTEBOOK\Comelio'
EXEC sp_grantdbaccess @loginame='NOTEBOOK\Comelio'
GRANT CONNECT ON ENDPOINT::epProduct TO [NOTEBOOK\Comelio]

Die Prozedur sp_reserve_http_namespace legt eine explizite Namensraum-Reservierung für einen Endpunkt fest, wobei keine Administrationsrechte auf dem Server notwendig sind, sondern lediglich die Prozedur erlaubt sein muss. Dabei kommt für den Namensraum die Form <scheme>://<hostpart>[:<port>]/ <RelativeURI> zum Einsatz. Für scheme kann man die beiden Werte http oder https verwenden, während hostpart durch einen gegebenen Hostnamen oder einen Platzhalter wie ein Pluszeichen (Reservierung gilt für alle Hostnamen in diesem Schema und den Computer) oder ein Sternchen (Reservierung gilt für die mit dem Pluszeichen angegebenen Hostnamen und das Schema, sofern sie nicht anderweitig durch die gleiche Prozedur, aktive Endpunkte oder Dritt-Anwendungen reserviert sind) ersetzt werden kann. Mit der Prozedur sp_delete_http_namespace_reservation kann eine solche Reservierung wieder gelöscht werden, wobei die gleiche Notation für den Namensraum gilt.

Auch wenn der Quelltext korrekt läuft und alle einzelnen Anweisungen bestätigt werden, ist dies noch keine Gewissheit oder kein Beweis, dass der Webservice verfügbar ist. Dazu lassen sich die beiden URLs http://localhost/sql?wsdl und http://localhost/sql?wsdlsimple verwenden, um die beiden verschiedenen WSDL-Dateien abzurufen.

Es ist in .NET und auch in anderen Sprachen nicht notwendig, tatsächlich eine SOAP-Nachricht zu erstellen, aber die Funktionsweise eines Webdienstes kann über eine SOAP-Nachricht auf einfache Weise überprüft werden. Man kann die Parameter leicht variieren, muss keinen Klienten erstellen, sondern kann einen fertigen Klienten verwenden, der nur SOAP-Anfragen an den Endpunkt sendet, und die Antwort wieder anzeigen. Da im Normalfall typisiertes XML zum Einsatz kommt, kann man leicht aus der XML Schema-Datei eine entsprechende Frage entwickeln, da es ja im Wesentlichen nur auf das versendete XML ankommt. Die Struktur der Nachrichten ist darüber hinaus nicht nur für das Visual Studio lesbar, sondern für den Programmierer ebenfalls, weil die erwarteten Anfragen und Antworten schließlich im WSDL-Dokument verzeichnet sind.

In der Anfrage ruft man im Body-Element den Webservice über den angegebenen Namen auf, der als direktes Kind-Element verwendet wird. Dabei verwendet man den zuvor bei der Anmeldung des Dienstes angegebenen (spielerischen) Namensraum. Der Parametername der Prozedur dient hier wiederum als Vorlage für das Kind-Element, das den Parameterwert enthält.

<SOAP-ENV:Envelope --Namespaces-->
<SOAP-ENV:Body>
<t:GetProductInfo xmlns:t="http://tempUri.org/">
<t:ProductNumber>FR-T67Y-58</t:ProductNumber>
</t:GetProductInfo>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

In der Ergebnismenge gibt es zwei Bereiche, die man in einem Element GetPro-ductInfoResponse/GetProductInfoResult findet, welcher wiederum automatisch aus dem Prozedur-/Webservicenamen gebildet wird. Im ersten Bereich befindet sich das SqlXml-Element, das wiederum innerhalb von einem SqlXml-Element die von der FOR XML-Abfrage erzeugte XML-Nachricht enthält. Das Element SqlRowCount liegt auf der gleichen Ebene wie das zweite SqlXml, welche Meta-Informationen über die Ergebnismenge enthält. In diesem Fall erfährt man nur, dass genau eine Zeile abgerufen wurde. Schließlich folgt noch als Geschwister-Element vom ersten SqlXml-Element das Element SqlResultCode, welches den Fehlerstatus enthält.

<SOAP-ENV:Envelope --Namespaces-->
<SOAP-ENV:Body>
<method:GetProductInfoResponse>
<method:GetProductInfoResult xmlns="">
<sqlresultstream:SqlXml
xsi:type="sqlsoaptypes:SqlXml">
<SqlXml>
<Product-List>
<Product P-Nr="FR-T67Y-58">
<Name>LL Touring Frame - Yellow, 58</Name>
...
</Product>
</Product-List>
</SqlXml>
</sqlresultstream:SqlXml>
<sqlresultstream:SqlRowCount
xsi:type="sqlrowcount:SqlRowCount">
<sqlrowcount:Count>1</sqlrowcount:Count>
</sqlresultstream:SqlRowCount>
<sqlresultstream:SqlResultCode xsi:type="sqlsoaptypes:
SqlResultCode">0</sqlresultstream:SqlResultCode>
</method:GetProductInfoResult>
</method:GetProductInfoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
GetProductInfo-Antwort.xml: Antwort-SOAP-Nachricht

Eine Funktion und eine Prozedur als Webservice anbieten

Ein Webservice besteht in einem realistischen Szenario durchaus nicht nur aus einer einzigen Operation. Wenigstens verschiedene überladene Varianten, d.h. Operationen mit gleicher Funktionalität und unterschiedlichen Parametern, findet man sehr häufig. Bei einem komplexen Dienst folgen dagegen verschiedene Operationen, sodass man sich bei der AdventureWorks-Datenbank solche Operationen wie Produktsuche, Bestellung, Statusanfrage in einem einzigen Webservice denken könnte, die zu unterschiedlichen Zeiten und/oder im Rahmen einer Transaktion in einer bestimmten sachlogischen Reichenfolge aufgerufen werden. Dadurch erhält der Webservice auch insgesamt mehr den Charakter einer Programmierschnittstelle, die einen größeren Verantwortungsbereich verwaltet als nur Informationen auszugeben oder für eine interne Verarbeitung einmalig anzunehmen.

Um zu zeigen, wie man eine Liste von Prozeduren/Funktionen an einem Endpunkt anbietet und wie man eine Funktion anstelle einer Prozedur verwendet, folgt nun die Funktion GetProductsPerSubcategory. Sie liefert die Anzahl der Produkte anhand einer Unterkategorienummer zurück. Unabhängig davon, dass hier eine Funktion verwendet wird, ist der zu erwartende Rückgabewert in diesem Fall auch gerade nicht vom Typ xml, sondern stattdessen vom skalaren Typ int. Man kann sich an dieser Stelle also auch vorstellen, dass man eine ganz gewöhnliche Funktion/Prozedur als Webservice anbietet und den MS SQL Server entscheiden lässt, wie dann die entsprechenden SOAP-Nachrichten aussehen sollen.

CREATE FUNCTION Production.GetProductsPerSubcategory(
@vProductSubcategoryID int
)
RETURNS int
AS BEGIN
DECLARE @vProducts int
SELECT @vProducts = COUNT(*)
FROM Production.Product
WHERE ProductSubcategoryID = @vProductSubcategoryID
RETURN @vProducts
END

In diesem Fall gibt der nachfolgende Quelltext noch einmal an, dass der bestehende Endpunkt zunächst gelöscht werden muss, bevor man Änderungen vornehmen kann. In den anderen Skripten, die teilweise immer wieder die gleichen Endpunkte überschreiben, sind diese Schritte dann ebenfalls auszuführen und befinden sich auch in den dazugehörigen Skripten. Sie werden allerdings nicht jedes Mal erneut abgedruckt. Wenn man gleichzeitig verschiedene Webservices testen möchte, die in diesem Kapitel erstellt werden, dann sollte man am besten die Namen und natürlich die Pfade der Endpunkte ändern.

Als Neuerung ist in diesem Quelltext zu sehen, wie gleichzeitig mehrere Operationen in einem Webservices angeboten werden. Dies geschieht ganz einfach durch eine Auflistung der Prozedur und Funktion, die zuvor erstellt wurden.

-- Endpunkt löschen
DROP ENDPOINT epProduct
GO
-- Namensraum löschen und anlegen
EXEC sp_delete_http_namespace_reservation N'http://localhost:80/sql'
EXEC sp_reserve_http_namespace N'http://localhost:80/sql'
GO
-- Webservice mit Prozedur und Funktion anlegen
CREATE ENDPOINT epProduct
STATE = STARTED
AS HTTP(
PATH = '/sql',
AUTHENTICATION = (INTEGRATED ),
PORTS = ( CLEAR ),
SITE = 'localhost'
)
FOR SOAP (
WEBMETHOD 'http://tempUri.org/'.'GetProductInfo'
(name='AdventureWorks.Production.GetProductInfo',
schema=STANDARD ),
WEBMETHOD 'http://tempUri.org/'.'GetProductsPerSubcategory'
(name='AdventureWorks.
Production.GetProductsPerSubcategory',
schema=STANDARD ),
BATCHES = DISABLED,
WSDL = DEFAULT,
DATABASE = 'AdventureWorks',
NAMESPACE = 'http://Adventure-Works/Products')

Die beiden WSDL-Dokumente, die diesen Webservice beschreiben, befinden sich in den Dateien 532_01.wsdl und 532_01_simple.wsdl. Es interessieren in beiden möglichen WSDL-Dokumenten weniger die Datentypangaben, die ja in jedem WSDL-Dokument gleich sind, sondern vielmehr die Nachrichtenstrukturen. In diesem Fall fällt auf, dass bei der Prozedur eine XML-Datei zurückgegeben wird, deren Struktur allerdings nicht spezifiziert ist, während als Rückgabewert der Funktion nur ein Element zum Einsatz kommt.

Klienten in .NET

Auch wenn es viele verschiedene Möglichkeiten gibt, Klienten für die erstellten Webservices in .NET zu erstellen, soll doch an dieser Stelle ein kurzer Exkurs eingeflochten werden, der zeigt, dass die eingerichteten Dienste tatsächlich im Visual Studio abrufbar sind. Dabei ist in der einfachen Version, bei der ein Webdienst einfach nur einen atomaren Wert und gerade keine XML-Struktur zurückliefert, auf den Assistenten voller Verlass.

  1. Erstellen Sie ein Projekt für eine Konsolenanwendung. Wählen Sie dann PROJEKT/WEBVERWEIS HINZUFÜGEN, um die verfügbaren Webservices abzurufen und die dazugehörige WSDL-Datei dem Projekt hinzuzufügen.
  2. Geben Sie im sich öffnenden Dialogfenster in dem Textfeld URL die Adresse ein, die zum entsprechenden WSDL-Dokument führt, welches den aufzurufenden Webservice beschreibt. Tragen Sie den Namen des Webverweisnamen in das entsprechende Textfeld ein. Kontrollieren Sie, ob die erwarteten Methoden in der Vorschau angezeigt werden. Klicken Sie dann auf VERWEIS HINZUFÜGEN.
  3. Kontrollieren Sie die Referenz im Projektmappen-Explorer unter dem Eintrag WEB REFERENCES.

Die Miniaturanwendung fragt den Benutzer in der Konsole nach der Nummer einer Unterkategorienummer, um diesen Wert der Webserviceoperation GetProductsPerSsubcategory zu übergeben, welche dann die entsprechende Anzahl an Produkten abruft und wieder ausgibt. Neben diesen Aktionen innerhalb der Konsole, welche nicht weiter spektakulär sind, ist es interessant, wie einfach durch das WSDL-Dokument die Operation eines Webservices an einem Proxy (Stellvertreter)-Objekt aufgerufen werden kann. Der Webservice wird unter dem zuvor gespeicherten Namen als Klasse innerhalb des Projekts verfügbar gemacht, sodass ein Objekt seines Typs angelegt werden kann, von dem die verschiedenen Webservice-Operationen als Methoden abrufbar sind.

using System;
using System.Collections.Generic;
using System.Text;
namespace Client1 {
class Program {
static void Main(string[] args){
Console.WriteLine("Geben Sie
eine Produkt-Unterkategorie ein: ");
int productSubcategoryID = Int16.Parse(
Console.ReadLine());
epProductWS.epProduct proxy =
new epProductWS.epProduct();
proxy.Credentials = System.Net.CredentialCache.
DefaultCredentials;
try {
int products = (int) proxy.GetProductsPerSubcategory
(productSubcategoryID);
Console.WriteLine(products);
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
Console.ReadLine();
} } }

Der Klient lässt sich aus dem Ordner EINFACHE WS 5.1\CLIENT1\ CLIENT1\CLIENT1\BIN\DEBUG heraus unter Client1.exe öffnen und liefert bspw. für die Unterkategorienummer 2 die Anzahl von 43 Produkten.

    Comelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen RügenComelio GmbH MS SQL Server: Programmierung mit T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Server MS Microsoft XML SQL T-SQL Bücher Services Analysis Intelligence Webservices .NET Reporting Business Heidelberg Stuttgart Ol Koblenz Frankfurt Zwickau Kiel München Koblenz Würzuburg Magdeburg Berlin Hannover Lübeck Köln Kassel Ingolstadt Wolfsburg Bremen Ludwigshafen Göttingen Freiburg Leipzig Bonn Hamburg Erlangen Mannheim Bochum Andernach Aachen Rügen
Seminare