HowTo: LINQ to SQL debuggen

Da ich in einem aktuellen Projekt LINQ to SQL einsetze, kam irgendwann der Punkt: Wie debuggt man das ordentlich? Wie sieht das SQL aus, was der zum Server schickt?

1. Variante: Visual Studio

Die einfachste (und nahliegendste) Variante: Das debuggen mit Visual Studio

Einfach schauen, wie die Objekte gefüllt werden und schauen ob das überhaupt logisch passt.

Für ein Großteil der Fehler reicht dies erstmal, allerdings fragt man sich doch machmal, was LINQ to SQL eigentlich für einen SQL Statement produziert.

2. Variante: LINQ to SQL Debug Visualizer

Dafür gibts den LINQ to SQL Debug Visualizer. Es ist mir unbegreiflich, warum Microsoft so ein geniales Tool so versteckt.

3. Variante: DataContext.Log

Wer nicht immer den Debug Visualizer nutzen kann oder mag, hat Microsoft etwas in LINQ to SQL eingebaut: DataContext.Log 

In Konsolenanwendungen ist dies auch praktisch – allerdings ist dies in Klassenbibliotheken ist dies nicht so einfach möglich. Allerdings gibts hier eine kleine Klasse, welche ermöglicht, dass das generierte SQL in dem Debug Output Fenster sichtbar ist: Sending the LINQ To SQL log to the debugger output window

Ich werde sicherlich noch den einen oder anderen Post zum Thema LINQ to SQL schreiben – allerdings sind manche Probleme auch zum Teil vom Konzept so oder leicht "trickreich" ;)

Diese 3. Varianten machen aber das Leben schon etwas leichter.

HowTo: SQL Server Compact Edition mit LINQ to SQL verwenden

Auf der VSone habe ich das erste mal von den SQL Server in der Compact Edition (Version 3.5 ist momentan aktuell) gehört. In dem dazugehörigen Blogeintrag habe ich bereits einige der wichtigsten Angaben zu dem SQL Server Compact Edition geschrieben.

Kurzzusammenfassung:

- SQL DB für bis zu 4 GB Daten
- Bekannte Tools nutzbar
- Benötigt keine Installation etc.
- Relationen sind einstellbar

Kleines Problem: Geht LINQ to SQL?

Wer einmal mit LINQ to SQL gearbeitet hat, wird es sicherlich schick finden – jedenfalls schicker als das normale ADO.NET Thema. Wenn man das allerdings ausprobiert, bekommt man eine Fehlermeldung:

image

Erstmal die Entwarnung: Es geht – mit einem kleinen Trick.

Doch langsam: Vorbereitung

Als erstes benötigen wir eine "Local Database":

image

Diese SQL Server Compact Edition Database erkennt man an der "sdf" Endung. Nachdem wir dies gemacht haben, kommt so ein Dataset Dialog – den einfach ignorieren und schließen. 

Jetzt legen wir unsere Tabellen an – der Dialog ist ähnlich wie beim SQL Management Studio:

image

Nachdem wir  nun eine Company Tabelle ("Id" = Guid, "Name" = nvarchar) & Employee ("Id" = Guid, "Firstname" = nvarchar, "Lastname" = nvarchar, "CompanyId" = Guid) können wir über ein Kontextmenü die Verknüpfung zwischen den beiden Tabellen einstellen:

image

Das entsprechende Menü:

image

Nachdem wir das haben, kommen wir nun zum eigentlichen Teil:

LINQ to SQL einsetzen

Bei einer normalen MDF oder einer SQL Server Tabelle kann man den Designer nehmen, allerdings ist dies momentan bei einer Compact Edition DB nicht möglich.

Allerdings gibt es ein kleines Tool namens "SQLMetal.exe", welches normalerweise unter diesem Pfad zu finden ist:
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin

Die Datei "SQLMetal.exe" sowie die Config Datei habe ich einfach mit in mein Projektverzeichnis kopiert.

Danach wird es wie folgt aufgerufen:
SqlMetal.exe Database.sdf /dbml:Enterprise.dbml /namespace:SQLCompact /pluralize

Folgendes passiert: Die Database.sdf wird aufgerufen und es wird eine Enterprise.dbml erstellt – im Namespace "SQLCompact".

DBML dem Projekt hinzufügen

Damit man es im Projekt nutzen kann, muss man es noch einblenden:

image

… und am Ende sieht man sowas:

image

Müsste eigentlich bekannt vorkommen, oder? ;)

Beispiel: Datensatz einfügen

Der Datacontext kann entweder über die verschiedenen Parameter anders benannt werden oder ist im Standardfall gleich mit dem sdf Namen. Sodass wir nun mit dem Code eine neue Firma einfügen können:

image

Also alles nix neues – der Connectionstring kann vom Serverexplorer unter "Eigenschaften" ausgelesen werden oder von dieser Website.

Jetzt können wir alles anwenden, was wir z.B. hier oder hier kennengelernt haben.

Hier gibts noch ein paar LINQ Informationen.

Viel Spaß :)

[Download Source Code]

VSone – Tag 1 – WPF, Sync Framwork, LINQ to SQL, ASP.NET, Parallel Extensions

Der erste Tag der VSone ist vorbei und ich werde einfach mal ein kurzes Review geben. Vom organisatorischen her, ist es bis jetzt sehr gut verlaufen. Im großen IMAX zu sitzen, kostenlos Popcorn zu bekommen und dabei auf der großen Leinwand eine WPF Entwicklung zu sehen, ist schon sehr cool – auch wenn das für Normalsterbliche sicherlich ein seltsamer Anblick ist ;)

Die Verpflegung ist spitze (und kostenlos) und die Leute sind allesamt recht nett, auch wenn ich teilweise den Altersdurchschnitt etwas senke (wobei Andere ihn wiederrum steigern).

Die Sprecher sind allesamt sehr nett und kann man auch mal leicht etwas über sein Kummer und Leid erzählen.

Nun kurz zu den Themen, die ich heute besucht hatte – es gab pro Track noch mehrere andere Vorträge parallel – interessierte sollten die Blogs von den Sprechern im Auge behalten:

Erstes Vortrag: XAML, WPF & Expression Blend

Ein sehr praktischer Vortrag von Markus Egger – wie frischt man eine einfache “UI” mit WPF auf. Ausführlich wurde dort Expression Blend vorgestellt, was ich zwar schon kannte, allerdings waren mir einige Funktionen noch nicht bekannt. Wie schnell man aus einer langweiligen UI eine schicke Anwendung bastelt (natürlich nur mit dem “Glass Effekt”, hat mich doch etwas angespornt, dort mehr Zeit zu investieren.
Eine andere Sache, die mir dabei ins Auge fiel: In Expression Blend gibt es eine coole “Commandsuche”, man konnte die Menüs ähnlich wie in Vista einfach so durchsuchen und es wurde jeweils das richtige angezeigt – bin mal gespannt, wann das in Office zu finden ist.

Nächster Vortrag: WPF Datenbindung

Ebenfalls sehr praxisbezogen – was versteht man unter Datenbindung, was kann diese und wie sieht das aus. Was spielen “Dependancy Properties” und was sind die “INotification…” Schnittstellen (z.B. INotifyPropertyChanged) – dies waren unter anderem Hauptthemen. Vorgetragen war dies von Christian Nagel. Zusammen mit dem ersten Vortrag, in dem es mehr um das Design ging, hat dieser Vortrag mich besonders als Entwickler begeistert – und wird sicherlich mal den ein oder anderen Blogpost Wert sein.

Nächster Vortrag: Microsoft Sync Framework

Das Microsoft Sync Framework ist noch nicht allzu alt, jedoch bereits in VS 2008 zu finden. In dem Vortrag von Alexander Duggleby ging es nur um dieses Thema. Mich persönlich hat dieses Thema doch recht interessiert und war auch gut vorgetragen. An praktischen Anwendungsfällen fehlt es mir momentan noch, vor allem da mir es noch nicht 100% klar war, wie man die einzelnen Provider (es gibt von MS einige wenige vorgefertige Provider, z.B. einen Fileprovider, um Files zu synchronisieren) einsetzt – aber man stelle sich folgendes vor: Ich lege Bilder in meinen “Eigene Bilder” Ordner und es wird auf mein Flickr, MySpace etc. Profil geladen – und wenn ich mich auf einen anderen Rechner einlogge, kann dies ebenfalls wieder syncronisiert werden. Theoretisch sollte dies gehen. Das wäre mal ein nettes Projekt ;)

Nächster Vortrag: Parallele Programmierung

Zum Anfang war ich hier recht skeptisch, weil viel Zeit mit “nicht .NET Themen” verging – jedoch ergab es hinterher ein sehr rundes Bild, vorgetragen von Bernd Marquardt. Es standen 3 Hauptthemen an – “OpenMP“, was für C++ oder Fortran gedacht ist, “SIMD“, welches ein anderes Modell fährt als “OpenMP” und “LINQ Parallel“, was direkt für C#/.NET momentan als CTP verfügbar ist.

Grundgedanke von “OpenMP”: Leicht mehrere Prozessoren nutzen. Bei einer Schleife von “0-999″ macht Prozessor 1 “0-499″ und Prozessor 2 “500-999″. Dies wurde recht anschaulich demonstriert und vor allen ist der gebrauch recht einfach.
Als zweites kam “SIMD”, was mehr auf die Hardware geht und Prozessoreigenschaften nutzt. Es werden Daten in einem Array in einem Schritt bearbeitet – anstatt einzeln. Das ist zwar auch schick, weil Threads nicht eingesetzt werden, ist aber noch mehr an die Prozessoren gebunden (und der Aufruf ist hässlich)  das geht beides nur in C++ (oder OpenMP in managed C++).
“Parallel LINQ” geht einen ähnlichen Weg wie “OpenMP”, allerdings zum Teil mit einem eigenen Syntax. Primär werden hier anonyme Methoden genutzt und wer Lamda Expressions verstanden hat, der kann hier einiges an Performance rausholen. Allerdings momentan alles alpha, aber das ist sicherlich in nächster Zeit ein sehr wichtiges Thema.

Nächster Vortrag: Was kommt nach ASP.NET?

Ein sehr humorvoller Vortrag von Hannes Preishuber. Auch wenn er versprochen hat, dass man nichts davon mitnimmt, hab ich doch die ein oder andere Sache aufgeschnappt. Mir waren die ASP.NET Futures, Silverlight, ASP.NET 3.5 Extensions usw. zwar schon ein begriff, allerdings hatte ich nie das Media Control ausprobiert oder Dynamic Data Pages ausprobiert (was sehr cool ist) und auch LINQ to SQL nutzt.

Letzter Vortrag für heute: LINQ to SQL

Christoph Wille stellt LINQ to SQL vor. Zwar hatte ich selber damit schon rumgespielt, allerdings war es interessant zu wissen, wo bestimmte Sachen “schwierig” umzusetzen sind oder schlicht und einfach langsam. Als generellen Tipp habe ich mitgenommen, den SQL Server Profiler laufen zu lassen, damit man sieht, was LINQ eigentlich am SQL Server veranstaltet. Den Blog von Scott Guthrie und Mike Toulty sollte man zudem ebenfalls besuchen. Morgen (oder heute wenn sie diesen Post lesen werden), wird das ADO.NET Entity Framework vorgestellt – und die Unterschiede zu LINQ to SQL ebenfalls dargestellt.
Einige Stichpunkte noch kurz am Rande erwähnt: Die Integration von Stored Procedures (gibt auf dem Blog von Scott und Mike noch wesentlich mehr Informationen), “CompiledQuerys” für performance Gewinn, das nachträgliche Laden von Kindelementen und die damit verbundenen Probleme (DataLoad Options erlaubt nur die nächste Ebene – man kann wohl keine 2 Ebenen übergehen – wenn man danach sucht, findet man was ich meine ;) ).

Zwischenfazit

Sehr schicke Veranstaltung.Ich werde in den nächsten Tagen/Wochen die Themen zum großen Teil aufgreifen.

Den Tag zwei werde ich sicherlich auch noch niederschreiben :)

GUIDs vs. auto-increment IDs

Ich oute mich mal als ein “ehemaliger” PHP Entwickler. MySQL ist natürlich die Standarddatenbank für PHP Anwendungen. Man macht über phpmyadmin (ich war noch jung und kannte nichts anderes ;) ) seine Datenbanken und wie es sich so gehört, hat jeder Datensatz in einer X-beliebigen Tabelle eine eindeutige ID. Die “ID” Spalte wird einfach ein Integer und setzt diese als den primären Schlüssel und schaltet noch ein, dass diese Spalte automatisch hochgezählt werden soll.

So für sich gesehen, ist es recht simpel und funktioniert gut. Dieses Prinzip wird aber dann schwieriger umzusetzen, wenn bestimmte Abhängigkeiten zu anderen Tabellen bestehen sollen.

Aber erstmal ganz langsam… unser Szenario:

Wir haben Produkte, welche wir in eine “Products” Tabelle speichern:

  • Id
  • Name
  • Price

Jedes Produkt kann ähnliche Produkte haben, welche in einer “ReleatedProducts” Tabelle gespeichert wird:

  • Id (diese Spalte könnte man auch weglassen – spielt aber jetzt keine große Rolle).
  • ProductFirstId
  • ProductSecondId

Wir haben jetzt ein Anwendung, in dem wir 2 Produkte erstellen wollen und diese in Beziehung setzen. Wenn wir dies jetzt mit “normalen” autoincrement IDs umsetzen, wird es etwas knifflig…

Warum? Das geht ganz einfach…

Natürlich geht das mit dem autoincrement IDs. Allerdings muss man quasi folgende Schritte machen:

  1. Erstes Produkt erstellen
  2. Erstelltes Produkt in die DB speichern
  3. Eben erstellte ID als Rückgabewert bekommen
  4. Zweites Produkt erstellen
  5. Erstelltes Produkt in die DB speichern
  6. Eben erstelle ID als Rückgabewert bekommen
  7. Beziehung setzen mit den beiden Rückgabewerten

Das ist nur teilweise schön – das liegt nunmal an der Natur von autoincrement IDs:

Die Verwaltung der eindeutigen IDs wird der Datenbank überlassen!

Man kann nicht 100% die nächste ID erraten. Wenn man nun einen Datensatz mit einem anderen Verknüpfen möchte, muss man vorher immer die DB befragen. Falls in der Zwischenzeit was schief geht und auch keine referenzielle Integrität angeschaltet ist, bleiben im schlimmsten Falle leichen übrig.

Was gibts für eine Alternative?

GUIDs bieten eine nette Abhilfe und sind insbesondere im Microsoft Umfeld stark vertreten.

Im .NET Framework gibt es direkt eine GUID Klasse zum Erzeugen solcher IDs. Im SQL Server ist der Datentyp “uniqueidentifer” dafür vorgesehen.

Was sind die Vorteile?

  • Die Kontrolle der eindeutigen IDs kann direkt im Programm gesteuert werden.
  • Die Daten brauchen erst zur DB gespeichert werden, wenn dies nötig ist.
  • Bei einer evtl. Zusammenführung von 2 Tabellen hat man das ID Problem nicht.

Was sind die Nachteile?

  • Performance? Jedenfalls aus dem Microsoft SQL Lager hört man sowas nicht – zwar ist eine GUID länger als eine “normale” ID, allerdings fällt dies kaum ins Gewicht.
  • Ids sind etwas unschöner und man kann schlechter nachvollziehen und mal einzelne Daten Testen:
    • Webseiten mit …?id=2312 sind (wie z.B. bei Wordpress) durch konkrete Titel ersetzt wurden – die IDs “sieht” der Benutzer (oder die Suchmaschine) kaum. Das man einzelne Daten schlechter Nachverfolgen kann, ist allerdings wahr.

Demoprojekt nötig?

Ich habe ein Demoprojekt mit VS 2008 Express Edition und LINQ to SQL erstellt – dort wird einmal mit GUIDs und ohne GUIDs gearbeitet – allerdings habe ich nur den GUID Teil bis zum Ende geführt – weil es für mich simpler war ;)

Der ConnectionString muss natürlich angefasst werden:

[ Download Democode ]

Buchüberblick Januar 2008 – .NET 3.5, ASP.NET 3.5, AJAX, ADO.NET, LINQ

Das .NET Framework in der Version 3.5 ist schon eine Weile draußen und da ich selber immer auf der Suche nach aktuellen Büchern bin, hier eine aktuelle Auswahl oder kommenden Büchern – Haupthema ist ASP.NET 3.5, .NET 3.5, AJAX oder LINQ.

Deutschsprachige:

Englischsprachige:

Das ganze natürlich noch als schickes Widget ;)

HowTo: O/R Mapper LINQ to SQL – Einführung in den Designer & 1:N Beziehungen

Im letzten HowTo rund um LINQ to SQL ging es mehr darum, ein Grundverständnis zu vermitteln. Das manuelle Mapping ist eine Methode, wie man LINQ to SQL nutzen kann – mit Visual Studio 2008 (selbst in der Express Edition) ist auch ein LINQ to SQL Designer Template mit dazugekommen:

image

Variante A: Unser einfaches Costumer Beispiel diesmal mit dem Designer

Schritt 1: .dbml Datei erstellen

Nachdem eine solche “xxx.dbml” Datei erstellt hat – in unserem Beispiel wollen wir wieder die Costumer Tabelle aus der Northwind Database auslesen (Installationsanleitung), daher “Costumers.dbml” – sieht man nur eine solche Meldung:

image

Schritt 2: DB Verbindung hinzufügen

Über den DB Explorer legen wir eine Verbindung zu einem Microsoft SQL Server Database File her:

image

Danach wählen wir unserer Northwind.MDF (Standardinstallationspfad: “C:\SQL Server 2000 Sample Databases\NORTHWND.MDF”).

Schritt 3: Tabellen auf die Designer Oberfläche ziehen

Im nächsten Schritt ziehen wir einfach die jeweiligen Tabellen die wir haben wollen (in unserem Fall die Costumers) auf die Oberfläche:

image

Visual Studio 2008 erstellt daraufhin folgende Sturktur:

image

  • Customer.dbml.layout = XML Beischreibung
  • Costomer.dbml.cs = Der eigenliche Code mit sämtlichen Attributen und dem

Allerdings landet in der Customer.designer.cs nicht nur wie bei dem vorherigen HowTo die MappingInformationen der Tabelle, sondern es wird zugleich auch gleich eine spezielle DataContext Klasse “CostumerDataContext” gebildet:

 

image

Die Interfaces INotifyPropertyChanging & INotifyPropertyChanged wurden ebenfalls implementiert – WPF benutzt dies zum Beispiel im Kontext mit Databinding, was hier ja quasi ebenfalls passiert.

 Schritt 4: Daten abrufen, verändern und löschen

Jetzt können wir die selben Befehle ausführen, wie in unserem anderen Beispiel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;

namespace LinqToSqlDesigner
{
    class Program
    {
        static void Main(string[] args)
        {
            CustomerDataContext context = new CustomerDataContext();
            Table<Customer> customerTable = context.GetTable<Customer>();

            var customerResult = from customer in customerTable
                                 where customer.City == "London"
                                 select customer;

            foreach (Customer oneCustomer in customerResult)
            {
                Console.WriteLine(oneCustomer.CompanyName);
            }

            Console.ReadLine();

        }
    }
}

Diese Variante ist so nicht zu empfehlen – jedenfalls sollte die dbml nicht nach einer Tabelle genannt werden. In der nächsten Variante sieht man gut, wie man eine größere Datenbank mit einer dbml abbilden kann.

Variante B: 1:N Beziehungen mit dem Designer

Das war ja jetzt nicht sonderlich spannend, allerdings ist es auch möglich direkt im Designer 1:N Beziehungen vorzunehmen.
Dazu erstellen wir uns wieder unsere LINQ to SQL “Northwind.dbml” und wählen jetzt mal den Customers und die Orders aus:

image
Der “Pfeil”, also die Association, zwischen CustomerID aus der Customer Klasse/Tabelle und der CustomerID aus der Order Klasse/Tabelle wird automatisch gebildet (wahrscheinlich weil diese Assoziation bereits im DB System bekannt ist – ob das Mapping auch anhand des Namens erfolgt, habe ich momentan nicht getestet). Man kann diese Assoziation allerdings auch selber bearbeiten, indem man in den Eigenschaften des “Pfeils” schaut:

image

Eigenschaften:

image

Der LINQ to SQL O/R  Designer besitzt ebenso eine eigene Toolbox, mit welchen man auch einige eigene Sachen erstellen kann:

image

Wenn man nun noch zwei weitere Tabellen in den Designer zieht, macht das langsam einen recht schicken Eindruck:

image

Dadurch habe wir innerhalb weniger Minuten bereits folgende fertige Klassen zur Verfügung:

image

Jetzt machen wir mal wieder unsere Abfrage um zu zeigen, was man damit machen kann:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;

namespace LinqToSqlDesigner
{
    class Program
    {
        static void Main(string[] args)
        {
            NorthwindDataContext context = new NorthwindDataContext();
            Table<Customer> customerTable = context.GetTable<Customer>();

            var cust = from c in customerTable
                       where c.City == "London"
                       select c;

            foreach(Customer customer in cust)
            {
                Console.WriteLine(customer.CompanyName);
                if(customer.Orders.Count > 0)
                {
                    Console.WriteLine("Customer Orders");
                    foreach(Order order in customer.Orders)
                    {
                        if(order.Order_Details.Count > 0)
                        {
                            Console.WriteLine(" - " + order.Order_Details[0].Product.ProductName);
                        }
                    }

                }
            }

            Console.ReadLine();
        }
    }
}

Hierbei holen wir uns erst alle Kunden aus “London”, welche wir auf der Konsole ausgeben. Danach prüfen wir, ob der Kunde eine Bestellung offen hat und welche dazugehörige Produkte er dazu gekauft hat. Resultat:
image

Was verbirgt sich hinter den 1:N Beziehungen?

Wenn wir jetzt mal in den generierten Code reinschauen, z.B. bei der Customer Klasse, finden wir folgenden Abschnitt:

      private EntitySet<Order> _Orders;

        public Customer()
        {
            this._Orders = new EntitySet<Order>(new Action<Order>(this.attach_Orders), new Action<Order>(this.detach_Orders));
            OnCreated();
        }

Ein “EntitySet” (Streng Typisiert) dient dabei als Datenspeicher der Beziehungsdaten. Das Gegenstück von EntitySet ist EntityRef.

Schematisch wäre dies grob so:

image

Der Customer hat Orders in seinem EnitySet gespeichert. Die Orders halten wiederrum die Verweise als EntityRef fest. Nachzulesen lässt sich das (und auch das Thema Many-to-Many-Relations, auch in der MSDN: How to: Map Database Relationships (LINQ to SQL)

Natürlich kann man auch mit dieser Basis die ganzen CRUD Sachen aus diesem Blogpost machen.

Fazit

LINQ to SQL bietet einen sehr netten Designer, mit dem man schnell zu Ergebnissen kommt. Wie ich selber aber gemerkt habe, ist sicherlich erst eine Grundsätzlich Einarbeitung in den Abfrage Syntax von LINQ notwenidig um gute und performante Abfragen zu gestalten.

Jeder der mit Microsoft SQL und .NET 3.5 zutun hat, sollte sich die Möglichkeiten von LINQ to SQL nicht entgehen lassen!
Es gibt sicherlich noch mehr Themen rund um LINQ to SQL – das war ja auch erst der zweite Post zu diesem Thema ;)

Downloaden könnt ihr das Testprojekt auch, allerdings musste ich aus Platzgründen die Northwind.mdf (und die log Datei) aus dem Verzeichnis entfernen – installiert die Northwind Datenbank einfach und kopiert die beiden Datein mit in das Verzeichnis. Visual Studio 2008 (mit Administrator Rechten) ist bei .NET 3.5 Pflicht.

[ Download Sourcecode ]

HowTo: O/R Mapper LINQ to SQL – Einführung & einfaches manuelles Mapping

LINQ to SQL ist Microsofts LINQ Provider für das Hauseigene Datenbanksystem SQL Server 2005 (und 2008 – und noch mehr?). Das Konzept von LINQ sollte man vorher bereits verstehen – hier eine Kurzeinführung:

LINQ? Was ist das?

In jeder Applikation arbeitet man mit Daten, Objekten und noch mehr Ansammlungen von Objekten. Externe Datenquellen (XML, Datenbanken) muss man über ihre jeweiligen Abfragesprachen ansprechen – SQL oder XPath. Objektcollections oder komplexe Objekte in C# 2.0 haben kein solches Abfragesystem gehabt – man musste über Foreach-Schleifen die Collections durchgehen und dann immer wieder mit dem Suchwort vergleichen.

Hier tritt LINQ ins Spiel – LINQ erlaubt es, .NET Objekte mit einem SQL ähnlichen Syntax zu durchsuchen, allerdings mit allen Vorteilen die Visual Studio und Objekte bieten: Einfaches Debugging möglich, man arbeitet mit direkten Objekten und die IntelliSense hilft natürlich auch kräftig. Am Ende einer LINQ Abfrage kann man direkt ein “var” Objekt erzeugen (was das ist, wurde hier besprochen). Da man nicht nur Objekte so durchsuchen kann, sondern auch andere Daten, zeigt z.B. der LINQ to XML oder der LINQ to SQL Provider.

Microsoft hat LINQ so gestaltet, dass viele solche Provider erstellt werden können, sodass es momentan z.B. folgene Provider in Entwicklung befinden:

Wir beschäftigen uns heute mit LINQ to SQL – dabei werden wir einmal nur in einer sehr einfachen Abfrage LINQ to SQL anschauen und später ein etwas komplexeres Mapping per Hand vornehmen.
Der LINQ to SQL Designer wird später behandelt!

Vorbereitung & Dokumente

Ich halte mich hier an das Hand on Lab von Microsoft, welches es hier kostenlos zum Runterladen gibt. Desweiteren benötigen wir die Northwind Datenbank – eine Installationsanleitung gibt es hier. Natürlich benötigen wir Visual Studio 2008 Express Edition. Visual Studio und das SQL Management Studio sollten im Administratormodus laufen (und bei meinem Demoprojekt muss hinterher der ConnectionString angepasst werden)

Erster Schritt mit LINQ to SQL

Als erstes benötigen wir die System.Data.Linq.dll, damit wir die entsprechenden Namespaces verwenden können.
Danach erstellen wir unser erstes Mapping (wie bereits oben erwähnt, werden wir hier ein manuelles Mapping vornehmen um das System besser zu verstehen).

Schauen wir uns mal die Tabellen der Northwind Datenbank an:

image

Wir wollen einfach eine kleine Konsolen-Applikation haben, welche die Kundendaten bearbeitet.

LINQ to SQL – Schritt 1: Das Mapping

Die Kundendaten stehen in der “Customers” Tabelle – daher legen wir eine “Customers” Klasse an:

image

Dabei verwenden wir den Namespace “System.Data.Linq.Mapping” in der “Customer.cs”.
Das Mapping erfolgt über das Zuweisen von Attributen zu der Klasse (das Table Attribut) und den Properties (das Column Attribut).

Quellcode sagt meist mehr als tausend Wort:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq.Mapping;

namespace LinqToSqlTest
{
    [Table(Name = "Customers")]
    public class Customer
    {
        [Column(IsPrimaryKey = true)]
        public string CustomerID;

        [Column]
        public string Address { get; set; }

        private string _City;

        [Column(Storage = "_City")]
        public string City
        {
            get { return this._City; }
            set { this._City = value; }
        }
    }

}

Erklärung:

Das Mapping erfolgt durch die Zuweisungen der entsprechenen Attribute (Attribute-based Mapping) – beim Klassennamen “Customer” wird das Tabel Attribut verwendet – das “Name” dort verbindet die “Costumers” Tabelle mit dieser Klasse. Der Name kann auch weggelassen werden, wenn DB Tabellennamen == Klassenname (in unserem Fall fehlt ein “s” im Klassennamen).
Die einzelnen Spalten werden über das Column Attribut den Properties zugewiesen (MSDN Artikel: How to: Represent Columns as Class Members (LINQ to SQL)).

Das Mapping erfolgt hierbei wieder anhand des Namens der DB Spalte und des entsprechenden Properties. Falls dies abweicht, gibt es noch ein Name Property (wie bei dem Tabellen Attribut oben).
Standardmäßig wird dann der DB Wert in das Property geschrieben, welches zugewiesen wurde. Wenn man als Datenhalterobjekt z.B. ein privates Property verwenden möchte, kann man dies über das “Storage” Property zuweisen.

LINQ to SQL – Schritt 2: Die DB Abfrage

In unserer Program.cs wollen wir nun eine einfache Abfrage der Kunden einbauen.

Als erstes müssen wireinen DataContext aufbauen, welchen wir den ConnectionString übergeben. Danach veranlassen wir den Datacontext das Mapping über die GetTable Methode zu “starten”:

DataContext db = new DataContext(@"Data Source=REMAN-NOTEBOOK\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True");
Table<Customer> Customers = db.GetTable<Customer>();

Um zu sehen, was für SQL Befehle ausgeführt werden, gibt es auch ein Log Property, wo wir unsere Console anheften:

db.Log = Console.Out;

Jetzt zur eigentlichen Abfrage mit LINQ (und der Ausgabe):

            var custs =
                from c in Customers
                where c.City == "London"
                select c;

            foreach (var cust in custs)
            {
                Console.WriteLine("ID={0}, City={1}, Address={2}", cust.CustomerID, cust.City, cust.Address);
            }

Mit der Abfrage werden alle Kunden aus London in “var custs” (das Konzept von “var” ist hier beschrieben) gespeichert.

Hinweis: Mit GetTable<…>() werden noch keine Daten vom SQL Server geholt – erst beim Zugriff auf Customers wird die Abfrage gemacht (Siehe Video).

Ergebnis:

image

Vollständiger Quellcode:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace LinqToSqlTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Use a standard connection string
            DataContext db = new DataContext(@"Data Source=REMAN-NOTEBOOK\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True");
            Table<Customer> Customers = db.GetTable<Customer>();

            db.Log = Console.Out;

            // Query for customers in London
            var custs =
                from c in Customers
                where c.City == "London"
                select c;

            foreach (var cust in custs)
            {
                Console.WriteLine("ID={0}, City={1}, Address={2}", cust.CustomerID, cust.City, cust.Address);
            }

            Console.ReadLine();
        }
    }
}

LINQ to SQL – Schritt 3: Änderung an der DB vornehmen – einen neuen Eintrag anfügen

Ein neuen Eintrag hinzufügen geht recht schnell:

            Customer newCostumer = new Customer();
            newCostumer.City = "Dresden";
            newCostumer.Address = "Riesaer Str. 5";
            newCostumer.CustomerID = "DDMMS";
            newCostumer.CompanyName = "T-Systems MMS";

            Customers.InsertOnSubmit(newCostumer);
            db.SubmitChanges();

Hierbei musste ich aber noch ein neues Property für den Costumer hinzufügen: CompanyName – weil dieser (ebenso wie CustomerID) NOT NULL sein muss:

image

Wie oben zu sehen ist, wird einfach ein neuer Customer angelegt und über Customers wird dieses Objekt an (die wir über den DataContext bekommen haben) die Methode “InsertOnSubmit” weitergereicht und dort vorgemerkt, beim “Submitten” in die DB eingetragen zu werden.

Das “Submitten” wird über “SubmitChanges” vom DataContext gestartet.

LINQ to SQL – Schritt 4: Änderung an der DB vornehmen – einen Eintrag editieren

Das Dateneditiere wird direkt an den Objekten vorgenommen:

            var upObjects = from test in Customers
                             where test.CompanyName == "T-Systems MMS"
                             select test;

            upObjects.First().Address = "Straße";
            db.SubmitChanges();

Hier erfolgt nur ein “SubmitChanges” und der erste gefundene Eintrag (über First()) wird entsprechend geändert.

LINQ to SQL – Schritt 5: Änderung an der DB vornehmen – einen Eintrag entfernen

Den eben angelegten Eintrag kann man auch wieder entfernen:

var delObjects = from test in Customers
                             where test.CompanyName == "T-Systems MMS"
                             select test;

            Customers.DeleteOnSubmit(delObjects.First());
            db.SubmitChanges();

Hier holen wir uns einfach unser eben erstelltes Objekt wieder und nehmen wieder über die First() Methode das erste gefundene (da es sowieso nur einen Eintrag dort gibt) Ergebnis. Löschen kann man es einfach über “DeleteOnSubmit“.

Der gesamte Source Code (download weiter unten):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace LinqToSqlTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Use a standard connection string
            DataContext db = new DataContext(@"Data Source=REMAN-NOTEBOOK\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True");
            Table<Customer> Customers = db.GetTable<Customer>();

            // Logging
            db.Log = Console.Out;

            // CREATE
            Customer newCostumer = new Customer();
            newCostumer.City = "Dresden";
            newCostumer.Address = "Riesaer Str. 5";
            newCostumer.CustomerID = "DDMMS";
            newCostumer.CompanyName = "T-Systems MMS";

            Customers.InsertOnSubmit(newCostumer);
            db.SubmitChanges();

            // READ
            var custs =
                from c in Customers
                where c.City == "Dresden"
                select c;

            // Debugging - Console.WriteLine
            foreach (var cust in custs)
            {
                Console.WriteLine("ID={0}, City={1}, Address={2}", cust.CustomerID, cust.City, cust.Address);
            }

            // UPDATE
            var upObjects = from test in Customers
                             where test.CompanyName == "T-Systems MMS"
                             select test;

            upObjects.First().Address = "Straße";
            db.SubmitChanges();

            // Debugging - Console.WriteLine
            foreach (var cust in custs)
            {
                Console.WriteLine("ID={0}, City={1}, Address={2}", cust.CustomerID, cust.City, cust.Address);
            }

            // DEL
            var delObjects = from test in Customers
                             where test.CompanyName == "T-Systems MMS"
                             select test;

            Customers.DeleteOnSubmit(delObjects.First());
            db.SubmitChanges();

            Console.ReadLine();
        }
    }
}

Wichtiger Hinweis:

Die Anwendung läuft nur einmal durch, da die CustomerID eindeutig sein muss – aber das hier soll nur der Anfang sein um LINQ to SQL mehr zu verstehen.
Das die LINQ Abfragen hier sicherlich verbesserungswürdig sind, ist eine andere Sache (und wird sicherlich in einem anderen HowTo unterkommen).

Das Mapping erfolge hier sehr einfach über das manuelle dazu schreiben der Attribute – es gibt einen direkten LINQ to SQL Designer.

Empfehlung an alle die sich für LINQ to SQL interessieren:

Das HoL über LINQ to SQL, was ich bereits oben erwähnt hatte, geht noch auf andere Aspekte ein. Einige davon werde ich sicherlich selber noch in HowTos packen, allerdings bis dahin dient es als gute Anlaufstelle zu Themen wie:

  • LINQ to SQL Designer
  • Beziehungen zwischen einzelnen Tabellen modellieren
  • Streng-typisierte DataContext Objekte
  • Transaktionen (Sehr interessantes Thema!)
  • Mapping a stored procedure

Das Thema wird sicherlich bei mir noch häufiger anzutreffen sein (und das war heute mein erster richtiger Berührungspunkt mit LINQ to SQL – ich hoffe, dass das Konzept (und wie es intern tickt) etwas klarer ist ;) )

[ Download Demoanwendung ]