Jeg har i længe gerne ville prøve at arbejde med LINQ. Undervejs i forløbet hvor jeg prøvede at sætte mig ind i det stødte jeg ind i et emne, Lambda expression, som Linq gør brug af. Dette vakte min nyskerrighed og jeg begyndte derefter at undersøge Lambda expressions ligeså. Jeg vil i dette indlæg vise nogle enkelte muligheder med Lambda expression og Linq.
Lambda expression bygger på det hvad man kalder en anonym metode. Ideen med anonyme metoder er at man kan lave inline metode kald, hvilket gør at man kan spare et metodekald. Anonyme metoder er et godt supplement til små metodekald som ikke skal genbruges af andet kode.
Jeg vil nu gennemgå noget kode som viser besparelsen i at bruge lambda expression. Find metoden tar et delegat som peger på en metode som input. Det som sker er at hele listen bliver itereret igennem indtil et match findes som så returneres hvis der findes et match. Årsagen til metoden returnere en bool er kun et tjek om metoden opfylder delegatet signatur. Det man vil opnå er at undersøge om ens liste indeholde et navn som hedder Abe.
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Dave");
names.Add("John");
names.Add("Abe");
names.Add("Barney");
names.Add("Chuck");
string abe = names.Find(IsAbe);
Console.WriteLine(abe);
}
public static bool IsAbe(string name)
{
return name.Equals("Abe");
}
Nedenstående kode viser hvordan man har kodet en inline metode. Tanken med inline metoder er også at man placere noget kode der hvor det anvendes.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Dave");
names.Add("John");
names.Add("Abe");
names.Add("Barney");
names.Add("Chuck");
string abe = names.Find(delegate(string name)
{
return name.Equals("Abe");
});
Console.WriteLine(abe);
}
}
Her anvendes der Lambda expression. Syntaxen er x => x * x, hvor x er inputparameter til udtrykket x * x, returnværdien er så resultatet af udtrykket. I nedenstående eksempel siger man derfor at hvis det aktuelle navn er lig med abe så returnere det tilbage i variablen abe. Med denne kode undgår vi tuborklammerne.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Dave");
names.Add("John");
names.Add("Abe");
names.Add("Barney");
names.Add("Chuck");
string abe = names.Find((string name)
=> name.Equals("Abe"));
Console.WriteLine(abe);
}
}
Lambda expression anvender type infer, hvilket betyder at compileren på runtime, kan udlede typen udfra højre side af udtrykket. Derfor kan man fjerne erklæren af string.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Dave");
names.Add("John");
names.Add("Abe");
names.Add("Barney");
names.Add("Chuck");
string abe = names.Find(name =>
name.Equals("Abe"));
Console.WriteLine(abe);
}
}
Derfor er man i princippet ikke bunde til at anvende name variablen. Ofte vil man bruge 1 bogstav såsom p.
class Program
{
static void Main(string[] args)
{
List<string> names = new List<string>();
names.Add("Dave");
names.Add("John");
names.Add("Abe");
names.Add("Barney");
names.Add("Chuck");
string abe = names.Find(p => p.Equals("Abe"));
Console.WriteLine(abe);
}
}
Inden jeg går videre vil jeg gerne vise nogle syntaxmæssige/symbolmæssige forskelle imellem VB og C# når man arbejder med Lambda expression. Her ses hvordan lambda expression kan anvendes i linq to sql. First metoden input parameter er et delegat som peger på en funktion. Det som sker er at når produkterne gennemløbes og når man første gang støder på produkt(p) som opfylder kriterierne på funktionen så vil produktet returneres.
Dim product = db.Products.First(Function(p) p.ProductName.StartsWith("Sune"))
Product product = db.Products.First(p => p.ProductName.StartsWith("Sune"));
Definitionen for et lambda expression er Function(parameter) <function definition>. I neden stående eksempel sættes checkName til at pege på en funktion som tjekker om en given variabel er true eller false.
Func siger at den har et inputparameter af typen string og en returværdi af typen boolean.
Dim checkName As Func(Of String, Boolean) = _
Function(x As String) _
If(x = "Cabernet Sauvignon", True, False)
Funktionen kan fx anvendes i denne kontekst
If checkName(myStringVar) Then ...
Ovenstående kan forkortes ned til, hvor man udnytter type inferrence. Compileren tjekker ternary operator udtrykket og finder ud fra returværdien at det er en boolean.
Dim checkName = Function(x As String) _
If(x = "Cabernet Sauvignon", True, False)
Linq omfatter disse tre hovedpunkter LINQ to Objects, LINQ to ADO.NET(LINQ to DataSet, LINQ to Entities og LINQ to SQL) og LINQ to XML. Som det kan ses så spænder Linq bredt. Det som Microsoft blandt andet har prøvet at sælge denne teknologi på var at prøve at finde en fælles måde at bygge sine query uafhængig om det var til xml, sql eller objects. Jeg vil her belyse nogle enkelte kodeeksempler, for at vise et lille spektra af dets funktionalitet.
Her opbygger vi xml som illusterer en kundes; kundeinfo, ordreinfo og próduktinfo omkring alle kunders ordre. Linq to sql har igennem Linq to sql classes, generet alle referencer/relationer/afhængigheder imellem de forskellige klasser ud fra databasen. Man kan bruge linq to xml kombineret med embedded expression dvs. inde i linq to xml query kan man kode en linq to sql.
Dim orders = <orders>
<%= From customer In db.Customers _
Select <customer>
<name><%= customer.CompanyName %></name>
<phone><%= customer.Phone %></phone>
<orders>
<%= From order In customer.Orders _
Select <order total=
<%= Aggregate detail In order.Order_Details Into Sum(detail.UnitPrice * detail.Quantity) %>>
<date><%= order.OrderDate %></date>
<address><%= order.ShipAddress %></address>
<details><%= From detail In order.Order_Details _
Select <item id=<%= detail.ProductID %>>
<product><%= detail.Product.ProductName %></product>
</item> %>
</details>
</order> %>
</orders>
</customer> %>
</orders>
Igennem de oprettede referencer kan man igennem kunder (customer.Orders _) få fat i kundens ordre. Og igennem ordre kan man få fat i de enkelte ordrelinier (order.Order_Details) og derved få fat i produkterne.
Aggregate detail In order.Order_Details Into Sum(detail.UnitPrice * detail.Quantity)
Den aggregerede funktion har til formål at lave summering så man har den samlede pris på ordren. Det som sker på hver enkelt ordrelinie er at antal og stk.pris ganges sammen og til sidst beregnes summen af alle disse ordrelinier på den enkelte ordre.
På samme måde kan opbygge en html side.
Dim customers = <html>
<body>
<table width="100%" border="0">
<tr>
<td><b>Name</b></td>
<td><b>Phone</b></td>
<td><b>Country</b></td>
</tr>
<%= From customer In db.Customers _
Select <tr>
<td><%= customer.CompanyName %></td>
<td><%= customer.Phone %></td>
<td><%= customer.Country %></td>
</tr> %>
</table>
</body>
</html>
Her er noget kode som illustrere noget Linq to sql. Her vil man finde antallet af ordre(summen af alle ordrelinier) på en givet produkt og den samlede indtægt (stkpris * antallet for hver ordrelinie som til sidst summeres sammen).
Dim product = From p In db.Products _
Select p.ProductID, _
p.ProductName, _
NumOrders = p.OrderDetails.Count, _
Revenue = Aggregate detail In p.OrderDetails _
Into Sum(detail.UnitPrice * detail.Quantity)
Indlægget har ikke skulle vise noget revolutionerende nyt, men bare prøvet på at give min forståelse (på dansk) over Lambda expression samt Linq.