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) {} }