Lektionsbloggen: Linq to Sql

Lektionens datum: 6/8 2008 
Lektionens rubrik: Linq (2/2)
Lektionsbloggens fokus: Linq to Sql och Lambda-expressions

Idag handlade lektionen om Linq to Sql och Lambda-expressions.

Förutsättningar
Innan vi kan sätta igång måste vi få en databas på plats. Mina exempel kommer använda sig av exempeldatabasen Northwind. Ladda hem Northwind, packa upp zip-filen på en plats där du vet var du har den. Observera att detta exempel kommer att kräva att du har Sql Server eller Sql Server Express.

Skapa ett nytt windows-projekt. Välj sedan Add -> Existing Item..., bläddra fram till och välj NORTHWND.mdf. Nu kommer fönstret Data Source Configuration Wizard att öppnas, ignorera det genom att välja Finish. Svara "Ja" på varningen som kommer upp. Nu syns följande i Solution Explorer;
image

Högerklicka på .xsd-filen och välj att ta bort denna. Vi ska nämligen använda Linq to Sql och inte ett dataset (bloggen lär återkomma till detta någon annan gång).

Linq to Sql
Nu när vi har databasen på plats ska vi ordna så att Visual Studio genererar klasser för de tabeller i databasen som vi vill jobba med. Välj Add -> New Item... och välj mallen LINQ to SQL Classes;
image

Döp filen till Northwind.dbml.

När du klickat Add kommer du se den nya noden Northwind.dbml i Solution Explorer. Under den finns filerna Northwind.cs, Northwind.dbml.layout och Northwind.designer.cs.

I Visual Studios huvudfönster är Northwind.dbml öppet och ett fönster som är delat i två vertikala avdelningar visas. Till den vänstra kan du dra in tabeller från databasen och i den högra kan man dra in stored procedures. För varje objekt som dras in genereras kod som hamnar i Northwind.designer.cs. Dra in tabellerna Customers och Orders i Northwind.dbml;
image
Dra tabeller från Server Explorer till Northwind.dbml:s vänstra halva.

Gränssnitt i Form1
Nu ska vi göra ett enkelt gränssnitt i Form1.cs. Öppna designläget, gör fönstret lite större och dra ut en DataGridView som du ger namnet gvOutput samt en knapp med namnet btnShowCustomers och texten "Visa alla kunder";
image

Linq to Sql-fråga
Dubbelklicka på knappen och skriv kod för att ta fram alla kunder;

private void btnShowCustomers_Click(object sender, EventArgs e)
{
    NorthwindDataContext db = new NorthwindDataContext();

    var output = from customer in db.Customers
                 select customer;

    gvOutput.DataSource = output;
}

Först skapar vi en referens, db, av typen NorthwindDataContext. Detta är en typ som finns deklararerad i Northwind.designer.cs och den krävs för att vi ska kunna accessa de tabeller som vi valt att inkludera i .dbml-filen. Därefter kan vi skriva en enkel Linq-fråga och koppla resultatet till DataGridView-kontrollens DataSource-property.

I just det här fallet är Linq-syntaxen helt onödig. Eftersom vi vill visa alla kunder hade vi lika gärna kunnat skriva så här, eftersom db.Customers är en samling av Customer-objekt.

gvOutput.DataSource = db.Customers;

Men säg att vi vill visa endast kunder från Sverige istället. Lägg in en ny knapp, döp den till btnSwedishCustomers och ge den texten "Visa alla kunder från Sverige". Flytta ut deklarationen av NorthwindDataContext-objektet db direkt i klassen Form1 (så att vi har tillgång till den i alla metoder). Implementera sedan följande kod i knappens Click-event;

private void btnSwedishCustomers_Click(object sender, EventArgs e)
{
    var output = from customer in db.Customers
                 where customer.Country == "Sweden"
                 select customer;
    gvOutput.DataSource = output;
}

Här villkorar vi alltså resultatet genom att kräva att customer.Country=="Sweden".

Lambda-variant
Exakt samma filtrering kan också göras med hjälp av mer klassiska metodanrop samt lambda-uttryck;

var output = db.Customers.Where(customer => customer.Country == "Sweden");

Här anropas metoden Where direkt på db.Customers. Where är egentligen en metod som tar emot en delegatparameter. Denna delegatparameter kräver ett customerobjekt som parameter (eftersom vi anropar den på en instans av en Customer-samling) och returnerar bool. Men med hjälp av lambda-operatorn => kan vi direkt deklarera inputparametern (före operatorn) och sedan kod som kan utvärderas till true eller false (efter operatorn). customer (med litet c!) är alltså vår identifierare/input-parameter. I Where-metoden kommer den i en loop att anta varje Customer-objekt som finns i db.Customers. Om objektets Country-property har värdet "Sweden" kommer det objektet att inkluderas i resultatet, annars inte.

Låt användaren filtrera ordrar per kund
Vi ska nu göra ett mer avancerat/dynamiskt exempel där användaren får välja bland alla kunder och när han valt en kund visas en lista över kundens ordrar. Börja med att dra in en ComboBox i fönstret och namnge den ddlCustomers. Implementera sedan följande kod i Form1-klassens constructor (så att ComboBoxen fylls direkt när fönstret öppnas);

foreach (Customer c in db.Customers)
{
    ddlCustomers.Items.Add(c);
}

Om du nu kör programmet kommer du få en dropdownlista där samtliga val visar exakt samma text, nämligen "applikationensnamn.Customer". Detta beror förstås på att Items i en ComboBox är av typen Object och när ComboBoxen visar upp sitt innehåll förlitar den sig på att den faktiska typen har en ToString-metod som ger objektet en vettig strängrepresentation. Om en sådan inte finns körs Object-klassens version som kort och gott skriver ut namnet på objektets typ. För att fixa detta öppnade jag filen Nortwhind.designer.cs, letade mig fram till deklarationen av klassen Customer och la till följande implementation av ToString;

public override string ToString()
{
    return string.Format("{0}: {1}", CustomerID, ContactName);
}

Om du studerar klassen Customer kommer du se att den har publika properties som heter CustomerID och ContactName (bland flera andra) och dessa properties get- och set-block tar hand om hanteringen för hur värdena hämtas från databasens tabellceller. Jag åker snålskjuts på dessa och får således en vettig strängrepresentation för varje Customer.

Låt oss nu titta på den kod som ska implementeras i ComboBoxens SelectedIndexChanged-event;

private void ddlCustomers_SelectedIndexChanged(object sender, EventArgs e)
{
    Customer selectedCustomer = ddlCustomers.SelectedItem as Customer;
    var output = from order in db.Orders
                 where order.Customer == selectedCustomer
                 select order;
    gvOutput.DataSource = output;
}

Vi börjar med att typa om ComboBoxens SelectedItem (som är av typen Object) till en Customer och placerar den i referensen selectedCustomer. Därefter kan vi använda den referensen i vårt Linq-uttryck för att filtrera ut just de ordrar som hör till denna kund.

image

Jag stannar här för idag. Lektionen tog även upp hur vi kan spara in och ta bort poster i databasen, t.ex. genom att skapa ett nytt object av typen Customer, och sedan på NortwhindDataContext-objektet db anropa db.Customers.InsertOnSubmit och därefter db.SubmitChanges().

Imorgon blir det lite Linq to XML, samt serialisering.

Ladda hem "the Lektionsbloggen Solution"
Tips! I den nedladdningsbara versionen finns en del kommentarer som stöd när du läser koden.

No comments :

Post a Comment