Lektionsbloggen: Gratis E-learning från Microsoft och InnerWorkings

Hela denna vecka handlar utbildningen om WPF - Windows Presentation Foundation. Istället för att blogga om detta tänkte jag tipsa om att Microsoft E-learning erbjuder en gratis elearning-introduktion om WPF på www.microsoftelearning.com. Så här hittar du den:

  • Logga in med ditt Windows Live ID.
  • Klicka på "My Learning" i vänstermenyn".
  • Du kommer nu till en sida med lite annorlunda layout än tidigare. I högerspalten listas "Gratisprodukter", efter några poster finns länken "Visa alla...". Klicka på den.
  • Scrolla ner till "Collection 6261: Developing Rich Experiences using Microsoft .NET Framework 3.5 & Visual Studio 2008". Välj den.
  • På nästa sida listas tre kurser ("Clinics"). Den i mitten handlar om WPF. Klicka på den.
  • Kryssa i kryssrutan uppe till höger och klicka på knappen "Aktivera kostnadsfritt innehåll". Observera att du kommer ha tillgång till kursen i max ett år från att du aktiverar den.
  • Nu kommer du till kursens sida och du kan köra igång.

Microsoft Elearning-kurserna är lite småsega och det är ganska mycket att läsa. Men jag tycker ändå de är ett bra avbrott till att läsa i boken och de innehåller bra exempel och ganska roliga labbar (även om de också är lite sega, då man måste ansluta via webbläsaren till en labbdator nånstans hos Microsoft). Igår hade jag dock problem med diverse felmeddelanden som dök upp, men jag hoppas att det ska vara löst till nu. Kolla även in de andra gratiskurserna som finns här, t.ex. finns det en om Asp.NET AJAX.

En betydligt bättre labbmiljö erbjuder utbildningsföretaget InnerWorkings, nämligen din egen dator med Visual Studio etc. Även de erbjuder ett antal gratiskurser, bland annat om C# och Linq. Om du går till www.innerworkings.com så hittar du gratiskurserna så här:

  • Klicka på "Trial" i huvudmenyn till höger.
  • Fyll i formuläret för att skapa dig ett konto och välj sedan en av de gratis trial-kurserna. (Det kan verka som att man bara får välja en, men du kan återvända till denna sida senare, välja länken "simply sign-in" och välja ytterligare en av kurserna.)
  • Följ sedan instruktionerna. Du kommer att få ladda hem ett program som körs lokalt på din dator där kursen verkar. Dessutom installeras en Visual Studio Add-On som gör att du under labbarna har tillgång till ett fönster inne i Visual Studio med instruktioner(!).
  • Dessutom kan du logga in via www.innerworkings.com/promotions för ytterligare ett antal gratiskurser om Linq, WPF och AJAX. Du kommer först till WPF-sidan (just nu), välj bland de andra i rutan "Current Promotions". När du valt, klicka på den orange "Register Now"-rutan. Här kan du återigen välja "Simply sign-in" om du redan skapat ditt konto och därefter ladda ner kursen. Den här sidan lär det säker finnas anledning att återkomma till då och då för att se om de erbjuder nya kurser gratis. Jag kollade vad en kurs kostar om man vill betala, kommer inte ihåg vilken nu, och priset var $29.99 vilket jag tyckte var lite väl saftigt...

Jag gjorde Linq-kursen igår och det var verkligen kul. Jättebra småövningar att komplettera utbildningen med och kul labbmiljö där man får ett påbörjat projekt med ett avgränsat antal metoder som ska implementeras. När man är klar kan man dels få koden "rättad" av en "code checking enginge" och dessutom kan man öppna upp en facit-version av hela lösningen för att se hur de har tänkt sig att man ska skriva koden (den automatiska rättningen ger rätt även om man inte gjort exakt som facit-lösningen så det kan verkligen vara värt att kika på den!).

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.

Lektionsbloggen: LINQ och ny C#-syntax

Lektionens datum: 5/8 2008
Lektionens rubrik: Linq (1/2)
Lektionsbloggens fokus: LINQ to Objects och diverse ny C#-syntax

Dagens lektion introducerade LINQ och demade också ett antal nya syntax-möjligheter i C#.

Automatiska properties
Här följer två varianter på exakt samma klass. I exemplet till höger används C# 2008:s nya sätt att skriva en property som automatiskt ger en privat datamedlem som kapslas in av den publika propertien;

Traditionellt sätt:
class Contact
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private string _city;
    public string City
    {
        get { return _city; }
        set { _city = value; }
    }
}
Ny C#-syntax:
class Contact
{
    public string Name { get; set; }
    public string City { get; set; }        
}

Som du ser kan man spara in en hel del kod på det här sättet. Observera dock att det endast är möjligt att använda denna kortvariant om du inte har behov av att hantera propertien på något sätt i get- eller set-blocket.

Object Initialization Syntax
En annan syntax-nyhet i C# 2008 är Object Initialization. Tidigare var man tvungen att initera ett objekts properties med värden antingen genom en constructor eller så här:

Contact a = new Contact();
a.Name = "Ola Salo";
a.City = "Malmö";

Med den nya syntaxen kan du skriva exakt samma sak så här:

Contact b = new Contact { Name = "Alexander Bard", City = "Stockholm" };

Återigen, du sparar in en del tangenttryckningar och dessutom minskar behovet av att förse klassen med en constructor.

LINQ-introduktion
LINQ gör det möjligt att på ett enhetligt sätt hantera data från olika datakällor. En datakälla kan exempelvis vara en SQL-databas, ett XML-dokument eller rent av en lista med objekt. Se följande kod, där vi återanvänder objekten a och b från ovan;

List<Contact> contactList = new List<Contact>();
contactList.Add(a);
contactList.Add(b);
contactList.Add(new Contact { Name = "Lasse Kronér", City = "Göteborg" });
contactList.Add(new Contact { Name = "Nina Persson", City = "Jönköping" });
contactList.Add(new Contact { Name = "Tina Nordström", City = "Helsingborg" });
contactList.Add(new Contact { Name = "Pekka Heino", City = "Stockholm" });

var sthlmCitizens = from contact in contactList where contact.City=="Stockholm" select contact;

foreach (Contact c in sthlmCitizens)
{
    Console.WriteLine(c.Name);
}

Själva linkuttrycket är fetstilt ovan och följer följande struktur:
from identifier in list where condition select identifier

Notera att sthlmCitizens-referensen är deklarerad med nyckelordet var. var är ett nyckelord som gör det möjligt att deklarera en variabel och sedan låta första tilldelningen till variabeln avgöra vilken datatyp variabeln är. I detta fall returernar Linq-uttrycket ett objekt av typen IEnumerabe<Contact>, men det är inte alltid man kan veta, så var är en smidig genväg. Det du kan vara säker på är att ett Linq-uttryck alltid returnerar ett objekt som går att loopa med foreach.

När Linq-uttrycket körs loopar den igenom listan (contactList) för att se vilka objekt i listan (kan även vara en collection, array etc.) som uppfyller villkoret (contact.city=="Stockholm"). Identifier är den variabel som för varje loop innehåller det aktuella objektet, precis som c i foreach-loopen. Linq-uttrycket avslutas med en select-sats där man talar om vad som ska väljas ut, i det här fallet hela contact-objektet vilket innebär att jag i foreach-loopen kan typa c som en Contact och komma åt både c.Name och c.City. Man kan begränsa detta genom att förändra select-satsen i Linq-uttrycket enligt följande;

var sthlmCitizensNames = from contact in contactList where contact.City == "Stockholm" select new { contact.Name };

Det som händer här är att en helt ny datatyp skapas "on-the-fly" via new. Denna datatyp innehåller endast en property, nämligen Name. Eftersom vi inte kan veta namnet på denna datatyp innebär det att vi nu måste använda var i den foreach-loop som skriver ut träffarna;

foreach (var c in sthlmCitizensNames)
{
    Console.WriteLine(c.Name); //c.City finns inte här
}

Man kan också låta Linq-uttrycket sortera objekten, t.ex. så här;

var sthlmCitizensOrdered = from contact in contactList where contact.City == "Stockholm" orderby contact.Name descending select contact;

Apropå mina tidigare inlägg om sortering så är ju det här ganska intressant med tanke på att klassen Contact inte implementerar IComparable och vi har inte gjort någon separat IComparer-klass heller. Nyckelordet descending kan utelämnas eller ersättas med ascending, man kan också andrahandssortera genom att kommaseparera de properties man vill sortera på.

Extension Methods
En annan nyhet i C# 2008 är möjligheten att utöka redan existerande klasser med egna metoder. I följande exempel kommer jag att utöka klassen char med en metod som returnar true om tecknet är en vokal och annars false. För att detta ska vara möjligt måste metoden placeras i en static klass, metoden i sig måste vara public och static och metodens första parameter måste vara av den typ som jag vill utöka samt föregås av nyckelordet this. Dessa förutsättningar kommer i fetstilt här;

public static class CharExtensions
{
    public static bool IsVowel(this char letter)
    {
        letter = Convert.ToChar(letter.ToString().ToLower());

        char[] vowels = { 'a', 'o', 'u', 'å', 'e', 'i', 'y', 'ä', 'ö' };        
        return vowels.Contains<char>(letter);
    }
}

För att metoden ska hantera både versaler och gemener korrekt börjar jag med att i metodens första rad konvertera bokstaven till en sträng och göra den till liten bokstav via String-klassens ToLower-metod. Därefter konverteras tecknet tillbaka till en char via Convert.ToChar.

Sen deklarerar jag en array av char som innehåller alla vokaler (som gemener) och slutligen anropar jag char-arrayens Contains-metod som kommer att returnera true när letter är en vokal, annars false.

Nu kan denna metod anropas direkt på en instans av en char;
image

I IntellieSense-fönstret ser du att det är en extension-method på den blå lilla pilen i ikonen, samt att tooltipfönstret markeras med "(extension)".

Imorgon ska lektionen handla om Linq to SQL.

Ladda hem "the Lektionsbloggen Solution"