LinQ Introduction

36
1 LinQ Introduction

description

LinQ Introduction. Outline. Goals of LinQ Anatomy of a LinQ query More expression examples LinQ to Objects LinQ to XML LinQ to SQL. Goals of LinQ. Integrate data queries into .Net languages - PowerPoint PPT Presentation

Transcript of LinQ Introduction

Page 1: LinQ  Introduction

1

LinQ Introduction

Page 2: LinQ  Introduction

2

OutlineGoals of LinQAnatomy of a LinQ queryMore expression examplesLinQ to ObjectsLinQ to XMLLinQ to SQL

Page 3: LinQ  Introduction

3

Goals of LinQIntegrate data queries into .Net languagesBefore C# 3.0 you could use delegates, extension methods, anonyous methods and Visitor Pattern to make something similar to LinQ.But the syntax is messy and key points, eg. selection criteria, are not easy to read.

Page 4: LinQ  Introduction

4

Goals of LinQProvide a standardized way to query dataChallenges:

Different data typesDifferent data representations (xml, sql, objects)Data organization

Hierarchical – xml (and object)Relational – sql

Before LinQ you should use different api’s for accessing databases, objects and xmlLinQ provides one single way (nearly) to access it allLinQ works on collections that implements IEnumerable<T>

(the .Net language must therefore support generics)

Page 5: LinQ  Introduction

5

Anatomy of a LinQ queryAn example:

string[] characters = { "Donald", "Mickey", "Goofy", "Minnie",

"Daisy", "Scrooge" };

IEnumerable<string> query = from c in characters where c.StartsWith("M")!=true orderby c descending select c;

foreach(string s in query)Console.WriteLine(s);

The collection is here a simple string arrayBut the same query will run for more complex objects, SQL, XML etc.

Page 6: LinQ  Introduction

6

DemoWith objects

After demo, note the following:IntellisenseStatic type checking

Page 7: LinQ  Introduction

7

Query ExpressionsTypes of expressions

Filteringe.g. Where

Projectionse.g. Select

Joininge.g. Join

Partitioninge.g Skip and Take

Orderinge.g OrderBy

Aggregatione.g. Count and Sum

Page 8: LinQ  Introduction

8

ProvidersThe same expresions works on different kinds of dataThis is done by accessing a providerA LinQ provider is a gateway to a querable type.There are several builtin providers

ObjectsSQLXMLActive DirectoryPLINQ (Parallel processing)Etc.

And many custom providers too:LinQ to AmazonLinQ to TwitterEtc.

Page 9: LinQ  Introduction

9

How are people in Aalborg ?Twitter demoGet LinqToTwitter here: http://linqtotwitter.codeplex.com/

var twitterCtx = new TwitterContext();.. var queryResults = from search in twitterCtx.Search where search.Type == SearchType.Search && search.Attitude == Attitude.Positive && search.GeoCode ==

"57.028811,9.917771,25km" select search;

foreach (SearchEntry entry in srch.Results) Console.WriteLine(entry.Text);

Page 10: LinQ  Introduction

10

Deferred ExecutionNormally the query is not executed before the result is neededThis is when only lazy operators (where, orderby...) are used.When busy operators are used, the query is executed immediately (count, average)

var adults = from p in personList where p.Age > 18 orderby p.Age select (p.FirstName + " " + p.LastName); Console.WriteLine(adults.Count()); personList.Add(new Person { FirstName = “Ib", LastName = “Madsen", Age = 35 }); foreach (var p in adults) { Console.WriteLine(p.ToString()); }

Page 11: LinQ  Introduction

11

Composed QueriesA composed query is a query that uses another query.In behind LinQ will make new query that is optimized for the given data store (objects, sql, xml...)

var adults = from p in personList where p.Age > 18 orderby p.Age select (p.FirstName + " " + p.LastName);..... var query = from p in adults where p.StartsWith("B") select p; foreach (var p in query) Console.WriteLine(p);

Page 12: LinQ  Introduction

12

Encapsulate QueryIt is not possible directly to return an anonymous typeAnd it wouldn’t be nice either Therefore it not is possible to return a query if is declared as varThe nice way here is to declare the query as an IEnumerable<type> and return that.If the query is a join or a projection etc. then make a class that maps the output from ‘select’ and return a collection of objects of that class

Page 13: LinQ  Introduction

13

Collections of objectsWe already seen how to access a collection of objectsthis is called LinQ to objectsLinQ to objects is a good alternative to foreach and other iterations

Page 14: LinQ  Introduction

14

Custom providersAnd we have seen use of a custom provider to access a webservice.

If it is a plain webservice that returns a collection, then we could have accessed that with LinQ to objects

Page 15: LinQ  Introduction

15

In the next part, we will see how to access XML and SQLServer.

Page 16: LinQ  Introduction

16

LinQ to XMLUses the System.Xml.Linq namespaceIs somewhat different from other xml api’sThe XElement class is the key class.When instanizing a XElement you can generate the whole document in the constructor

XElement doc = new XElement("Inventory", new XElement("Car", new XAttribute("ID","1000"), new XElement("Color", "Red"), new XElement("Make", "Ford")) ); <Inventory>

<Car ID="1000"> <Color>Red</Color> <Make>Ford</Make> </Car> </Inventory>

Page 17: LinQ  Introduction

17

Use LinQ to generate XML XElement personDoc =

new XElement("People", from c in personList orderby c.LastName select new XElement("Person",

new XAttribute("Age", c.Age), new XElement("FirstName", c.FirstName), new XElement("LastName", c.LastName) )); <People>

<Person Age="2"> <FirstName>Caroline</FirstName> <LastName>Bendtsen</LastName> </Person> <Person Age="67"> <FirstName>Bjarne</FirstName> <LastName>Hansen</LastName> </Person> <Person Age="13">...

Page 18: LinQ  Introduction

18

Use LinQ to search in XML

XElement doc = MakeXElementFromList(); var query = from p in doc.Elements("Person") where Convert.ToInt32(p.Attribute("Age").Value) < 40 select p; foreach (var p in query) Console.WriteLine(p.Value);

Page 19: LinQ  Introduction

19

LinQ to SQLLinQ accesses the sql db through a datacontext classThe class can be created with a wizard in VisualStudio,where you select which tables to accessOnly SQLServer is supported from MicrosoftBut dbms vendors like Oracle are also providing support for LinQ. (haven’t tested it myself).An (better?) alternative is to use the Entity Framework

Page 20: LinQ  Introduction

20

Use the wizard to create the DataContext classAdd a new item to the project using the ”LINQ to SQL classes” template.Select the database and the tables that shall be available

Page 21: LinQ  Introduction

21

Select the tablesNote that wizard knows the carnalities

Page 22: LinQ  Introduction

22

Using LinQ to SQLSelect orders from a certain customer

NorhwindDataContext dc = new NorhwindDataContext(); var orders = from o in dc.Orders where o.CustomerID == "ALFKI" orderby o.OrderDate select o.OrderID;

foreach (var o in orders) Console.WriteLine(o);

SELECT [t0].[OrderID]FROM [dbo].[Orders] AS [t0]WHERE [t0].[CustomerID] = @p0ORDER BY [t0].[OrderDate]

Page 23: LinQ  Introduction

23

A joinWrite products that the customer has brought

var orders = from o in dc.Order_Details join p in dc.Products on o.ProductID equals p.ProductID where o.Order.CustomerID == "ALFKI" orderby o.Order.OrderDate select new { product = p.ProductName, orderDate = o.Order.OrderDate };foreach (var p in orders) Console.WriteLine("{0:dd-MM-yyyy}: {1}",

(DateTime)p.orderDate, p.product);{SELECT [t1].[ProductName] AS [product], [t2].[OrderDate] AS [orderDate]FROM [dbo].[Order Details] AS [t0]INNER JOIN [dbo].[Products] AS [t1] ON [t0].[ProductID] = [t1].[ProductID]INNER JOIN [dbo].[Orders] AS [t2] ON [t2].[OrderID] = [t0].[OrderID]WHERE [t2].[CustomerID] = @p0ORDER BY [t2].[OrderDate]}

Page 24: LinQ  Introduction

24

JoinJoin is similar to Inner Join in SQLThat means that it is the intersection between two sequencesThe inner sequence is a keyed collection, that makes it a lot faster than a subquery (or traversing in a nested loop)Note that it uses Equals instead of == (remember the difference?)

Page 25: LinQ  Introduction

25

Group

Group transforms a sequence into a sequence of groups that contains a subsequence

var products = from p in dc.Products group p by p.Category.CategoryName;

foreach (var group in products) { Console.WriteLine("\nCategory: {0}", group.Key); foreach (var product in group) Console.WriteLine(product.ProductName);

Page 26: LinQ  Introduction

26

Use objects as keysIt is possible to use objects as keys.It can be of an anonymous type or of a defined class var products = from p in dc.Products

group p by new { cname = p.Category.CategoryName, cid = p.Category.CategoryID } into productCategories orderby productCategories.Key.cid select productCategories;

foreach (var group in products){ Console.WriteLine("\nID: {0} Category: {1}", group.Key.cid,group.Key.cname); foreach(var product in group) Console.WriteLine(product.ProductName);}

Page 27: LinQ  Introduction

27

Group into

Group will end the query. Use ‘Into’ to continue the query var products = from p in dc.Products group p by p.Category.CategoryName into productCategories orderby productCategories.Key select productCategories; foreach (var group in products) { Console.WriteLine("\nCategory: {0}", group.Key); foreach (var product in group) Console.WriteLine(product.ProductName);

Page 28: LinQ  Introduction

28

Grouping and projectingMake a projection on the content of the group

var products = from p in dc.Products group p by p.Category into productCategories where productCategories.Count() < 8 orderby productCategories.Key.CategoryName select new { cname = productCategories.Key.CategoryName, count = productCategories.Count() }; foreach (var group in products) { Console.WriteLine("Category: {0}, Count: {1}", group.cname,group.count); }

Page 29: LinQ  Introduction

29

Nested queriesNested queries are similar to nested SELECT in SQL.But be careful: O(n2)

var products = from p in dc.Products where p.CategoryID == (from c in dc.Categories where c.CategoryName == "Seafood" select c).First().CategoryID select p;

foreach (var p in products){ Console.WriteLine("Category: {0}, Product: {1}", p.Category.CategoryName, p.ProductName);}

Page 30: LinQ  Introduction

30

Let keywordReuse expressions

var products = from p in dc.Products group p by p.Category into productCategories let upperName= productCategories.Key.CategoryName.ToUpper() where productCategories.Count() < 8 orderby upperName select new { cname = upperName, count = productCategories.Count() }; foreach (var group in products) { Console.WriteLine("Category: {0}, Count: {1}", group.cname,group.count); }

Page 31: LinQ  Introduction

31

A few words on operatorsThe LinQ operators are implemented with extension methods (recall the .Where method)You can define your own operator by defining an extension methodAnd you can overwrite the existing operatorsBut be careful:

The operator should still do the same sort of things as it was originally intended for.E.g. the Where operator should still be a filter operatorIf the operator is not an aggregator it should be lazy to support deferred execution. Use ”yield” keyword when returning items from lazy operators

Page 32: LinQ  Introduction

32

Define a contrary version of WhereThe extension method must be in another namespace

using System;using System.Collections.Generic;using LinQExamples;namespace MaryTheContrary //Rasmus Modsat{ public static class ExtensionMethods { public static IEnumerable<Person> Where( this IEnumerable<Person> sequence, Func<Person, bool> predicate) { foreach (Person p in sequence) { if (!predicate(p)) //Contrary yield return p; } } }}

Page 33: LinQ  Introduction

33

Use of the contrary ‘where’The compiler differs between different where’s by the namespace. So you must use ‘using <the namespace>

using MaryTheContrary;

....

IEnumerable<Person> nonAdults = from p in personList where p.Age > 18 orderby p.Age select p; foreach(Person p in nonAdults) Console.WriteLine(“First Name: {0} Age:{1}", p.FirstName,p.Age);

Page 34: LinQ  Introduction

34

LINQPadUtility for interactively search with LinQYou can get it here: http://www.linqpad.net/

Page 35: LinQ  Introduction

35

Exercise 1You can get the processes that runs on the machine withIEnumerable<Process> System.Diagnostics.Process.GetProcesses()The Process object has several properties, e.g.

ProcessName returns the name of the processWorkingSet64 returns the allocated memory in bytesTotalProccessorTime return the CPU time that has been used

TODO:Write and test a LinQ query that returns the processes that has allocated more than 50mb (1mb=10124*1024 bytes) and ordered by process name. The allocated memory should be outputted in mb.Do the same in a foreach without LinQ.

Page 36: LinQ  Introduction

36

Exercise 2, advancedContinued from exercise 1.You will get an exception if you try to get TotalProcessorTime on a process that you do not own, e.g. a system processAnd it not possible to filter those processes out !A workaround could be like this:

IEnumerable<Process> processes = Process.GetProcesses(); foreach (Process p in processes){ try { Console.WriteLine("Process Name: {0} Time: {1}",

p.ProcessName, p.TotalProcessorTime); } catch (Exception e){ Console.WriteLine("Process Name: {0} Time: {1}", p.ProcessName, null); }}

TODO: Override “select” so it inserts null’s for values that are not available. Test it in a LinQ expression(To get it work: Remove “Using System.Linq”, and also implement “Where”)