Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 26 ADO.NET – Daten im lokalen Speicher
Pfeil 26.1 »DataSet«-Objekte verwenden
Pfeil 26.1.1 »DataSet«-Objekte erzeugen
Pfeil 26.1.2 Anatomie einer »DataTable«
Pfeil 26.1.3 Zugriff auf eine Tabelle im »DataSet«
Pfeil 26.1.4 Zugriff auf die Ergebnisliste
Pfeil 26.1.5 Dateninformationen in eine XML-Datei schreiben
Pfeil 26.2 Gültigkeitsprüfung im »DataSet«
Pfeil 26.2.1 Dem »DataSet« Schema-Informationen übergeben
Pfeil 26.2.2 Eigenschaften einer »DataColumn«, die zur Gültigkeitsprüfung dienen
Pfeil 26.2.3 Die »Constraints«-Klassen einer »DataTable«
Pfeil 26.2.4 Schema mit Programmcode erzeugen
Pfeil 26.2.5 Schema-Informationen mit »SqlDataAdapter« abrufen
Pfeil 26.3 Änderungen in einer DataTable vornehmen
Pfeil 26.3.1 Editieren einer DataRow
Pfeil 26.3.2 Datenzeile löschen
Pfeil 26.3.3 Neue Datenzeile hinzufügen
Pfeil 26.3.4 Der Sonderfall: Autoinkrementspalten
Pfeil 26.3.5 Was passiert bei der Änderung einer Datenzeile?
Pfeil 26.3.6 Manuelles Steuern der Eigenschaft »DataRowState«
Pfeil 26.4 Mit mehreren Tabellen arbeiten
Pfeil 26.4.1 Der Weg über JOIN-Abfragen
Pfeil 26.4.2 Mehrere Tabellen in einem »DataSet«
Pfeil 26.4.3 Eine »DataRelation« erzeugen
Pfeil 26.4.4 »DataRelation« und Einschränkungen
Pfeil 26.4.5 In Beziehung stehende Daten suchen
Pfeil 26.4.6 Ergänzung zum Speichern von Schema-Informationen in einer XML-Schema-Datei
Pfeil 26.5 Filtern und Suchen in einer DataTable
Pfeil 26.5.1 Die Methode »Find«
Pfeil 26.5.2 Die Methode »Select«
Pfeil 26.6 Objekte vom Typ »DataView«
Pfeil 26.6.1 »DataView« erzeugen
Pfeil 26.6.2 Auf die Datenzeilen in einer »DataView« zugreifen
Pfeil 26.6.3 Die Eigenschaft »Sort« und die Methode »Find«
Pfeil 26.6.4 Die Methode »FindRows«
Pfeil 26.6.5 Die Eigenschaft »RowFilter«
Pfeil 26.6.6 Die Eigenschaft »RowStateFilter«
Pfeil 26.6.7 Änderungen an einem »DataView«-Objekt
Pfeil 26.6.8 Aus einer »DataView« eine »DataTable« erzeugen


Galileo Computing - Zum Seitenanfang

26.4 Mit mehreren Tabellen arbeiten Zur nächsten ÜberschriftZur vorigen Überschrift


Galileo Computing - Zum Seitenanfang

26.4.1 Der Weg über JOIN-Abfragen Zur nächsten ÜberschriftZur vorigen Überschrift

Bisher haben wir immer nur eine Tabelle im DataSet betrachtet. Das entspricht aber nur in wenigen Fällen den üblichen Anforderungen in der Praxis. Um beispielsweise die Frage zu beantworten, welche Artikel von den einzelnen Lieferanten stammen, sind zwei Tabellen notwendig: Products und Suppliers. Die meisten Tabellen einer Datenbank stehen mit anderen Tabellen in Beziehung. Meistens handelt es sich dabei um eine 1:n-Beziehung. Beispielsweise stammen von einem Lieferanten mehrere Artikel. Allerdings berücksichtigt die Northwind-Datenbank nicht, dass ein bestimmtes Produkt durchaus auch von mehreren Lieferanten angeboten werden könnte. Dann müsste die Beziehung zwischen den beiden Tabellen durch eine m:n-Beziehung beschrieben werden, die normalerweise in drei Tabellen aufgelöst wird, die miteinander jeweils in einer 1:n-Beziehung stehen.

Wenden wir uns für die weiteren Ausführungen nun den beiden Tabellen Products und Suppliers zu, deren Beziehung Sie in Abbildung 26.3 sehen.

Um Daten aus mehreren Tabellen auszuwerten, werden üblicherweise JOIN-Abfragen benutzt. Wollen Sie zum Beispiel wissen, welche Produkte von den einzelnen Lieferanten angeboten werden, könnte die Abfrage wie folgt lauten:


SELECT Suppliers.CompanyName, Suppliers.ContactName,
       Products.ProductName, Products.UnitPrice
FROM Suppliers INNER JOIN
     Products ON Suppliers.SupplierID = Products.SupplierID

Abbildung 26.3 Die Beziehung zwischen den Tabellen »Products« und »Suppliers«

Das Ergebnis der Abfrage sehen Sie in Abbildung 26.4.

JOIN-Abfragen haben einige Vorteile:

  • Das Ergebnis lässt sich filtern.
  • Das Resultat steht in einer überschaubaren Ergebnismenge.
  • JOIN-Abfragen sind anerkannter Standard.

Abbildung 26.4 Ergebnisliste einer JOIN-Abfrage

Bei kritischer Betrachtung stehen den Vorteilen auf der anderen Seite aber auch schwerwiegende Nachteile gegenüber:

  • Die Daten einer JOIN-Abfrage sind schwierig zu aktualisieren. Insbesondere beim Löschen oder Hinzufügen einer Datenzeile in einer JOIN-Abfrage wird die Problematik deutlich. Löschen Sie beispielsweise eine Datenzeile, stellt sich sofort die Frage, ob nur die Datenzeile in der Detailtabelle, also auf der n-Seite einer Beziehung, gelöscht werden soll oder gleichzeitig auch die Datenzeile in der übergeordneten Mastertabelle, also der 1-Seite.
  • JOIN-Abfragen geben redundante Daten zurück. Lassen Sie sich beispielsweise die Artikelliste und zu jedem Artikel auch noch die notwendigen Informationen des entsprechenden Lieferanten ausgeben, werden die Lieferanteninformationen mehrfach zurückgeliefert (siehe dazu auch Abbildung 26.4).
  • Änderungen in einer JOIN-Abfrage sind schwer zu synchronisieren. Firmiert sich einer der Lieferanten um und tragen Sie das im Abfrageergebnis ein, muss die Änderung sofort zur Datenbank übermittelt und die gesamte Abfrage erneut ausgeführt werden.

Galileo Computing - Zum Seitenanfang

26.4.2 Mehrere Tabellen in einem »DataSet« Zur nächsten ÜberschriftZur vorigen Überschrift

ADO.NET löst die Nachteile, die eine JOIN-Abfrage hat, auf eine eigene Art und Weise. Dazu wird die JOIN-Abfrage in Einzeltabellen aufgeteilt, die miteinander in Beziehung gesetzt werden. Mit anderen Worten: Es wird ein Teil der Originaldatenbank abgebildet. Die Beziehung zwischen zwei Tabellen wird durch ein Objekt vom Typ DataRelation beschrieben.

Obschon solchermaßen strukturierte DataSets schwer zu filtern sind, überwiegen die Vorteile. So werden weniger Daten zurückgegeben als bei einer JOIN-Abfrage. Damit wird sowohl die Netzbelastung als auch die Auslastung des lokalen Speichers so gering wie möglich gehalten. Zudem ist es viel einfacher, Daten zu aktualisieren. Löschen Sie zum Beispiel einen Datensatz aus der Detailtabelle (n-Seite), möchten Sie vermutlich nicht auch gleichzeitig den entsprechenden Datensatz der Mastertabelle (1-Seite) löschen. Beide Informationen sind in einer JOIN-Abfrage jedoch in einer Datenzeile zusammengefasst. Operieren Sie mit einer DataRelation zwischen zwei DataTable-Objekten, lässt sich der Datensatz aus der Detailtabelle löschen, ohne dass zwangsläufig auch die entsprechende Datenzeile der Mastertabelle gelöscht wird.


Galileo Computing - Zum Seitenanfang

26.4.3 Eine »DataRelation« erzeugen Zur nächsten ÜberschriftZur vorigen Überschrift

Mithilfe einer DataRelation werden zwei DataTable-Objekte über DataColumn-Objekte miteinander verknüpft. In der Products/Suppliers-Beziehung ist die Tabelle Suppliers das übergeordnete und die Tabelle Products das untergeordnete Element der Beziehung. Dies ist vergleichbar mit einer Primärschlüssel/Fremdschlüssel-Beziehung. Beziehungen werden zwischen einander entsprechenden Spalten in der übergeordneten und der untergeordneten Tabelle erstellt. Das heißt, dass der Datentyp für beide Spalten identisch sein muss.

Aus einer längeren Liste möchten wir Ihnen einen der DataRelation-Konstruktoren vorstellen.


public DataRelation(string relationName, DataColumn parentColumn, 
                    DataColumn childColumn)

Dem ersten Parameter teilen Sie mit, unter welchem Namen die DataRelation angesprochen werden soll, der zweite Parameter erwartet die Referenz auf die übergeordnete Spalte der Mastertabelle (1-Seite), der dritte Parameter die Referenz auf die untergeordnete Spalte der Detailtabelle (n-Seite).

Nachdem eine DataRelation erzeugt worden ist, muss sie dem DataSet bekanntgegeben werden. Dazu enthält das DataSet eine Auflistung vom Typ DataRelationCollection. Die Eigenschaft Relations des DataSets gibt die Referenz auf die Auflistung zurück.

Das folgende Beispiel zeigt, wie die Beziehung zwischen den beiden Tabellen Suppliers und Products festgelegt wird:


SqlConnection con = new SqlConnection();
con.ConnectionString = "...";
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "SELECT * FROM Suppliers; " +
                  "SELECT * FROM Products";
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.TableMappings.Add("Table", "Lieferanten");
da.TableMappings.Add("Table1", "Produkte");
da.Fill(ds); 
// Erzeugen einer Beziehung zwischen den beiden Tabellen
DataColumn colMaster = ds.Tables["Lieferanten"].Columns["SupplierID"];
DataColumn colDetail = ds.Tables["Produkte"].Columns["SupplierID"];
DataRelation rel = new DataRelation("LieferantenProdukte",
                                    colMaster, colDetail);
ds.Relations.Add(rel);


Galileo Computing - Zum Seitenanfang

26.4.4 »DataRelation« und Einschränkungen Zur nächsten ÜberschriftZur vorigen Überschrift

Erzeugen Sie eine DataRelation zwischen zwei Tabellen wie zuvor gezeigt, werden ein UniqueConstraint auf der Mastertabelle sowie ein ForeignKeyConstraint auf der Detailtabelle erstellt.

Haben Sie vor dem Erstellen der DataRelation Einschränkungen definiert, die einer Unique- und einer Fremdschlüsseleinschränkung entsprechen, übernimmt die neue DataRelation die vorhandenen Einschränkungen und erzeugt implizit keine neuen.

Andererseits können Sie auch das implizite Erzeugen der Einschränkungen unterdrücken. Dazu übergeben Sie dem Konstruktor der DataRelation im vierten Parameter false:


DataRelation rel = new DataRelation("LieferantenProdukte",
                    colMaster, colDetail, false);

Das »ForeignKeyConstraint«-Objekt im Detail

Das ForeignKeyConstraint-Objekt gehört zur ConstraintCollection der Detailtabelle, also der Tabelle auf der n-Seite einer 1:n-Beziehung. Es weist nicht nur eine Reihe von Eigenschaften auf, um die Beziehung zwischen den beiden Tabellen zu untersuchen, sondern legt darüber hinaus fest, wie sich die beiden Tabellen verhalten, wenn in der übergeordneten Mastertabelle Daten geändert oder Datenzeilen gelöscht werden.

Das folgende Codefragment zeigt, wie Sie die Eigenschaften Table, RelatedTable, Columns und RelatedColumns auswerten. Die Auswertung basiert auf der Beziehung, die weiter oben zwischen den beiden Tabellen Suppliers und Products codiert worden ist.


ConstraintCollection constr = ds.Tables["Produkte"].Constraints;
foreach (Constraint cTemp in constr) {
  if (cTemp is ForeignKeyConstraint) {
    Console.Write("Untergeordnete Tabelle: ");
    Console.WriteLine(((ForeignKeyConstraint)cTemp).Table);
    Console.Write("Untergeordnete Spalte(n): ");
    foreach (DataColumn col in ((ForeignKeyConstraint)cTemp).Columns)
      Console.WriteLine(col.ColumnName);
      Console.Write("Übergeordnete Tabelle: ");
      Console.WriteLine(((ForeignKeyConstraint)cTemp).RelatedTable);
      Console.Write("Übergeordnete Spalte(n): ");
      foreach (DataColumn col in ((ForeignKeyConstraint)cTemp).
      RelatedColumns)
        Console.WriteLine(col.ColumnName);
  }
}

Table liefert die Referenz auf die untergeordnete Tabelle, RelatedTable die Referenz auf die übergeordnete Tabelle (Mastertabelle). Columns beschreibt die Spalten der untergeordneten Tabelle der Einschränkung, RelatedColumns die Spalten der übergeordneten Tabelle. Beide zuletzt genannten Eigenschaften liefern ein DataColumn-Array zurück, weil mehrere Spalten ein gemeinsames Merkmal für die Beziehung zwischen zwei Tabellen darstellen können.

Wichtiger als die Auswertung der Eigenschaften einer Beziehung sind diejenigen Eigenschaften, über die das Verhalten der Relation festgelegt wird. Löschen Sie beispielsweise eine Datenzeile in der Mastertabelle, stellt sich die Frage, wie sich die verknüpften Datenzeilen in der untergeordneten Detailtabelle verhalten sollen. Sollen sie ebenfalls gelöscht werden? Oder sollen sie in der Detailtabelle erhalten bleiben? Was ist, wenn in der Mastertabelle ein Wert geändert wird? Wird dann der Wert in der untergeordneten Tabelle ebenfalls aktualisiert?

Die Steuerung dieses Verhaltens wird von den Eigenschaften UpdateRule und DeleteRule bestimmt. Beide Eigenschaften sind vom Typ Rule. Dabei handelt es sich um eine Enumeration im Namespace System.Data. Die Werte der Enumeration sind in Tabelle 26.5 angegeben.


Tabelle 26.5 Werte der Enumeration »Rule«

Wert Beschreibung
Cascade

Hierbei handelt es sich um den Standardwert. Wird eine Datenzeile in der Mastertabelle gelöscht (geändert), werden auch alle Detaildatenzeilen gelöscht (geändert).

None

Es wird keine Aktion ausgeführt und stattdessen eine Ausnahme ausgelöst.

SetDefault

Die Werte in den verknüpften Datenzeilen der Detailtabelle werden auf Standardwerte eingestellt. Die Standardwerte werden über die Eigenschaft DefaultValue des DataColumn-Objekts festgelegt.

SetNull

Die Werte in den verknüpften Datenzeilen werden auf DBNull festgelegt.


Eine dritte Eigenschaft, die in diesem Zusammenhang auch noch erwähnt werden sollte, ist AcceptRejectRule. Sie gibt an, wie mit den verknüpften Datenzeilen umgegangen wird, wenn in der Mastertabelle AcceptChanges oder RejectChanges aufgerufen wird.

Die Eigenschaft kann nur zwei Werte annehmen: Entweder AcceptRejectRule.None oder AcceptRejectRule.Cascade. Der Vorgabewert ist None. Das bedeutet, dass der Aufruf von AcceptChanges oder RejectChanges auf einer Datenzeile sich nicht auf die untergeordneten Datenzeilen auswirkt. Wenn Sie die Eigenschaft AcceptRejectRule auf Cascade setzen, wird die Aktion an diejenige untergeordnete Datenzeile weitergegeben, die vom ForeignKeyConstraint-Objekt definiert ist.


Galileo Computing - Zum Seitenanfang

26.4.5 In Beziehung stehende Daten suchen Zur nächsten ÜberschriftZur vorigen Überschrift

DataRelation-Objekte werden hauptsächlich dazu benutzt, um Daten zu suchen, die in verschiedenen DataTable-Objekten enthalten sind. Zu diesem Zweck stellt eine DataRow drei Methoden zur Verfügung, die auf einer DataRelation basieren:

  • GetChildRows
  • GetParentRow
  • GetParentRows

GetChildRows sucht, ausgehend von einer Datenzeile in der Mastertabelle, alle zugehörigen untergeordneten Datenzeilen in der Detailtabelle. Dazu übergeben Sie der Methode die DataRelation, die beide Tabellen miteinander verknüpft. Sie erhalten als Ergebnis ein DataRow-Array.


DataRow[] GetChildRows(DataRelation)

Ausgehend von der untergeordneten Zeile einer Detailtabelle ruft GetParentRow die zugehörige übergeordnete Datenzeile aus einer Mastertabelle ab. Auch dieser Methode müssen Sie die DataRelation zwischen den beiden Tabellen angeben; der Rückgabewert ist eine einzige Datenzeile.


DataRow getParentRow(DataRelation)

Sollte zwischen zwei Tabellen eine n:m-Beziehung bestehen, können Sie die GetParentRow-Methode einsetzen.


DataRow[] GetParentRows(DataRelation)

Ich möchte Ihnen nun in einem Beispielprogramm die Benutzung der Methoden zeigen. Die Aufgabenstellung dazu lautet, dass zu den einzelnen Aufträgen (Tabelle Orders) die bestellten Produkte (Tabelle Products) aufgelistet werden sollen. Zwischen diesen beiden Tabellen besteht eine m:n-Beziehung, die durch die Tabelle Order Details in zwei 1:n-Beziehungen aufgelöst wird (siehe Abbildung 20.5).

Abbildung 26.5 Die Beziehungen zwischen den Tabellen »Orders«, »Order Details« und »Products«


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 26\DataRelationNavigation
// ------------------------------------------------------------------
static void Main(string[] args) {
  SqlConnection con = new SqlConnection();
  con.ConnectionString = "...";
  SqlCommand cmd = new SqlCommand();
  cmd.Connection = con;
  cmd.CommandText = "SELECT * FROM Orders; " +
                    "SELECT * FROM [Order Details]; " +
                    "SELECT * FROM Products";
  DataSet ds = new DataSet();
  SqlDataAdapter da = new SqlDataAdapter(cmd);
  da.TableMappings.Add("Table", "Bestellungen");
  da.TableMappings.Add("Table1", "Bestelldetails");
  da.TableMappings.Add("Table2", "Produkte");
  da.Fill(ds);
  // DataRelation zwischen Orders und OrderDetails erzeugen
  DataColumn colMaster = ds.Tables["Bestellungen"].Columns["OrderID"];
  DataColumn colDetail = ds.Tables["Bestelldetails"].Columns
  ["OrderID"];
  DataRelation rel =
          new DataRelation("Bestellungen_Bestelldetails",
          colMaster, colDetail);
  ds.Relations.Add(rel);
  // DataRelation zwischen OrderDetails und Products erzeugen
  colMaster = ds.Tables["Produkte"].Columns["ProductID"];
  colDetail = ds.Tables["Bestelldetails"].Columns["ProductID"];
  rel = new DataRelation("Produkte Bestelldetails", colMaster, colDetail);
  ds.Relations.Add(rel);
  // zu jeder Bestellung die bestellten Artikel anzeigen
  foreach (DataRow rowOrder in ds.Tables["Bestellungen"].Rows)
  {
    Console.WriteLine("Autor: {0}", rowOrder["OrderID"]);
    foreach (DataRow rowOrderDetail in rowOrder.GetChildRows(
               ds.Relations["Bestellungen_Bestelldetails"]))
    {
      DataRow rowProduct;
      rowProduct = rowOrderDetail.GetParentRow(
               ds.Relations["Produkte_Bestelldetails"]);
      Console.WriteLine("Artikel: {0}", rowProduct["ProductName"]);
    }
    Console.WriteLine(new string('-', 40));
  }
  Console.ReadLine();
}

In diesem Beispiel werden vor dem Füllen des DataSets den drei beteiligten Tabellen zunächst über TableMappings sprechende Bezeichner zugewiesen. Hier ist ein solches Vorgehen besonders empfehlenswert, um den Code besser lesbar zu gestalten. Nachdem der SqlDataAdapter die Daten abgerufen hat, werden in einer äußeren foreach-Schleife alle Datenzeilen der Tabelle Orders nacheinander durchlaufen. Auf jeder Datenzeile (also Bestellnummer) wird GetChildRows aufgerufen. Diese Methode liefert ein DataRow-Array zurück, das alle Datenzeilen aus Order Details enthält, die dieser Bestellnummer entsprechen. Aus der gefundenen Datenzeile wird anschließend das Feld ProductID extrahiert und mit GetParentRow die entsprechende Datenzeile in der Tabelle Products gesucht. Letztere liefert uns den Artikelnamen.


Galileo Computing - Zum Seitenanfang

26.4.6 Ergänzung zum Speichern von Schema-Informationen in einer XML-Schema-Datei topZur vorigen Überschrift

In Abschnitt 26.2.5, »Schema-Informationen mit »SqlDataAdapter« abrufen«, habe ich gezeigt, wie Sie die Schema-Informationen mit der Methode WriteXmlSchema des DataSets in einer XML-Schema-Datei speichern können. Ich hatte Ihnen das auch an einem Beispiel demonstriert, das die Metadaten einer Tabelle in die Schema-Datei schrieb. Sie können selbstverständlich auf die gleiche Weise auch die Metadaten mehrerer sich in einem DataSet befindlicher Tabellen in einer Datei bereitstellen. Allerdings gibt es dabei einen besonderen Punkt zu beachten: Beabsichtigen Sie, auch die Beziehungen zwischen den Tabellen in der Schema-Datei zu speichern, müssen Sie die Beziehungen zwischen den Tabellen zuerst mit Programmcode definieren, bevor Sie die Methode WriteXmlSchema aufrufen.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2010

Visual C# 2010
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2010
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern