The .
NET Language Integrated
Query Framework
Demo Script
Prepared by: [Demo/Script Owner(s)]
Version: 1.1
2007-08-06
This is a progressive coding demo, targeted to developers, offers a
breadth introduction to LINQ. LINQ or the Language Integrated Query,
is a new technology included with Visual Studio 2008 and the .NET
Framework 3.5 that is designed to help integrate relational data, XML
and other data sources directly from C# 2.0, 3.0 and Visual Basic 9. In
short this progressive demo covers LINQ concepts, LINQ and objects,
LINQ and xml, LINQ and typed datasets, LINQ and SQL, LINQ mixing
SQL and XML, LINQ and XML literals in VB9.
Key Messages:
1. LINQ integrates the worlds of Data and Objects.
2. LINQ provides powerful natural language query syntax to access data and objects, with independence of the data source.
3. LINQ focuses on being declarative instead of imperative.
Key Technologies:
The following technologies are utilized within this demo:
Technology / Product
Version
1. Visual Studio 2008
RTM
2. LINQ
.NET 3.5 (included with Visual Studio 2008)
3. C# 3.0
(included with Visual Studio 2008)
4. Visual Basic 9
(included with Visual Studio 2008)
Setup and Configuration
Before starting the demo, be sure to have all of the resources and code for this demo script installed. By default these files will
be extracted to C:\VS2008TrainingKit\Demos\02 LINQ. After uncompressing the demo files, be sure to set up the
environment as follows.
Reset the demo
If you already demonstrated LINQ using this demo script, then you should reset the source code.
To do this:
1. Double-click on the Reset.bat file on the demo root folder.
Install the demo (database and Visual Studio Code Snippets)
If you still didnt demonstrate LINQ using this demo script, then you should run the setup.
To do this:
1. Double-click on the Setup.bat file on the demo root folder. When prompted, choose to install the Visual Studio Code
Snippets (extensively used through the demo).
Here is a sample of the expected output:
Setting up the Visual Studio environment
1. Open Visual Studio 2008.
2. In Server Explorer, add a data connection to the Northwind database. You will need the connection for later use.
3. After creating the connection, expand it until all tables are shown.
4. Open the solution C:\VS2008TrainingKit\Demos\02 LINQ\ LINQOverview \LINQOverview.sln.
5. Double click in Program.cs (located inside the Demo project).
6. Set full screen mode with Shift+Alt+Enter.
Opening Statement
Database-centric applications have traditionally two distinct programming languages: one for the database and one for the
application.
In this talk we will introduce LINQ, a component designed to help integrate relational data, XML and other data sources directly
from C# and Visual Basic.
Step-by-step Walkthrough
Estimated time for setting up and configuring the demo: 20 minutes.
Estimated time to complete the demo: 60 minutes.
Writing an object query in C# 2.0
Action
Script
1. Go to visual studio, set it full
screen
Let me see what we can do here.
I will be using snippets for my
presentation. Snippets are a way
to write a single word like
Exception and then some code to
appear on the screen.
I am going to write an application
using C# 2.0 and then convert it to
C# 3.0 to show you where LINQ
comes into place.
2. Insert the Exception snippet
3. Remove the code in the file.
Insert the use snippet
Screenshot
4. Insert the fullcust snippet
First I'm going to write a query
against customers. So the first
thing I want is a customer object.
This is a customer object. It has
customer ID, ContactName and
City, and properties to expose
those fields.
Then I want to write a class to
operate over those customers.
It will have a static void main and a
function to return a list of
customers.
5. Insert the class snippet and fill
the class name with Driver.
6. Insert the svm snippet
7. Insert the custlist snippet
1
0
8. Select the first line of the
LoadCustomers function
9. Select the relevant code lines
as you type
10.Go to a blank area (not in a
class) and insert the resultcust
snippet
Let's take a look at this function.
I'm creating a new list of
customers.
I'm creating a new customer,
assigning 3 properties, and adding
my customer to the list. And after
doing this several times, I finally
return my list of customers.
So then, I have a customer object
and something that creates a list
of customers.
Let's say I want to bring out a list
of cities and the number of
customers for each of these cities.
But I want to do that just for the
cities that start with the letter M.
Sounds easy to do, right?
The first thing I need to do is to
create something that stores the
result of my queries. So for
example I've created a Result class
that has City and the number of
customers for that particular city.
Then I can write the code to return
those results
The first thing I want is a list of
results
Then I want to foreach over all the
customers.
And then for each that its city
11.Go to the static void Main
method and type:
List<Result> results = new
List<Result>();
12.Type:
foreach(Customer c in LoadCustomers())
{
}
1
1
13.Type inside the foreach loop:
if (c.City.StartsWith("M"))
starts with M.
Here I'm trying to find if there is
already a city that matches the
current customer's city. If there is
none, I create a new result, set its
city and start the counter and add
the result to the list of results.
If there is such a result I increment
the counter for that particular
result.
{
}
14.Insert the gclausecust snippet
1
2
15.Insert the printresults snippet
Now I'll print out the results.
16.Run the code
These are all the cities that start
with M and the number of
customers for that particular city.
Using the new features in C# 3.0
Action
Script
17.Go to the beginning of the
code and select all of the using
directives
Now let's start from the top to see
how LINQ helps to write this
particular code.
18.Insert the linquse snippet
The first thing I want to do is
adding all the USINGs we will be
using in the demo. They are not all
necessary every time, but we want
to avoid going back to this line.
19.Select all of the Customer
fields and properties
Let's take a look at this. Notice that
you are not doing anything
business-related in those property
accessors, just setting and
returning the property values. But
you still need to write all of this C#
code in 2.0.
What we did in C# 3.0 is this.
Those are called automatically
properties. Essentially the compiler
20.Replace all of the Customer
fields and properties with the
custprop snippet
Screenshot
1
3
generates all of the remaining
code.
21.Select the first line of the
static void Main method
Then look at this line of code. I
have a list of results on both sides
of the instruction. The compiler
already knows by looking at the
right side that this is a list of
results so you can just say var.
And by saying var the compiler
introduces the type from the right
side.
In fact you will see that the
compiler knows the type of the list
and inserts it there at compile
time. This is strongly typed, not
dynamic.
I can use this feature even here in
my foreach statement and you see
that the compiler knows that is a
customer.
Let's take a look at this delegate.
Delegates are a way to pass some
code to a method as if it were
something else. The syntax is
particularly cumbersome.
In C# 3 you can write it in a much
easier way by doing this. This is a
new syntax to write delegates
which is quite simple to the eye,
and it is named lambda
expressions.
22.Replace the first occurrence of
List<Customer> with var
23.Point to var and wait for the
tooltip to pop up
24.Replace Customer with var in
the foreach statement and
then point to var in the code
(wait for the tooltip to pop up)
25.Select the delegate declaration
and the delegate body
26.Type:
cust=> cust.City == c.City
1
4
Let's take a look at this. I need to
create a class for storing the
results, create and assign
properties, and just to create a
new result you need to write 4
lines of code.
In C# 3 you can do all of it in a
single line of code with what is
called object initializers (ie. create
an object using a public
constructor, setting public
properties).
29.Select all of the
LoadCustomers method body.
Replace its contents with the
newcustlist snippet
You can do something similar here.
You can initialize not only objects
but also collections.
For example, here I'm creating a
new list and in the same
instruction that creates the list I'm
assigning all the items in the list
and initializing each item in the
list. You don't need a complex
structure to initialize objects. This
feature is named collection
initializers.
30.Select the last statement from
the static void main method.
Replace it with the dump
snippet
Then let me replace this foreach
statement that is hardcoded for a
more professional way.
Let me use a little tool called
object dumper (the code is in the
sample). It does the foreach
statement, looking by Reflection
the properties and outputting its
27.Point to the then block in
the if res
28.Replace the selected code with
the addcustres snippet
31.Run the sample again (Ctrl+F5)
1
5
values with coloring.
If I execute this you will see that
the result is the same.
LINQ Object queries
Action
Script
32.Select all of the static void
main code with the exception
of the last line.
Screenshot
So far I've shown you features that
will make your life easier but it's
not a revolution. The real problem
with this thing is here. The real
problem is that you have to
represent something that is simple
and you need to tell the compiler
not only what you want it to do but
also HOW to do it. I would like a
more declarative way to express
what in your mind is. Here is where
LINQ comes into place.
1
6
Let's take a look on how it looks
like in LINQ.
35.Run the sample
Let's run it. You see that the results
are the same. This is the real
power of LINQ. This expression
looks a lot like what I said. Notice
that there is no detail on how to do
it. It looks like a query. In fact, we
took the power that is inside query
languages and brought it to C#,
being able to query independently
of the data source.
36.Select the LoadCustomers
invocation in the static void
Main method.
Let's focus on this. Just by
returning IEnumerable of
something it means that you can
query against that particular type.
If you think about it, all of the
collections in .net are
IEnumerable<T>. With no need to
change any code in every .NET
collection you can use LINQ to
query all of that. Almost everything
returned by a library (built-in or
custom) is an IEnumerable, so it's
queryable with no need to change
33.Type and explain line by line
var query = from c in LoadCustomers()
where c.City.StartsWith("M")
group c by c.City into g
select new Result {City = g.Key, Count
= g.Count()};
34.Replace results by query
in the last statement
1
7
nothing of that code.
37.Select the Result word from
the LINQ query
This may look similar to SQL with
the select inverted. There are
reasons for that. One is to support
IntelliSense (if you don't declare
what you are going to use first
then IntelliSense won't be able to
help), like first declaring your
variable and then using it.
There is still some wrong code here
(select the word Result). Note that
Result is not part of the business
domain, and I've been forced to
introduce it just to store the results
of my query. But it does not have
any business meaning.
So let's just remove it, and it will
still work.
The reason why this works is a new
feature called anonymous types.
The compiler here creates a new
type at compile time with some
crazy name that you will not be
able to use in the program, and it
will create 2 properties: City and
Count. So if you have an
application with many queries, you
don't need to create a type for
each of the queries, just let the
compiler do it.
38.Remove the Result word from
the LINQ query and the Result
class from the code
39.Run the sample
40.Select the last part from the
LINQ query (from new to } )
1
8
LINQ: Querying an XML file
Action
Script
41.Open the Customers.xml file
Until now we've done queries to
objects in memory. But most of the
time your data is not in memory,
it's somewhere else, for example,
in an XML file.
For example I have this XML file
representing the customer list you
saw in the previous example. So
lets write the code to achieve
that.
In LoadCustomers I'm going to
change how to obtain the query
elements.
I'm using LINQ to XML here. I'm
opening customers.xml,
obtaining the Customer elements
and for each Customer element,
I'm creating a new Customer
object based on the element's
attributes, and I'm returning the
list of customers. Notice that this is
all I need to do in LINQ to XML for
parsing an XML file and return a
list of objects out of it.
Notice that my query here hasn't
changed at all because I'm still
returning a list of customers so
what I'm doing stays the same.
42.Switch back to Program.cs.
43.Select the first statement from
the static void main method.
Replace it with the following
statement
var custs = from x in
XElement.Load("customers.xml").Elements
("Customer")
select new Customer {
City = x.Attribute("City").Value,
ContactName =
x.Attribute("ContactName").Value
};
44.Replace query by custs as
a parameter for ObjectDumper
45.Select the query in the static
void Main method
Screenshot
1
9
46.Run the sample (Ctrl+F5)
(after closing the example,
switch back to the previous
query)
If I run this the result is obviously
the same.
This is what I wanted from the
start, this ability to do a complex
thing fluently.
This is how to read in the XML file,
we will see in a moment how to
produce an xml file out of it with
the same simplicity.
LINQ: Querying a Typed Dataset
Action
Script
47.Replace the LoadCustomers
method declaration and body
with the dsload snippet
Let's say that I already have an
application that returns datasets,
typed datasets. Can I now query
over these datasets? Let's see how
I can do this.
Now I have a LoadCustomers
function that returns a typed
Customers DataTable. Let's
assume that I already have the
DataSet in scope.
I'm selecting data from the
database. This is the connection
string. I'm creating a new typed
48.Select the line that starts with
select *, then select the
connection string, and then
select the following lines (as
you explain)
Screenshot
2
0
DataTable and I'm filling the table.
49.Select the query from the
static void Main method and
then run the application
(Ctrl+F5)
This is code that just obtains the
typed dataset.
Now that I have a typed DataSet,
and all of my query stays the same
and also do my results. I'm
querying the database and
returning the dataset, and I'm
querying the dataset and returning
objects. And my query stays the
same because I'm still querying
objects.
If I write a new application I'd
probably not write this code. Why
should I go to the effort and
overhead of creating a typed
DataSet and then converting it to
Objects. I'm still living in 2 worlds.
In one world there is the database,
and in the other I have my objects.
I still have to think in SQL to do my
queries, so I still need to know C#
and SQL. I would really like to think
of my programs really in terms of
objects.
LINQ to SQL
Action
Script
Screenshot
2
1
50.Remove the LoadCustomers
method and the Customer
class.
So let's remove it and do
something else. So now I'm in the
situation where I have a database
and would like to access that
database in a purely strongly typed
fashion, as if it were objects, and
query them.
(Explain as you type).
Now I'm presented with a designer
surface and I can Drag and drop
for example Customers and Orders
as well. Notice that the tool infers
the one to many relationship.
I can also drag and drop stored
procedures like SalesByCategory or
Sales by Year into the tool's right
pane.
What is happening here is that my
tool is generating a strongly typed
object model from the database
that allows me to access my
database as if it were an object
model in memory.
51.In Visual Studio, go to Project |
New Item.
52.In the dialog that opens, go to
Visual C# items | Data, and
choose LINQ to SQL classes.
Name the file Northwind.dbml
53.Expand the Tables node from
the Northwind data connection
(which was previously added
to the Server Explorer) and
drag the Customers table to
the center pane. Then do the
same with the Orders table
54.Expand the Stored Procedures
node from the Northwind data
connection. Then drag and
drop the SalesByCategory
Stored Procedure to the right
pane, and then do the same
with Sales by Year
2
2
55.Return to the static void Main
method
If I go to the code and create a
new NorthwindDataContext (the
NorthwindDataContext is the
object that was created by the
designer), I can see all the tables
that were dragged and dropped
into my designer.
I can just say db.Customers and
if execute this, the results will be
the same, but this time coming
from the relational database.
Let me show the query that we
submit there. I add this little log
that brings out the queries that the
framework is doing to the console
and if I run this now you will see at
the top the query that we
submitted. Essentially, what the
Framework is doing, is taking the
object query, translating into SQL,
returning data and creating objects
out of the returned data.
56.Insert the ndc snippet before
the first statement
57.Replace the query from:
Write db. after from c in
58.Type Customers right after
db.
59.The resulting query statement
should look like this
var query = from c in db.Customers
where c.City.StartsWith("M")
group c by c.City into g
select new {city = g.Key, Count =
g.Count()};
60.Ensure that query is the
parameter for ObjectDumper
61.Run the sample
62.Insert the log snippet in the
line after the query
63.Run the sample
2
3
64.Select db.Customers from
the query
How it is doing so? The enabler is
here. This doesn't return an
IEnumerable<T> as in the
previous case. If that were the
case, then the compiler would
have generated the foreach
statement and loop through all of
the items in the database. That
would be very inefficient, and not
what youd like. But this guy
returns an IQueryable<T>.
So the compiler looks here, sees
an IQueryable<T> and translates
the query to an expression tree (an
object model that represents the
query), and gives this
representation to whatever
implementation of IQueryable<T>
I have here.
LINQ to SQL is just one possible
implementation of IQueryable<T>.
You can create your own
implementation of IQueryable<T>,
as some people already did, and
the compiler will just create an
object model that represents the
query and give it to your
implementation. The compiler only
knows that it is giving the query to
an object that implements
IQueryable<T>.
The only thing that is static here is
the shape of this query that is
compiled to an expression tree,
2
4
which is given to the
implementation. Parameters, such
as the QueryString, are not.
In LINQ to SQL, if you added or
removed a column, you'd have to
regenerate the object model, but
by doing so you wouldn't lose any
of your business logic because the
object model is created as a partial
class so the partial classes that
were not generated will be kept
when you regenerate the object
model. This is the pain and
pleasure of being strongly typed.
65.Remove the log sentence
and the query text (just leave
var query=)
Do you remember that I dragged
and dropped Stored Procedures as
well in my designer surface?
66.Type db.Sa, wait for the
IntelliSense to pop up and then
select SalesByCategory
I can call them here for example.
(type as you say)
67.Type:
And if I go and execute this the
stored procedure is executed and
the result is returned. There are
many ways to plug stored
procedures and do inserts, deletes
and updates as well.
SalesByCategory("Seafood", "");
68.Run the sample (Ctrl+F5)
2
5
LINQ to XML
Action
Script
69.Remove the static void Main
method inner statements
Now I want to talk about LINQ to
XML.
Let me introduce this.
Console.WriteLine(xml);
Let's print out the XML.
72.Run the sample (Ctrl+F5)
So this is the simplest xml
document you can create.
73.Add a second parameter to the
XElement passing the string
"Engine" and run the
application
You can add an engine to your car.
You can decide that this engine
should be better a new element,
thus creating a one thousand
engine.
When you run the application you
will see that there is a new
element that is an engine.
You can decide that you'd better
have an attribute here and your
engine becomes an attribute.
This is a new way to declare XML
documents, a very declarative
way, almost like you visualize the
XML documents. You can create
complex XML and they look like
Screenshot
70.Read and type:
var xml = new XElement("Car");
71.Type:
74.Update the line to be:
var xml = new XElement("Car", new
XElement("Engine", 1000));
75.Run the application (Ctrl+F5)
76.Replace the engine XElement
declaration with an XAttribute
declaration and run the sample
2
6
normal XML elements.
Mixing LINQ to SQL and LINQ to XML
Action
Script
77.Insert the ndc snippet as the
first line of the static void Main
method
Things start to get interesting
when you mix those domains. For
example, if I go and introduce LINQ
to SQL here...
and replace the XML to output
the results of the Stored Procedure.
Now I execute this. I'm going to the
database, submitting to the stored
78.Replace the query (2nd line on)
by:
var xml = new XElement("Sales",
Screenshot
from s in
db.SalesByCategory("Seafood", "")
where s.TotalPurchase < 5000
select new XElement("Sale",
new XAttribute("Name",
s.ProductName),
new XAttribute("Total",
2
7
procedure, returning the data,
scoping down the data and
creating an XML out of the data.
s.TotalPurchase)));
Console.WriteLine(xml);
79.Run the code (Ctrl+F5)
80.Go to the end of the static void
Main method and insert the
oldcode snippet
Just imagine doing the same thing
with the DOM and with our ADO
code.
81.Scroll through the code as you
explain the second bullet
This is how it looks like.
Part of the problem is that you lose
the semantic meaning of what is
going on, and it is not easy to
understand.
82.Remove the code you inserted
using the snippet. Run the
code again, and copy the XML
output to the clipboard
VB Demo: XML Literals in VB9
Ask if most of the audience are VB programmers or not, because the next part will rise some
questions on why this feature is in VB and not in C#, so the answer is that the decision was
made by each language's team based on their philosophy
2
8
Action
Script
83.Copy the sample output to the
clipboard
Let me run this demo once again
and copy and paste the output
because I want to use it in my VB
demo.
You may have noticed that I
haven't used VB yet but all of the
things I've shown you so far are
present in VB.
Set this project as the startup
project.
86.In the Main Sub, type Dim xml
= and paste the XML from the
clipboard
Let's create an XML variable here
and paste the XML we copied
earlier.
87.Type
I can write the output to the
console.
What happened here is that we
added XML literals to the language
in VB.
84.Open
C:\VS2008TrainingKit\Demos\0
2 LINQ\SuperVB\Module1.vb
85.Go to Project | Set as Startup
project
Console.WriteLine(xml)
88.Run the sample (Ctrl+F5)
Screenshot
2
9
89.Point to the variable
declaration and wait for the
IntelliSense to pop up
If you see what the type of the
variable this is. This is the same
XElement that we had created
before. Under the cover, the
compiler is creating the same kind
of code that I've shown you before.
It is just syntactic sugar.
Mixing XML literals and code in VB9
Action
Script
90.Type at the beginning
You can do many things with this.
Dim name="Ravioli"
You can introduce a name here.
And you can run it.
Let's say that I want to create a
different XML from this XML.
And in my report I can access this
XML by typing like you do in
XPath. The language understands
this and gives you IntelliSense.
I can query by attributes ( @ )
and IntelliSense is always there to
91.Replace the first name
pasted from the XML with:
Screenshot
<%= name %>
92.Run the sample (Ctrl+F5)
93.Go to the last line of the Static
Sub Main method and type:
Dim xml2=<Report></Report>
94.Type the following between the
Report tags (explain as you
type):
<%= from s in xml... %>
95.Continue typing until the line
3
0
becomes:
help.
<%= From s In xml...<Sale> _
Where s.@Total < 1000 _
And then I can output the results.
Notice that I've been creating XML
with XML-like syntax, accessing
XML.
97.Run the sample
I can run this. What I've done is
very similar to an XSLT in VB.
98.Rename the Report tag
name by Lines and see how
the bottom declaration is also
renamed
You've got IntelliSense everywhere.
I can rename Report by Lines and
the IDE knows what you are doing,
and it becomes part of the
language.
select <Line><%= s.@Name %></Line>%>
96.Type:
Console.WriteLine(xml2)
Summary
If you just remember 3 things: we merged data with objects, we merged programming languages with data access, and I gave
you a variety of ideas on what you can do with this; we saw the difference on doing things imperatively vs. declaratively; and we
saw that the queries can be made independently from the data source.
3
1
I would add that the queries aren't executed until the time they are accessed, for example in
a foreach statement (deferred execution)
3
2