LINQ to SQL-Entitätsklassen und Datenbanktabellen

Werbung
LINQ
1) LINQ Überblick
2) LINQ Entities
a) ObjectQuery
3) LINQ SQL
4) LINQ XML
5) LINQ DataSets
6) LINQ ADO.NET
7) LINQ Concurrency
8) LINQ DataBase
LINQ (Abkürzung für Language Integrated Query) ist eine Komponente von Microsofts .NETFramework zur Abfrage von Datenquellen wie Datenbanken oder XML-Dateien. Besonderheit ist,
dass SQL-, XLink- und XQuery-Anfragen direkt in .NET-Programmiersprachen wie C# 3.0 oder VB.Net
9.0 als Code statt als String eingebunden werden können. LINQ wurde federführend von Erik Meijer
entwickelt.
Der Vorteil von LINQ besteht darin, dass der Code durch den Compiler auf Fehler geprüft und von
den anbietenden Bibliotheken optimiert, oder anderweitig manipuliert — zum Beispiel übersetzt —
werden kann. Die Syntax von LINQ ist dabei an den Befehlen der SQL-Anfragesprache wie „select“,
„from“ und „where“ angelehnt. Die Befehle werden jedoch nicht in SQL-Code umgewandelt, sondern
sprechen, mit Hilfe der im .NET-Framework 3.5 eingeführten Erweiterungen, anfragemanipulierende
Erweiterungsmethoden an, die zuletzt aus dem Anfrage-Ausdruck eine Gesamt-Anfrage generieren.
Im Wesentlichen ist LINQ dabei auf die Kombination von Syntaxbäumen und
Anfragesprachenbestandteilen ausgelegt, wobei die Bedeutungen der Kombinationsoperationen an
sich vollständig durch die LINQ-Provider festgelegt werden. Es existieren beispielsweise Projekte für
LINQ to Google, LINQ to LDAP oder auch LINQ to Streams. Auch gänzlich andere Provider, die sich
nicht auf den Zusammenbau von Datenabfragen beschränken, existieren. Als Beispiele für andere
Anwendungsgebiete des zunächst rein lingualen Features LINQ seien monadische
Parserkombinatoren und Ereignisbehandler wie im Rx Reactive Framework genannt.
Das LINQ-Framework enthält zudem das Tool SQLMetal, welches die automatische Codegenerierung
von Wrapper-Klassen für Microsoft SQL Server-Datenbanken in C# mit DLINQ ermöglicht, was
Softwareentwicklern einen zusätzlichen Komfort bei der Applikationsentwicklung bietet.
LINQ im Allgemeinen
Alle LINQ-Abfrageoperationen bestehen aus drei unterschiedlichen Aktionen:
1. Abrufen der Datenquelle
2. Erstellen der Abfrage
3. Ausführen der Abfrage
Im folgenden Beispiel wird gezeigt, wie die drei Teile einer Abfrageoperation in Quellcode
ausgedrückt werden. Das Beispiel verwendet aus praktischen Gründen ein Array von Ganzzahlen als
Datenquelle. Dieselben Konzepte gelten jedoch auch für andere Datenquellen. Auf dieses Beispiel
wird im Rest dieses Themas Bezug genommen.
class IntroToLINQ
{
static void Main()
{
// The Three Parts of a LINQ Query:
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
}
}
Die Abfrage gibt an, welche Informationen aus der Datenquelle oder den Datenquellen abgerufen
werden sollen. Optional kann eine Abfrage auch angeben, wie diese Informationen vor der Rückgabe
sortiert, gruppiert und strukturiert werden sollen. Eine Abfrage wird in einer Abfragevariablen
gespeichert und mit einem Abfrageausdruck initialisiert. Um das Schreiben von Abfragen zu
erleichtern, hat C# eine neue Abfragesyntax eingeführt.
Verzögerte Ausführung
Wie bereits erwähnt, speichert die Abfragevariable selbst nur die Abfragebefehle. Die tatsächliche
Ausführung der Abfrage wird so lange verzögert, bis Sie die Abfragevariable in einer foreachAnweisung durchlaufen. Dieses Konzept wird als verzögerte Ausführung bezeichnet und wird im
folgenden Beispiel veranschaulicht:
//Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
In der foreach-Anweisung werden auch die Abfrageergebnisse abgerufen. So ist beispielsweise in der
vorherigen Abfrage in der Iterationsvariablen num jeder Wert (jeweils einzeln) der zurückgegebenen
Sequenz enthalten.
Da die Abfragevariable selbst nie die Abfrageergebnisse enthält, können Sie sie beliebig oft
ausführen. Sie könnten beispielsweise über eine Datenbank verfügen, die ständig durch eine
separate Anwendung aktualisiert wird. Sie könnten in Ihrer Anwendung eine Abfrage erstellen, die
die neuesten Daten abruft, und Sie könnten diese Abfrage in bestimmten Abständen wiederholt
ausführen, um bei jeder Ausführung andere Ergebnisse abzurufen.
Erzwingen der unmittelbaren Ausführung
Abfragen, die Aggregationsfunktionen für einen Bereich von Quellelementen ausführen, müssen
zuerst diese Elemente durchlaufen. Beispiele für solche Abfragen sind Count, Max, Average und
First. Diese Abfragen werden ohne explizite foreach-Anweisung ausgeführt, da die Abfrage selbst
foreach verwenden muss, um ein Ergebnis auszugeben. Beachten Sie auch, dass diese Typen von
Abfragen einen einzelnen Wert und keine IEnumerable-Auflistung zurückgeben. Die folgende
Abfrage gibt eine Anzahl der geraden Zahlen im Quellarray zurück:
var evenNumQuery =
from num in numbers
where (num % 2) == 0
select num;
int evenNumCount = evenNumQuery.Count();
Um die unmittelbare Ausführung einer Abfrage zu erzwingen und ihre Ergebnisse
zwischenzuspeichern, können Sie die ToList<TSource>-Methode oder die ToArray<TSource>Methode aufrufen.
List<int> numQuery2 =
(from num in numbers
where (num % 2) == 0
select num).ToList();
// or like this:
// numQuery3 is still an int[]
var numQuery3 =
(from num in numbers
where (num % 2) == 0
select num).ToArray();
LINQ und generische Typen (C#)
LINQ-Abfragen basieren auf generischen Typen, die in Version 2.0 von .NET Framework eingeführt
wurden. Sie benötigen kein ausführliches Wissen über Generika, um Abfragen zu schreiben. Dennoch
ist es gut, wenn Sie zwei grundlegende Konzepte verstehen:
1. Wenn Sie eine Instanz einer generischen Auflistungsklasse erstellen, wie zum Beispiel List<T>,
ersetzen Sie das "T" durch den Typ von Objekten, die die Liste enthalten wird. Zum Beispiel
wird eine Liste von Zeichenfolgen als List<string> und eine Liste von Customer-Objekten als
List<Customer> ausgedrückt. Eine generische Liste weist eine starke Typisierung auf und
bietet viele Vorteile gegenüber Auflistungen, die ihre Elemente als Object speichern. Wenn
Sie versuchen, Customer zu List<string> hinzuzufügen, tritt zur Kompilierungszeit ein Fehler
auf. Die Verwendung von generischen Auflistungen ist leicht, da Sie zur Laufzeit keine
Typumwandlungen ausführen müssen.
2. IEnumerable<T> ist die Schnittstelle, die die Aufzählung von generischen Auflistungsklassen
durch Verwenden der foreach-Anweisung ermöglicht. Generische Auflistungsklassen
unterstützen IEnumerable<T>, ebenso wie nicht generische Auflistungsklassen, wie zum
Beispiel ArrayList, IEnumerable unterstützen.
IEnumerable<T>-Variablen in LINQ-Abfragen
LINQ-Abfragevariablen werden als IEnumerable<T> typisiert oder als abgeleiteter Typ, wie zum
Beispiel IQueryable<T>. Wenn Sie eine Abfragevariable mit der Typisierung IEnumerable<Customer>
sehen, so bedeutet dies lediglich, dass die Abfrage bei ihrer Ausführung eine Sequenz von keinen
oder mehreren Customer-Objekten erstellen wird.
IEnumerable<Customer> customerQuery =
from cust in customers
where cust.City == "London"
select cust;
foreach (Customer customer in customerQuery)
{
Console.WriteLine(customer.LastName + ", " + customer.FirstName);
}
Behandeln generischer Typdeklarationen mithilfe des Compilers
Auf Wunsch können Sie die generische Syntax auch vermeiden, indem Sie das var-Schlüsselwort
verwenden. Das var-Schlüsselwort weist den Compiler an, den Typ einer Abfragevariablen abzuleiten,
indem er sich nach der in der from-Klausel angegebenen Datenquelle richtet. Das folgende Beispiel
erstellt den gleichen kompilierten Code wie das vorherige Beispiel:
var customerQuery2 =
from cust in customers
where cust.City == "London"
select cust;
foreach(var customer in customerQuery2)
{
Console.WriteLine(customer.LastName + ", " + customer.FirstName);
}
Das var-Schlüsselwort bietet sich besonders an, wenn der Variablentyp offensichtlich ist oder wenn
die ausdrückliche Angabe von geschachtelten, generischen Typen, z. B. solche, die bei
Gruppenabfragen erstellt werden, nicht so wichtig ist. Allgemein möchten wir darauf hinweisen, dass
Ihr Code durch Verwendung von var eventuell schlechter lesbar wird.
LINQ to Entities bietet LINQ (Language Integrated Query)-Unterstützung, die es Entwicklern
ermöglicht, in Visual Basic oder Visual C# Abfragen für das konzeptionelle Modell im Entity
Framework zu schreiben. Abfragen für das Entity Framework werden als Befehlsstrukturabfragen
dargestellt, die für den Objektkontext ausgeführt werden. LINQ to Entities wandelt LINQ (LanguageIntegrated Queries)-Abfragen in Befehlsstrukturabfragen um, führt die Abfragen für das Entity
Framework aus und gibt Objekte zurück, die sowohl vom Entity Framework als auch von LINQ
verwendet werden können. Mit folgendem Vorgang können Sie eine LINQ to Entities-Abfrage
erstellen und ausführen:
1. Erstellen Sie eine ObjectQuery-Instanz aus dem ObjectContext.
2. Verfassen Sie mithilfe der ObjectQuery-Instanz eine LINQ to Entities-Abfrage in C# oder
Visual Basic.
3. Wandeln Sie LINQ-Standardabfrageoperatoren und -ausdrücke in Befehlsstrukturen um.
4. Führen Sie die als Befehlsstruktur dargestellte Abfrage für die Datenquelle aus. Alle
Ausnahmen, die bei der Ausführung für die Datenquelle ausgelöst wurden, werden direkt an
den Client weitergegeben.
5. Geben Sie die Abfrageergebnisse an den Client zurück.
a. ObjectQuery
Stellt eine typisierte Abfrage eines konzeptionellen Modells in einem angegebenen
Objektkontext dar.
public class ObjectQuery<T> : ObjectQuery,
IOrderedQueryable<T>, IQueryable<T>, IEnumerable<T>,
IOrderedQueryable, IQueryable,
IEnumerable, IListSource
Erstellen einer 'ObjectQuery'-Instanz
Die generische ObjectQuery-Klasse stellt eine Abfrage dar, die eine Auflistung von 0 (null) oder mehr
typisierten Entitäten zurückgibt. Eine Objektabfrage wird normalerweise aus einem bestehenden
Objektkontext und nicht manuell erstellt. Sie gehört immer zu diesem Objektkontext. Dieser Kontext
stellt die Verbindungs- und Metadateninformationen bereit, die zum Verfassen und Ausführen der
Abfrage erforderlich sind. Die generische ObjectQuery-Klasse implementiert die generische
IQueryable-Schnittstelle, durch deren Generatormethoden die inkrementelle Erstellung von LINQAbfragen aktiviert wird. Sie können auch den Typ von Entitäten mit dem var-Schlüsselwort in C# (Dim
in Visual Basic, wobei der lokale Typrückschluss aktiviert sein muss) per Rückschluss vom Compiler
ableiten lassen.
Konvertieren von Abfragen
Um eine LINQ to Entities-Abfrage für das Entity Framework auszuführen, muss die LINQ-Abfrage in
eine Befehlsstrukturdarstellung umgewandelt werden, die für das Entity Framework ausgeführt
werden kann.
LINQ to Entities-Abfragen bestehen aus LINQ-Standardabfrageoperatoren (wie Select, Where und
GroupBy) sowie Ausdrücken (x > 10, Contact.LastName usw.). LINQ-Operatoren werden nicht von
einer Klasse definiert, sondern sind Methoden für eine Klasse. In LINQ können Ausdrücke alles
enthalten, was für Typen im System.Linq.Expressions-Namespace zulässig ist, und durch Erweiterung
alles, was als Lambda-Funktion dargestellt werden kann. Damit sind sie den Ausdrücken
übergeordnet, die im Entity Framework verwendet werden können. Diese sind per Definition auf
Operationen beschränkt, die auf der Datenbank zulässig sind und von ObjectQuery unterstützt
werden.
Abfrageausführung
Nachdem der Benutzer die LINQ-Abfrage erstellt hat, wird sie in eine Darstellung konvertiert, die mit
dem Entity Framework kompatibel ist (in Form von Befehlsstrukturen). Diese wird anschließend für
die Datenquelle ausgeführt. Bei der Abfrageausführung werden alle Abfrageausdrücke (oder
Abfragekomponenten) auf dem Client oder dem Server ausgewertet.
Materialisierung
Als Materialisierung wird das Zurückgeben von Abfrageergebnissen in Form von CLR-Typen an den
Client bezeichnet. In LINQ to Entities werden Datensätze mit Abfrageergebnissen nie zurückgegeben.
Hier kurz 2 Beispiele.
Bsp1.
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<Contact> sortedContacts = context.Contacts
.OrderBy(c => c.LastName)
.ThenBy(c => c.FirstName);
Console.WriteLine("The list of contacts sorted by last name then by
first name:");
foreach (Contact sortedContact in sortedContacts)
{
Console.WriteLine(sortedContact.LastName + ", " +
sortedContact.FirstName);
}
}
Hier wird eine Abfrage erstellt die zuerst den LastName danach den FirstName sortiert.
Bsp2.
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IOrderedQueryable<Product> query = context.Products
.OrderBy(product => product.ListPrice)
.ThenByDescending(product => product.Name);
foreach (Product product in query)
{
Console.WriteLine("Product ID: {0} Product Name: {1} List Price
{2}",
product.ProductID,
product.Name,
product.ListPrice);
}
}
.ThenByDescending sortiert absteigend.
In LINQ to SQL wird ein in der Programmiersprache des Entwicklers ausgedrücktes Objektmodell dem
Datenmodell einer relationalen Datenbank zugeordnet. Operationen mit den Daten werden dann
nach dem Objektmodell durchgeführt.
In diesem Szenario übergeben Sie keine Datenbankbefehle (z. B. INSERT) an die Datenbank.
Stattdessen ändern Sie Werte und führen Methoden innerhalb des Objektmodells aus. Wenn Sie die
Datenbank abfragen oder Änderungen übergeben möchten, übersetzt LINQ to SQL Ihre
Anforderungen in die richtigen SQL-Befehle und sendet diese an die Datenbank.
In der nachfolgenden Tabelle werden die grundlegenden Elemente im LINQ to SQL-Objektmodell und
deren Beziehungen zu Elementen im relationalen Datenmodell aufgeführt:
LINQ-zu-SQL-Objektmodell Relationales Datenmodell
Entitätsklasse
Tabelle
Klassenmember
Spalte
Zuordnung
Fremdschlüsselbeziehung
Methode
Gespeicherte Prozedur oder Funktion
LINQ to SQL-Entitätsklassen und Datenbanktabellen
In LINQ to SQL wird eine Datenbanktabelle durch eine Entitätsklasse dargestellt. Eine Entitätsklasse
entspricht jeder anderen Klasse, die Sie erstellen können. Allerdings fügen Sie der Klasse
Anmerkungen mit speziellen Informationen hinzu, die diese Klasse einer Datenbanktabelle zuweisen.
Sie fügen diese Anmerkungen hinzu, indem Sie Ihrer Klassendeklaration ein benutzerdefiniertes
Attribut (TableAttribute) hinzufügen. Siehe hierzu das folgende Beispiel:
Bsp.
[Table(Name = "Customers")]
public class Customerz
{
public string CustomerID;
// ...
public string City;
}
Nur als Tabelle deklarierte Klasseninstanzen (Entitätsklassen) können in der Datenbank gespeichert
werden.
LINQ to SQL-Klassenmember und Datenbankspalten
Neben der Zuordnung von Klassen zu Tabellen legen Sie Felder oder Eigenschaften fest, um
Datenbankspalten darzustellen. Zu diesem Zweck definiert LINQ to SQL das ColumnAttribute-Attribut
wie im folgenden Beispiel:
[Table(Name = "Customers")]
public class Customer
{
[Column(IsPrimaryKey = true)]
public string CustomerID;
[Column]
public string City;
}
Queries In-Depth
LINQ to SQL provides an implementation of the standard query operators for objects associated with
tables in a relational database. This chapter describes the LINQ to SQL-specific aspects of queries.
Query Execution
Whether you write a query as a high-level query expression or build one out of the individual
operators, the query that you write is not an imperative statement executed immediately. It is a
description. For example, in the declaration below the local variable q refers to the description of the
query not the result of executing it.
var q =
from c in db.Customers
where c.City == "London"
select c;
foreach (Customer c in q)
Console.WriteLine(c.CompanyName);
Ein kurzes Bsp. Für einen Join
var q =
from s in db.Suppliers
join c in db.Customers on s.City equals c.City
select new {
Supplier = s.CompanyName,
Customer = c.CompanyName,
City = c.City
};
XML hat sich mittlerweile als hervorragende Möglichkeit durchgesetzt, Daten in einer Vielzahl von
Kontexten zu formatieren. Sie finden XML beispielsweise im Internet, in Konfigurationsdateien, in
Microsoft Office Word-Dateien und in Datenbanken.
Mit LINQ to XML steht Ihnen ein topaktueller, völlig neu gestalteter Ansatz für die Programmierung
mit XML zur Verfügung, der die DOM-Funktionen (Dokumentobjektmodell) zum Ändern von
Dokumenten im Arbeitsspeicher als auch die Unterstützung für LINQ-Abfrageausdrücke in sich
vereint. Obwohl sich diese Abfrageausdrücke syntaktisch von XPath unterscheiden, bieten sie eine
ähnliche Funktionalität.
LINQ to XML ist eine LINQ-fähige XML-Programmierschnittstelle im Arbeitsspeicher, mit der Sie aus
.NET Framework-Programmiersprachen heraus mit XML arbeiten können.
LINQ to XML ähnelt dem Dokumentobjektmodell (DOM) darin, dass das XML-Dokument im
Arbeitsspeicher bereitgestellt wird. Sie können das Dokument abfragen und ändern, und nach dem
ändern können Sie es in einer Datei speichern oder es serialisieren und über das Internet versenden.
LINQ to XML unterscheidet sich aber auch von DOM: Es stellt ein neues Objektmodell bereit, das
leichter und einfacher zu handhaben ist und das sich Sprachverbesserungen in Visual C# 2008
zunutze macht.
Der wichtigste Vorteil von LINQ to XML besteht in dessen Integration in Sprachintegrierte Abfrage
(Language-Integrated Query, LINQ). Diese Integration ermöglicht es Ihnen, Abfragen für das XMLDokument im Arbeitsspeicher zu schreiben, um Auflistungen von Elementen und Attributen
abzurufen. Die Abfragefunktion von LINQ to XML ist von ihrer Funktionalität her mit der von XPath
und XQuery vergleichbar, die Syntax ist allerdings eine andere. Die Integration von LINQ in Visual C#
2008 bietet eine stärkere Typisierung, Syntaxüberprüfung bei der Kompilierung und verbesserte
Debuggerunterstützung.
Ein weiterer Vorteil von LINQ to XML ist die Fähigkeit, Abfrageergebnisse als Parameter für
XElement-Objektkonstruktoren und XAttribute-Objektkonstruktoren zu verwenden. Dies ist ein sehr
effizienter Ansatz zum Erstellen von XML-Strukturen. Diese Herangehensweise, die als funktionale
Konstruktion bezeichnet wird, versetzt Entwickler in die Lage, XML-Strukturen problemlos von einer
Form in eine andere zu transformieren.
Nehmen Sie das Beispiel eines typischen XML-Auftrags, wie er in XML-Beispieldatei: Typischer
Auftrag (LINQ to XML) beschrieben wird. Durch die Verwendung von LINQ to XML können Sie zum
Abrufen des Attributwerts der Teilenummer für jedes Artikelelement im Auftrag die folgende Abfrage
ausführen:
IEnumerable<string> partNos =
from item in purchaseOrder.Descendants("Item")
select (string) item.Attribute("PartNumber");
Ein anderes Beispiel: Sie möchten, sortiert nach Teilenummer, eine Liste der Artikel abfragen, deren
Wert über 100 Dollar liegt. Zum Abfragen dieser Informationen können Sie die folgende Abfrage
ausführen:
IEnumerable<XElement> partNos =
from item in purchaseOrder.Descendants("Item")
where (int) item.Element("Quantity") *
(decimal) item.Element("USPrice") > 100
orderby (string)item.Element("PartNumber")
select item;
Erstellen von XML Strukturen
Einer der bedeutendsten Vorteile beim Programmieren mit LINQ to XML ist die einfache Erstellung
von XML-Strukturen. Wenn Sie z. B. eine kleine XML-Struktur erstellen möchten, können Sie C#-Code
wie im folgenden Beispiel schreiben:
XElement contacts =
new XElement("Contacts",
new XElement("Contact",
new XElement("Name", "Patrick Hines"),
new XElement("Phone", "206-555-0144",
new XAttribute("Type", "Home")),
new XElement("phone", "425-555-0145",
new XAttribute("Type", "Work")),
new XElement("Address",
new XElement("Street1", "123 Main St"),
new XElement("City", "Mercer Island"),
new XElement("State", "WA"),
new XElement("Postal", "68042")
)
)
);
Mit LINQ to DataSet können Daten, die in einem DataSet-Objekt zwischengespeichert sind, leichter
und schneller abgefragt werden. Genauer gesagt, vereinfacht LINQ to DataSet das Abfragen, da der
Entwickler Abfragen direkt in der Programmiersprache schreiben kann, statt eine separate
Abfragesprache verwenden zu müssen. Dies ist besonders für Visual Studio-Entwickler von Vorteil,
kommen sie doch so beim Schreiben von Abfragen in den Genuss der von Visual Studio gebotenen
Funktionen, wie Syntaxüberprüfung bei der Kompilierung, statische Typisierung und IntelliSenseUnterstützung.
LINQ to DataSet kann auch für das Abfragen von Daten verwendet werden, die aus einer oder
mehreren Datenquellen konsolidiert wurden. Dies ermöglicht eine Vielzahl von Szenarios, bei denen
Flexibilität bei der Darstellung und Behandlung von Daten erforderlich ist, wie das Abfragen lokal
aggregierter Daten und die Zwischenspeicherung auf der mittleren Ebene bei Webanwendungen.
Diese Manipulationsmethode wird insbesondere bei der Erstellung von Berichterstellungs-, Analyseund Business Intelligence-Anwendungen benötigt.
Die LINQ to DataSet-Funktionalität wird in erster Linie durch die Erweiterungsmethoden in den
Klassen DataRowExtensions und DataTableExtensions verfügbar gemacht. LINQ to DataSet baut auf
der vorhandenen ADO.NET 2.0-Architektur auf und nutzt diese. Es soll auf keinen Fall ADO.NET 2.0
im Anwendungscode ersetzen. Der vorhandene ADO.NET 2.0-Code funktioniert auch in einer LINQ to
DataSet-Anwendung. Die Beziehung zwischen LINQ to DataSet und ADO.NET 2.0 und dem
Datenspeicher wird im folgenden Diagramm illustriert.
Vererbungshierarchie
System.Object
System.ComponentModel.MarshalByValueComponent
System.Data.DataSet
Namespace: System.Data
Assembly: System.Data (in System.Data.dll)
Syntax
[SerializableAttribute]
public class DataSet : MarshalByValueComponent, IListSource,
IXmlSerializable, ISupportInitializeNotification,
ISupportInitialize, ISerializable
Mit LINQ to ADO.NET können Sie eine Abfrage für ein beliebiges aufzählbares Objekt in ADO.NET
durchführen, indem Sie das Sprachintegrierte Abfrage (Language-Integrated Query, LINQ)Programmiermodell verwenden.
Unter den heutigen Bedingungen müssen viele Entwickler von Geschäftsanwendungen mit zwei
(oder mehr) Programmiersprachen arbeiten: mit einer allgemeinen Programmiersprache für die
Geschäftslogik- und die Darstellungsschicht (wie Visual C# oder Visual Basic) und einer
Abfragesprache für die Interaktion mit der Datenbank (z. B. Transact-SQL).Der Entwickler muss also
mehrerer Sprachen mächtig sein, um seine Arbeit effektiv erledigen zu können. Außerdem sind
dadurch Sprachkonflikte in der Entwicklungsumgebung vorprogrammiert. So ergibt es sich z. B., dass
eine Anwendung, die zur Ausführung einer Abfrage von Daten aus einer Datenbank eine
Datenzugriffs-API verwendet, die Abfrage als Zeichenfolgenliteral angibt, indem sie
Anführungszeichen verwendet. Diese Abfrage ist jedoch für den Compiler nicht lesbar und wird nicht
auf Fehler (Syntaxfehler, tatsächliche Existenz der Spalten oder Zeilen, auf die verwiesen wird, usw.)
geprüft.Auch der Typ der Abfrageparameter wird nicht geprüft, und es gibt keine IntelliSenseUnterstützung.
In Sprachintegrierte Abfrage (Language-Integrated Query, LINQ) können Entwickler in ihrem
Anwendungscode mengenbasierte Abfragen unterbringen, ohne dazu auf eine separate
Abfragesprache zurückgreifen zu müssen. Sie können LINQ-Abfragen für die verschiedensten
aufzählbaren Datenquellen (also Datenquellen, die die IEnumerable-Schnittstelle implementieren)
schreiben, wie Datenstrukturen, XML-Dokumente, SQL-Datenbanken und DataSet-Objekte im
Arbeitsspeicher. Auch wenn diese aufzählbaren Datenquellen auf unterschiedliche Art und Weise
implementiert sind, weisen sie doch alle dieselben Syntax- und Sprachkonstrukte auf. Da Abfragen
direkt in der Programmiersprache formuliert werden können, benötigen Sie keine andere
Abfragesprache, mit der Abfragen als Zeichenfolgenliterale eingebettet werden, die vom Compiler
weder gelesen noch geprüft werden können. Durch die Integration von Abfragen in die
Programmiersprache werden Visual Studio-Programmierer auch in die Lage versetzt, durch Typ- und
Syntaxprüfungen während der Kompilierung sowie IntelliSense-Unterstützung produktiver zu
arbeiten. Mit diesen Funktionen wird der für die Beseitigung von Abfragefehlern erforderliche
Aufwand beträchtlich reduziert.
Das Übertragen von Daten aus SQL-Tabellen in Objekte im Arbeitsspeicher ist häufig
nervenaufreibend und fehleranfällig. Der von LINQ to DataSet und LINQ to SQL implementierte LINQAnbieter konvertiert die Quelldaten in IEnumerable-basierte Objektauflistungen. Dem
Programmierer werden die Daten stets als IEnumerable-Auflistung angezeigt, gleich ob bei einer
Abfrage oder bei einem Update. Für das Schreiben von Abfragen für diese Auflistungen steht
uneingeschränkte IntelliSense-Unterstützung zur Verfügung.
Es gibt drei separate ADO.NET-Sprachintegrierte Abfrage (Language-Integrated Query, LINQ)Technologien: LINQ to DataSet, LINQ to SQL und LINQ to Entities. LINQ to DataSet ermöglicht
umfangreichere, optimierte Abfragen der DataSet-Daten, mit LINQ to SQL können Sie SQL ServerDatenbankschemas direkt abfragen, und mit LINQ to Entities können Sie ein Entity Data Model
abfragen.
Das folgende Diagramm ermöglicht eine Einordnung der ADO.NET LINQ-Technologien im Kontext von
allgemeinen Programmiersprachen und anderen LINQ-fähigen Datenquellen.
Concurrency:
The situation in which two or more users at the same time try to update the same database row.
Concurrency conflict:
The situation in which two or more users at the same time try to submit conflicting values to one or
more columns of a row.
Concurrency control:
The technique used to resolve concurrency conflicts.
Da Entitätsklassen Attribute beschreiben die Strukturen der relationalen Datenbank-Tabellen und
Spalten haben, ist es möglich, diese Informationen zu benutzen, um neue Instanzen für Ihrer
Datenbank zu erstellen. Sie können die CreateDatabase ()-Methode auf der DataContext Aufruf LINQ
zu SQL-Konstrukt eine neue Datenbank-Instanz mit einer Struktur, die durch Ihre Objekte definiert. Es
gibt viele Gründe, warum Sie dies tun wollen könnte: Sie könnte eine Anwendung erstellen, die
automatisch installiert sich auf einem Kunden-System oder eine Client-Anwendung, die eine lokale
Datenbank auf ihre Offline-Status zu speichern muss. Für diesen Szenarien ist die CreateDatabase ()
ideal, vor allem, wenn ein bekannter Datenanbieter wie SQL Server Express 2005 verfügbar ist.
Allerdings kann die Daten-Attribute nicht codieren alles über eine vorhandene Datenbank-Struktur.
Der Inhalt der benutzerdefinierten Funktionen, gespeicherten Prozeduren, Triggern und CHECKEinschränkungen werden nicht durch die Attribute dargestellt. Die CreateDatabase ()-Funktion wird
nur eine Replik der Datenbank mit Informationen, die sie kennt, der wird die Struktur der Datenbank
und die Typen der Spalten in jeder Tabelle. Doch für eine Vielzahl von Datenbanken ist dies
ausreichend.
Unten ist ein Beispiel dafür, wie man eine neue Datenbank namens MyDVDs.mdf erstellt:
[Table(Name="DVDTable")]
public class DVD
{
[Column(Id = true)]
public string Title;
[Column]
public string Rating;
}
public class MyDVDs : DataContext
{
public Table<DVD> DVDs;
public MyDVDs(string connection) : base(connection) {}
}
Herunterladen