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 29 LINQ to SQL
Pfeil 29.1 Allgemeine Grundlagen
Pfeil 29.2 Objektzuordnung mit Entitätsklassen
Pfeil 29.3 Mapping von Objekten
Pfeil 29.3.1 Das »Table«-Attribut
Pfeil 29.3.2 Das »Column«-Attribut
Pfeil 29.4 Verknüpfungen zwischen Entitäten
Pfeil 29.4.1 Der Typ »EntityRef<T>«
Pfeil 29.4.2 Verzögertes Laden
Pfeil 29.4.3 Der Typ »EntitySet<T>«
Pfeil 29.4.4 Ein weiteres Beispiel
Pfeil 29.4.5 Sofortiges Laden der Daten
Pfeil 29.5 Tools zur Erzeugung von Entitätsklassen
Pfeil 29.6 Die Klasse »DataContext«
Pfeil 29.6.1 Verbindungsaufbau
Pfeil 29.6.2 Daten abfragen
Pfeil 29.6.3 Von einer LINQ-Abfrage erzeugtes SQL-Statement ausgeben
Pfeil 29.6.4 Aktualisieren der Daten
Pfeil 29.6.5 Konflikte behandeln
Pfeil 29.7 Der LINQ to SQL-Designer (O/R-Designer)
Pfeil 29.7.1 Handhabung des O/R-Designers
Pfeil 29.7.2 Die abgeleitete »DataContext«-Klasse
Pfeil 29.7.3 Entitätsklassen


Galileo Computing - Zum Seitenanfang

29.4 Verknüpfungen zwischen Entitäten Zur nächsten ÜberschriftZur vorigen Überschrift

In einer relationalen Datenbank wird die Beziehung zwischen zwei Tabellen mit einem Konzept realisiert, bei dem der Fremdschlüssel einer Tabelle auf den Primärschlüssel einer anderen Tabelle verweist. Dieses Konzept wird in LINQ to SQL mit dem Association-Attribut umgesetzt. Dabei werden beide Seiten einer 1-n-Beziehung beschrieben.

Im folgenden Codeausschnitt sind die beiden Tabellen Categories und Products miteinander in Beziehung gesetzt. Categories beschreibt die 1-Seite der Beziehung (Mastertabelle) und Products die n-Seite der Beziehung (Detailtabelle). Auf Code, der nicht im Zusammenhang mit der Beziehung steht, wird in diesem Codefragment komplett verzichtet. Sehen Sie sich zuerst die Entität Category an.


public class Category {
  private EntitySet<Product> _Products;
  [Association(Name="Categories_Products",   
               Storage="_Products", 
               ThisKey="CategoryID", 
               OtherKey="CategoryID")]
  public EntitySet<Product> Products {
    get {
      return this._Products;
    }
    set {
      this._Products.Assign(value);
    }
  }
}

Jetzt folgt die Beschreibung der Entität Product.


public class Product {
  private EntityRef<Category> _Category;
  [Association(Name="Category_Product",           
               Storage="_Categories", 
               ThisKey="CategoryID", 
               OtherKey="CategoryID", 
               IsForeignKey=true)]
  public Category Category {
    get {
      return this._Category.Entity;
    }
    set {
      this._Category.Entity = value;
    }
  }
}

In beiden der miteinander in Beziehung stehenden Tabellen wird die Beziehung durch ein privates Feld und eine öffentliche Eigenschaft abgebildet. Da eine Instanz vom Typ der Entitätsklasse Product genau einer Kategorie zugeordnet wird, kann im Feld der Product-Instanz die Information der zugeordneten Kategorie gespeichert werden. Ähnliches gilt für eine Instanz der Entität Category. Der Unterschied ist nur der, dass sich einer bestimmten Kategorie mehrere Produkte zuordnen lassen.

Dass wir es auf der einen Seite mit einer eindeutigen Zuordnung zu tun haben (eine Kategorie je Produkt), auf der anderen Seite sich aber mehrere Produkte einer Kategorie zuordnen lassen, findet Berücksichtigung im Datentyp der Felder: EntityRef<T> und EntitySet<T>. Beide nehmen im Kontext des Object Relational Mappings eine wichtige Rolle ein.

Zur Beschreibung einer Beziehung zwischen zwei Datenbanktabellen sind die beiden Eigenschaften (Category in der Product-Entität und Product in der Category-Entität) mit dem Association-Attribut verknüpft. LINQ to SQL bezieht hieraus alle notwendigen Informationen, um bei Bedarf ein passendes SQL-Statement zu erzeugen, das die erforderlichen Daten aus der Datenbank besorgt.

Mit EntityRef<T>, EntitySet<T> und dem Attribut Association wollen wir uns nun etwas genauer beschäftigen.


Galileo Computing - Zum Seitenanfang

29.4.1 Der Typ »EntityRef<T>« Zur nächsten ÜberschriftZur vorigen Überschrift

Die Beziehung zwischen den beiden von uns betrachteten Tabellen besteht zwischen der Spalte CategoryID der Tabelle Products und der gleichnamigen Spalte der Tabelle Categories. Jeder Artikel ist einer bestimmten Kategorie zugeordnet. Die Eigenschaft Category der Entität Product speichert die entsprechende Information in einem Feld vom Typ EntityRef<Category>.


private EntityRef<Category> _Category;

Die Eigenschaftsmethode ist mit dem Attribut Association versehen, dessen Parameter entscheidende Informationen enthalten. ThisKey beschreibt hier die Schlüsselspalte der Entität Products und OtherKey die Schlüsselspalte der in Beziehung gesetzten Entität. Bilden mehrere Spalten den Schlüsselwert, sind die entsprechenden Spaltennamen durch ein Komma getrennt in der Zeichenfolge anzugeben. Mit IsForeignKey=true wird das unter ThisKey genannte Feld zur Fremdschlüsselspalte erklärt.


Tabelle 29.2 Parameter des Attributs »Association«

Parameter Beschreibung
IsForeignKey

Das Feld ist ein Fremdschlüsselfeld.

Name

Spezifiziert den Namen des Fremdschlüssels.

OtherKey

Bezeichnet die Spalte in der Entität, die auf der anderen Seite der Zuordnung liegt.

Storage

Bezeichnet das Feld, das verwendet wird, um das in Beziehung stehende Entitätsobjekt zu speichern.

ThisKey

Bezeichnet die Eigenschaft, die das lokale ID-Feld enthält. Fehlt dieser Parameter, wird das Feld genommen, dessen Column-Attribut die Angabe IsPrimary enthält.


Die Beschreibung der Beziehung mit dem Association-Attribut vereinfacht die Formulierung einer LINQ-Abfrage. Nehmen wir an, Sie möchten eine Liste aller Artikel ausgeben, die einer bestimmten Kategorie zugeordnet sind. Sie müssen keinen JOIN mehr definieren und können die Eigenschaft Category nutzen, die in Beziehung zur Entität Category steht. Auf der Eigenschaft Category rufen Sie die Primärschlüsselspalte ab und filtern die Ergebnisse wie gewünscht.


var query = from prod in products
            where prod.Category.CategoryID == 2
            select prod.ProductName;


Hinweis

Das komplette Beispiel finden Sie auf der Buch-DVD unter:
..\Beispiele\Kapitel 29\LINQ_to_SQL\Example_1



Anmerkung

Um die LINQ-Abfrage zu testen, benötigen Sie ein DataContext-Objekt und die Liste aller Produkte. Obwohl wir auf den Typ DataContext erst in Abschnitt 29.6 näher eingehen, möchte ich Ihnen nicht verschweigen, wie diese Klasse einsetzt wird:

DataContext context = new DataContext(con);
Table<Product> products = context.GetTable<Product>();
Table<Category> categories = context.GetTable<Category>();

Zunächst sollten Sie den Namensraum System.Data.Linq mit using bekannt geben. Dem Konstruktor der DataContext-Klasse übergeben Sie eine Verbindungszeichenfolge. Diese gleicht der eines SqlConnection-Objekts von ADO.NET. Das DataContext-Objekt hat mit GetTable<T> eine Methode, deren generischer Typparameter die Angabe einer Entitätsklasse erwartet. Sie rufen GetTable<T> für die Entität Product und – falls erforderlich – auch für Category auf.

Der Rückgabewert der Methode GetTable<T> ist vom Typ Table<T> und beschreibt die als Objekte abgebildete Liste aller Datensätze der Tabellen Products und Categories. Der Rückgabewert wird in den Variablen products und categories vorgehalten. products beschreibt hier die Liste, die von unserer LINQ-Abfrage durchlaufen wird.



Galileo Computing - Zum Seitenanfang

29.4.2 Verzögertes Laden Zur nächsten ÜberschriftZur vorigen Überschrift

LINQ to SQL arbeitet mit einer Technik, die als verzögertes Laden bezeichnet wird. Das bedeutet, dass die Entität Category erst dann von der Datenbank in den lokalen Speicher geladen wird, wenn darauf lesend oder schreibend zugegriffen wird, zum Beispiel innerhalb einer Schleife:


foreach (var item in query)
  Console.WriteLine(item);

LINQ to SQL lädt jedoch keine Daten in den Speicher, die sich bereits darin befinden. Deshalb wird vorher intern geprüft, ob sich die entsprechende Category-Entität möglicherweise bereits im Arbeitsspeicher befindet. Ist das der Fall, wird diese Entität genutzt.

Müssen Daten von der Datenbank bezogen werden, wird die LINQ-Abfrage von LINQ to SQL in einen SQL-JOIN-Befehl übersetzt, der wie folgt aussieht:


SELECT [t0].[ProductName]
FROM [dbo].[Products] AS [t0]
LEFT OUTER JOIN [dbo].[Categories] AS [t1] 
     ON [t1].[CategoryID] = [t0].[CategoryID]
WHERE [t1].[CategoryID] = 2


Galileo Computing - Zum Seitenanfang

29.4.3 Der Typ »EntitySet<T>« Zur nächsten ÜberschriftZur vorigen Überschrift

Vielleicht interessiert Sie, welchen Kategorien mehr als 10 Artikel aus der Tabelle Products zugeordnet sind? Um diese Frage zu beantworten, müssen Sie in der Entität Category eine Eigenschaft definieren, die die zugehörige Gruppe der Product-Entitäten repräsentiert.

Das Konzept ähnelt dem aus dem letzten Abschnitt. Allerdings ist eine Product-Entität mit genau einer Category-Entität verknüpft, während eine Category-Entität potenziell mehrere Product-Entitäten beschreiben kann. Daher ist die Eigenschaft, die wir der Entität Category hinzufügen müssen, nicht vom Typ EntityRef<T>, sondern wird durch EntitySet<T> beschrieben.


public class Categories {
  private EntitySet<Product> _Products;
  ...
}

Das private Feld wird durch die Eigenschaft Products veröffentlicht, bei der durch das Attribut Association ebenfalls die Verknüpfung der beiden Entitäten beschrieben wird. Dabei kommen dieselben Parameter ins Spiel, die weiter oben schon erläutert worden sind.


[Association(Name="Category_Product", Storage="_Products", 
             ThisKey="CategoryID", OtherKey="CategoryID")]
public EntitySet<Product> Products {
  get {
    return this._Products;
  }
  set {
    this._Products.Assign(value);
  }
}

Mit dieser Ergänzung in der Entität Category können wir die eingangs gestellte Frage mit einer passenden LINQ-Abfrage beantworten:


var query = from cat in categories
            where cat.Products.Count > 10
            select cat; 
// Ausgabe der Daten
foreach (var item in query) {
  Console.WriteLine(item.CategoryName);
}

Hierbei beschreibt categories die Menge aller Kategorien, die durch Category-Entitäten abgebildet werden. Auch die LINQ-Abfrage wird erst in dem Moment ausgeführt, wenn von der Datenbank Daten angefordert werden. Dabei wird ein SQL-Statement erzeugt, das dem folgenden entspricht:


SELECT [t0].[CategoryID], [t0].[CategoryName]
FROM [dbo].[Categories] AS [t0]
WHERE ((
    SELECT COUNT(*)
    FROM [dbo].[Products] AS [t1]
    WHERE [t1].[CategoryID] = [t0].[CategoryID]
    )) > 10


Hinweis

Das komplette Beispiel zu diesem Code finden Sie auf der Buch-DVD unter:

..\Beispiele\Kapitel 29\LINQ_to_SQL\Example_2



Galileo Computing - Zum Seitenanfang

29.4.4 Ein weiteres Beispiel Zur nächsten ÜberschriftZur vorigen Überschrift

Möglicherweise wollen Sie aber eine Liste aller Produkte ausgeben lassen, die nach Kategoriezugehörigkeit sortiert ist. Da die Entitätsklassen Product und Category die Verknüpfung der Entitäten beschreiben, ist die LINQ-Abfrage extrem einfach zu formulieren:


var query = from cat in categories
            select cat;

Jetzt kommt es nur darauf an, aus den vorliegenden Datenformationen diejenigen herauszufiltern, die von Interesse sind. Dabei hilft uns die Eigenschaft Products der Entität Category weiter. Products beschreibt die Liste all jener Artikel, die der betreffenden Kategorie zugeordnet sind. Um Detailinformationen zu jedem Artikel zu erhalten, müssen wir nur die durch die Eigenschaft Products bereitgestellte Liste Element für Element in einer Schleife durchlaufen:


foreach (var item in query) {
  Console.WriteLine(item.CategoryName);
  foreach (var product in item.Products)
    Console.WriteLine("  {0}", product.ProductName);
  Console.WriteLine(new String('-', 40));
}

Auch in diesem Codefragment wird die Technik des verzögerten Ladens verwendet. Greifen Sie das erste Mal auf die Products-Eigenschaft einer Kategorie zu (im Code ist das mit product.ProductName der Fall), wird LINQ to SQL die Datenbank abfragen, um die entsprechenden Dateninformationen bereitstellen zu können.


Galileo Computing - Zum Seitenanfang

29.4.5 Sofortiges Laden der Daten topZur vorigen Überschrift

Um wiederholtes Abfragen der Datenbank zu vermeiden, können Sie alle Daten sofort laden. Das kann das Laufzeitverhalten Ihrer Anwendung positiv beeinflussen.

Um Daten komplett zu laden, bieten sich Ihnen zwei Möglichkeiten. Die erste gibt explizit die Products-Eigenschaft des Artikels in der Ergebnismenge an:


var query = from cat in categories
            select new { cat.CategoryName, cat.Products };

Auch wenn diese LINQ-Abfrage wieder sehr einfach zu formulieren ist, ist das SQL-Statement, das zur SQL Server-Datenbank geschickt wird, schon recht komplex:


SELECT [t0].[CategoryName], [t1].[ProductID], 
[t1].[ProductName], [t1].[CategoryID], (
    SELECT COUNT(*)
    FROM [dbo].[Products] AS [t2]
    WHERE [t2].[CategoryID] = [t0].[CategoryID]
    ) AS [value]
FROM [dbo].[Categories] AS [t0] 
LEFT OUTER JOIN [dbo].[Products] AS [t1] ON 
[t1].[CategoryID] = [t0].[CategoryID] 
ORDER BY [t0].[CategoryID], [t1].[ProductID]

Die Alternative zu der Angabe der Eigenschaft bietet die Klasse DataLoadOptions. Der Methode LoadWith<T> des DataLoadOptions-Objekts geben Sie über einen Lambda-Ausdruck an, welche untergeordneten Objekte abgerufen werden sollen. Anschließend müssen Sie der Eigenschaft LoadOptions des DataContext-Objekts noch die Referenz auf das DataLoadOptions-Objekt übergeben.


DataContext context = new DataContext(con);
Table<Category> categories = context.GetTable<Category>();
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Category>(cat => cat.Products);
context.LoadOptions = dlo;
var query = from cat in categories
            select new { cat.CategoryName, cat.Products };

Angenommen, Sie möchten sich in allen Kategorien die zugehörigen Artikel anzeigen lassen, die mehr als 50 EUR kosten. Den entsprechenden Filter auf die gemappten Daten anzusetzen, würde bedeuten, dass zu viele Daten unnützerweise den Weg von der Datenbank in die Clientanwendung suchen. Besser ist es, den Filter bereits vorher festzulegen. Die Klasse DataLoadOptions bietet diese Möglichkeit. Sie müssen nur vor LoadWith<T> die Methode AssociateWith<T> aufrufen und dort den Filter als Argument angeben.


DataLoadOptions dlo = new DataLoadOptions();
dlo.AssociateWith<Category>(
    cat => cat.Products.Where(prod => prod.UnitPrice > 50));
dlo.LoadWith<Category>(cat => cat.Products);
            context.LoadOptions = dlo;
var query = from cat in categories
            select new { cat.CategoryName, cat.Products };

In manchen Fällen ist das sofortige Laden sämtlicher Daten sicherlich eine bessere Lösung als das verzögerte Laden nach Bedarf, bei dem es immer wieder zu Wartezeiten kommen kann. Der Nachteil dieser Technik liegt allerdings auch auf der Hand, denn es wird viel Speicher in Anspruch genommen, und das Netz wird belastet.



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