Transcript Document

The Adventure of LINQ
(Language Integrated Query)
Presented by:
Richard Broida
Senior Architect
Bennett Adelson
Visual Studio 2005 Team System
Tour
• Coming to Cleveland!
–
–
–
–
–
February 22, 2006
9:00 AM – 4:30 PM
Right Here
Free!
Sign Up:
• http://msevents.microsoft.com
• Event Code: 1032288761
Bennett Adelson
Agenda
•
•
•
•
•
•
•
LINQ’s Quest: Make Queries Easier
Hello, LINQ
LINQ Internals
DLINQ: Using LINQ with Relational Data
XLINQ: Using LINQ with XML
Resources on the Web
Q&A
Bennett Adelson
LINQ’s Quest: Make Queries Easier
• Make query syntax part of the
programming language
• Use the same query syntax for all types
of data
– Relational, hierarchical, or flat
– Persistent or in-memory
• Make query input and output always
type-safe
• Provide full tool support in IDE
Bennett Adelson
Back to the Future
• In 1991 FoxPro 2.0 had SQL SELECT,
INSERT, UPDATE and DELETE integrated
into the language
– No “command” object: just write the query
– SQL statements could incorporate FoxPro
expressions and functions and/or userdefined functions at will
– Database data types were exactly the same
as language data types
– SELECT produced a table that knew its
column names, data types and sizes
Bennett Adelson
.NET 2.0 Still Hasn’t Caught Up with
FoxPro 2.0
• SQL isn’t part of your .NET language
– No compile-time type checking
– No IntelliSense
– Set-based approach of SQL is foreign to procedural
languages
• Queries return typeless DataReaders and DataTables
– Column names aren’t part of the object
• Must identify by string or numeric index
– Must cast column value to correct data type and size
• Persisting objects to tables requires application code
– Write it manually, or
– Use code generator, or
– Use runtime O/R mapper
Bennett Adelson
What You Are -> How You Query
• Collections
– Index Looping
– IEnumerable (For … Each looping)
– Sort() and Find() methods
• Relational Data
– SQL
• XML
– XPATH
– XQUERY
• Active Directory
– LDAP
– ADSI
• Etc …
Bennett Adelson
The Realms of LINQ
• System.Query namespace
– Provides default implementation of standard query
operators for IEnumerable and IEnumerable<T>
– Uses in-memory iterators
• System.Data.DLinq namespace
– Translates standard query operators into SQL that
executes on database server
• System.Data.XLinq namespace
– A new XML API that supports LINQ as alternative to
XPath and XQuery
• Third Party Extensions
– Further specialization of standard query operators
– Creation of new query operators
Bennett Adelson
Hello, LINQ
Bennett Adelson
History of LINQ
• Conceived by Anders Hejlsberg, creator
of C#
• September 2005 – First technology
preview for C# 2.0 (Beta 2) released at
PDC
• November 2005 – Update of PDC CTP
for C# 2.0 RTM
• January 2006 – First technology
preview for Visual Basic 8.0 RTM
• ??? – Future CTPs, Betas and RTM
Bennett Adelson
Getting LINQ
• Download latest version from
– http://msdn.microsoft.com/netframework/future/linq
• As of 2/14/2005, the latest versions are
– C#: November 2005 Technology Preview
– VB: January 2006 Technology Preview
– Both require
• WinXP SP2
• RTM of VS.NET 2005 (or C#/VB Express)
• RTM of SQL Server 2005 (or Express)
– Don’t install on a working box! Use a VM if possible.
• C# IDE integration doesn’t install by default.
See instructions in ReadMe.htm
– Make sure Options - Project Templates are set to
default locations under My Documents
Bennett Adelson
Creating a LINQ Project in Visual
Studio
• With the current CTPs, you can only use
LINQ in Visual Studio if you create a
LINQ Project
• Behind the scenes, this causes VS to
use a special LINQ-enabled compiler
Bennett Adelson
Hello, LINQ in C#
using System.Query;
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNums =
from n in numbers
where n < 5
select n;
foreach (var num in lowNums ) {
Console.WriteLine(num);
}
Bennett Adelson
Hello, LINQ in VB
Option Strict On
Imports System.Query
Dim numbers as Integer() = _
{ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }
Dim lowNums = _
Select n _
From n In numbers _
Where n < 5
For Each num In lowNums
Console.WriteLine(num)
Next
Bennett Adelson
In C# SELECT and FROM Reversed
They Are
• In C# 3.0, the IDE still doesn’t do
background compilation, so it has to
parse code line-by-line
– Putting SELECT before FROM would prevent
IntelliSense from knowing what can be
SELECTed
• Visual Basic can put SELECT first
because its IDE supports background
compilation
Bennett Adelson
Standard Query Operators
• These work similarly to their SQL
counterparts
–
–
–
–
–
–
–
Select
Where
OrderBy/ThenBy
OrderByDescending/ThenByDescending
GroupBy
Count
Sum/Min/Max/Average
Bennett Adelson
Positional Query Operators
• First/FirstOrDefault
– Returns first element satisfying a predicate
• ElementAt
– Returns element at a numeric position
• Take/Skip
– Returns all elements before/after a numeric position
• TakeWhile/SkipUntil
– Returns elements while predicate satisfied
• Reverse
– Returns elements in reverse order
• Range
– Returns subset of elements in a range
Bennett Adelson
Set Based Query Operators
• Combine two sets of elements
– Union
• Returns all distinct elements in both sets
– Intersection
• Returns only elements belonging to both sets
– Except
• Returns elements in Set A but not in Set B
– Repeat
• Returns multiple copies of a set of elements
– Distinct
• Removes duplicate elements
Bennett Adelson
Projection: Creating New Types To
Hold Query Results
• The result of the query can be an IEnumerable<T> of a
new type custom-created to hold the data
int[] digits = { 0, 1, 2 };
string[] stooges = { “Moe”, “Larry”, “Curly” };
var v = from n in digits
select new { Index = n, Name = stooges[n] };
foreach ( var stooge in v )
Console.WriteLine( “Stooge {0} is {1}”,
stooge.Index, stooge.Name )
Bennett Adelson
Projection That Creates a Nested
1-Many Collection
• A query can be nested inside another query to produce a 1Many Collection
var q = from c in db.Customers
where c.City == "London"
select new {
c.CompanyName,
c.Phone,
OrderDates = (
from o in c.Orders
select o.OrderDate)
.Take(5) };
foreach( var c in q ) {
Console.WriteLine( c.CompanyName );
foreach( od in c.OrderDates )
Console.WriteLine( od )
}
Bennett Adelson
What Can You Query with LINQ?
• Any object that implements IEnumerable<T>
– Single-dimension arrays
– List<T>
– Etc
• Any object that implements non-generic
IEnumerable
– Use OfType<T> extension method to convert it to
IEnumerable<T>
ArrayList objectList = GetAnArrayList();
IEnumerable<int> ints = objectList.OfType<int>;
IEnumerable<int> smallInts = = ints.Where<int>(
i => i < 5 );
Bennett Adelson
LINQ Queries Are Lazy
• Assigning a query to an IEnumerable<T>
variable doesn’t execute the query
• When user iterates over members of the
collection, each query operator executes as
many times as needed to retrieve the next
element
– Hence the data can change while elements are still
being retrieved
• Use .ToList<T> or .ToArray<T> to force
iteration over the entire query in one
statement
– Creates a snapshot copy of the original data
Bennett Adelson
Modifying a Query Before Execution
• Since execution is deferred, query can be
modified any number of times before iteration
var v = from city in cities
where city.Name.Length > 3
select city
if (orderAscending)
v = from city in v
orderBy city.Name
select city
foreach (city in v) { …
Bennett Adelson
LINQ Internals
Bennett Adelson
LINQ Internals
• LINQ is built from language features that are
useful in their own right
– .NET 2.0 Features
•
•
•
•
•
Generics
Anonymous Methods
Iterators
Static Classes
SQL Data Types
– New Language Features
• Compile-Time Type Inference
• Object and Array Initializers
• Anonymous Types
• Extension Methods
• Lambda Expressions
• Expression Trees
Bennett Adelson
LINQ Is a Compiler Technology
• LINQ depends on compiler and library
technology, not CLR innovations
– The current CTPs run on the standard .NET
2.0 CLR
• At least theoretically LINQ could RTM
before the .NET 3.0 CLR
• Each compiler implements LINQ
differently, if at all
Bennett Adelson
Compile-Time Type Inference
•
No need to declare a variable’s type
•
C#
•
VB
•
VB late binding requires Option Strict Off and Object variable
dim o as Object = “World”
o = 123
‘ no error
– Compiler infers it from the object assigned to it
– Because the type inference happens at compile time, this is still 100% typesafe
var v = “Hello”;
v = 42;
var w;
// v is type string
// ERROR, type mismatch
// ERROR, what type is it?
dim v = “Hello”
v = 123
dim w
‘ v is type String
‘ ERROR, type mismatch
‘ ERROR, even if Option Strict Off
Bennett Adelson
Object Initializers
• Allow creation of an object and initialization of its public data
in one line of code, without calling a multi-argument
constructor
• Used to create objects in lambda expressions and expression
trees
• C#
Class Person {
public string Name;
public int Age;
}
Person p = new Person { Name = “Ralph”, Age = 23 };
• VB
Dim p = New Person { .Name = “Ralph”, .Age = 23 }
Bennett Adelson
Array Initializers
Person[] people =
{
new
new
new
new
new
Person
Person
Person
Person
Person
{
{
{
{
{
Name
Name
Name
Name
Name
=
=
=
=
=
“Bill”, Age = 42},
“Namita”, Age = 29},
“Juan”, Age = 47},
“Alice”, Age = 31},
“Al”, Age = 22},
};
Bennett Adelson
Anonymous Types
• Compiler creates a type that matches the
arguments used in an initializer
• Type has only public fields, no methods
• C#
var p = new { Name = “Ralph”, Age = 23 };
• VB
Dim p = New { .Name = “Ralph”, .Age = 23 }
Bennett Adelson
Anonymous Types and Type Safety
• Though unnamed in the source code, the type
is fully defined in the IL, so there is no loss of
type safety or IDE tool support
– However, the type cannot be referenced outside its
assembly
var fred = new { Name = "Fred", Age = 23 };
var barney = new { Name = "Barney", Age = 24 };
fred = barney;
// OK, types are the same
fred = 42;
// ERROR, type mismatch
Bennett Adelson
Projection: Creating Anonymous
Types Out of Other Types
var customer = {
Name = “Fred Blogs”, Zip = “44125” };
var product = {
Name = “Widget”, Price = 16.99 };
var order = { customer.Name,
Product = product.Name,
product.Price,
Quantity = 12 };
Console.WriteLine(
“Customer: {0}, Product: {1}, Total: {2}”,
order.Name, order.Product,
order.Price x order.Quantity );
Bennett Adelson
Extension Methods
• The Problem: How to add a method to a
type you don’t control and can’t
subtype?
– Example: How to add Standard Query
Operators to IEnumerable<T>, without
deriving a new interface?
• Solution: Extension methods aren’t
really members of the target type, but
the syntax makes them look that way
– Compile-time type safety is preserved
Bennett Adelson
Creating Extension Methods
• Define a Static Class
• Define a static method whose first argument is
an object of type T
• [System.Runtime.CompilerServices.Extension]
attribute on first parameter tells CLR the
method is an Extension
– C# binds the attribute to keyword “this”
• Create a T object, and call the method as if it
were a member of the object
• When the compiler fails to find the method
among T’s members, it searches for an
extension method in scope with matching
name and signature
Bennett Adelson
Extension Method in C#
public static class StringExtender
{
const string vowels = "aeiouy";
// keyword “this” indicates the CompilerServices.ExtensionAttribute
public static string NoVowels( this string s ) {
StringBuilder sb = new StringBuilder();
foreach ( char c in s )
if ( -1 == vowels.IndexOf(c) )
sb.Append(c);
return sb.ToString();
}
}
…
static void Main(){
string s = "abcde";
// string appears to have a NoVowels() method
Console.WriteLine( s.NoVowels() );
// outputs “bcd”
}
Bennett Adelson
Extension Method in VB
<System.Runtime.CompilerServices.Extension()> _
Public Module StringExtender
Const vowels As String = "aeiouy"
<System.Runtime.CompilerServices.Extension()> _
Public Function NoVowels(ByVal [Me] As String) As String
Dim sb As New System.Text.StringBuilder()
For Each Dim c As Char In [Me]
If -1 = vowels.IndexOf(c) Then
sb.Append(c)
End If
Next
Return sb.ToString()
End Function
End Module
Bennett Adelson
LINQ Implements Standard Query
Operators As Extension Methods
namespace System.Query {
}
}
public static class Sequence {
…
public static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, bool> predicate)
{
foreach (T item in source)
if (predicate(item))
yield return item;
}
Bennett Adelson
Replacing LINQ Extension Methods
• Extension Methods have lower priority of resolution than
type members
– So if a type has a member with same signature as a LINQ
extension method, the type’s member will be called, and
there is no ambiguity
class MyList : IEnumerable<int> {
public IEnumerable<int> Where(
Func<int,bool> filter) {
// LINQ queries on MyList objects will call this
// instead of Sequence.Where()
}
• This is how DLINQ and XLINQ replace the default LINQ
implementations of the Standard Query Operators
Bennett Adelson
Lambda Expressions
• Extends .NET 2.0 Anonymous Methods with
even simpler syntax
– Like anonymous methods, there’s no need to call an
existing method on a type
– Like anonymous methods, local variables stay in
scope when the lambda expression executes
– No need for “delegate” keyword
– No need to restate the parameter types
– Syntax:
parameters => expression
• As of February 2006, C# supports lambda
expressions, but VB does not and may never
Bennett Adelson
Comparing Delegates, Anonymous
Methods and Lambda Expressions
class LotsOfUppers {
delegate string MyDelegate(string s);
private static string MyFunc(string s) {return s.ToUpper();}
static void Main() {
Console.WriteLine( MyFunc(“Calling a Function”);
MyDelegate del;
del = new MyDelegate( MyFunc );
Console.WriteLine( del(“Calling a .NET 1.0 Delegate") );
del = delegate( string s ) { return s.ToUpper(); };
Console.WriteLine( del(“Calling a .NET 2.0 Anonymous Method") );
}
}
del = s => s.ToUpper() ;
Console.WriteLine( del(“Calling a .NET 3.0 Lambda Expression") );
Bennett Adelson
Parameters to Lambda Expressions
• Implicitly typed parameter
– Parentheses optional for single implicitly typed parameter
x => x + 1
(x) => x + 1
• Explicitly typed parameter
(int x) => x + 1
int x => x + 1
//ERROR, no parens
• Implicitly typed, multiple parameters
(x, y) => x * y
x, y => x * y
//ERROR, no parens
• No parameters
– Not supported in current CTP
() => Console.WriteLine()
Bennett Adelson
Lambda Body Can Be Single
Expression or Statement Block
• Expression body
x => x + 1
• Statement block body
x => { return x + 1; }
– Statement body can have arbitrarily many lines
– As of February 2006, the current CTP does not
support statement bodies in lambdas. You must use
.NET 2.0 anonymous methods instead.
• Only expression body lambdas can compile
into expression trees
Bennett Adelson
Hello, LINQ With Explicit Lamba
Expressions
using System.Query;
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNums =
numbers
.Where(n => n < 5)
.Select (n => n);
Bennett Adelson
Expression Trees
• When compiled as a delegate, lambda
expression produces IL code just like a classic
delegate or anonymous method
– In that case, only benefit is simpler syntax
• Or, lambda expression can be compiled to an
expression tree
– An efficient in-memory data structure that makes the
structure of the expression transparent and explicit
– This allows the expression to be evaluated, copied
and modified without using reflection
– DLINK uses expression trees to construct SQL
statements that can execute on database server
Bennett Adelson
Expression Tree Example
delegate bool MyDelegate(int i);
…
MyDelegate il = n => n < 5;
Console.WriteLine( il(7));
// OK, il is an executable delegate
Expression<MyDelegate> ex = n => n > 4;
Console.WriteLine( ex(7) ); // ERROR, ex not executable
BinaryExpression body = (BinaryExpression)ex.Body;
ParameterExpression left = (ParameterExpression)body.Left;
ConstantExpression right = (ConstantExpression)body.Right;
Console.WriteLine( "BODY: {0}, LEFT: {1}, RIGHT: {2}",
body, left, right );
// BODY: GT(n, 4), LEFT: n, RIGHT: 4
Bennett Adelson
Types of Expression Tree Nodes
•
•
•
•
•
•
•
•
•
•
•
•
•
•
BinaryExpression
ConstantExpression
FuncletExpression
InvocationExpression
LambdaExpression
MemberExpression
MethodCallExpression
NAryExpression
NewArrayExpression
NewExpression
ParameterExpression
TernaryExpression
TypeBinaryExpression
UnaryExpression
Bennett Adelson
DLINQ
Bennett Adelson
DLINQ: Using LINQ with Relational
Databases
• DLINQ is the next version of ADO.NET
• DLINQ supports “class-first” design
– Write classes
– Use attributes to map classes to tables and
properties to columns
• DLINQ supports both data retrieval and
updates
• Currently DLINQ only supports SQL
Server 2005
Bennett Adelson
How DLINQ Works
• DLINQ does not execute queries; the
database server does
– DLINQ queries are compiled to expression
trees rather than IL
– At runtime DLINQ parses the expression
trees, constructs equivalent T-SQL
statements, and sends them to the server
• Statements are not executed as SQL/CLR
– Thus .NET expressions that cannot translate
to T-SQL are disallowed
• Can’t call .NET functions with no T-SQL equivalent
Bennett Adelson
Attribute-Based DLINQ Mapping
[Table(Name=“Customers”)]
class Customer
{
[Column(ID=true)]
public string CustomerID;
[Column]
public string City;
[Column]
public string PostalCode;
}
Bennett Adelson
The DataContext
• Like an xxxConnection in ADO.NET, the DataContext
class binds table objects to a database connection
– By making the tables members of a DataContext subclass,
the binding takes place transparently
class NorthWind : DataContext
{
public Table<Customer> Customers;
public Table<Order> Orders;
public NorthWind( string connection )
: base( connection ) {}
}
Bennett Adelson
Hello, DLINQ
NorthWind db = new NorthWind(
connectonString );
var londonCustomers =
from c in db.Customers
where c.City = “London”
select c;
foreach ( Customer c in londonCustomers ) {
Console.WriteLine( c.ContactName ); }
Bennett Adelson
When Does the Query Go to the
Database?
• In the previous example, the query
goes to the database at the first
iteration of the foreach loop
– All the rows come back, so subsequent
iterations retrieve the data from memory
• If the same query is iterated in a
second foreach loop, the query will go
to the database a second time
– To prevent this, copy the result of the query
with ToList<T>() or ToArray<T>() and
iterate over the copy
Bennett Adelson
Declaring Parent-Child Relationship
[Table(Name="Customers")]
public class Customer
{
[Column(Id=true)]
public string CustomerID;
...
private EntitySet<Order> _Orders;
}
[Association(Storage="_Orders",
OtherKey="CustomerID")]
public EntitySet<Order> Orders
{
get { return this._Orders; }
set { this._Orders.Assign(value); }
}
Bennett Adelson
Declaring Child-Parent Relationship
[Table(Name="Orders")]
public class Order
{
[Column(Id=true)]
public int OrderID;
[Column]
public string CustomerID;
private EntityRef<Customer> _Customer;
}
[Association(Storage="_Customer", ThisKey="CustomerID")]
public Customer Customer
{
get { return this._Customer.Entity; }
set { this._Customer.Entity = value; }
}
Bennett Adelson
Attribute-Based Joins
// parent to children
var q =
from c in db.Customers, o in c.Orders
where c.City == "London“
select new { c, o };
// child to parent
var q =
from o in db.Orders
where o.Customer.City == "London“
select new { c = o.Customer, o };
Bennett Adelson
Controlling How Much Joined Data
To Retrieve
• By default, a DLINQ query only retrieves what
is requested in Select clause.
• Use .Including() method to retrieve data for
related objects at same time
var q = (
from c in db.Customers
where c.City == “London”
select c)
.Including(c => c.Orders);
Bennett Adelson
Executing Part of a Query on
Database Server and Part Locally
• .ToSequence() tells DLINK to run the query on the
database server up to that point, then run the rest of
the query locally
var q =
from c in db.Customers
where c.City == “London”
select new { c.ContactName, c.Phone };
// can’t call these functions on database server
var q2 =
from c in q.ToSequence()
select new MyType {
Name = DoNameProcessing(c.ContactName),
Phone = DoPhoneProcessing(c.Phone) };
Bennett Adelson
Updating the Database
• DataContext tracks changes to Tables in memory.
• SubmitChanges() submits them to database.
Northwind db = new Northwind("northwnd.mdf");
var cust = db.Customers.First(c => c.ID == “ACKE");
cust.ContactName = "New Contact";
Order order = cust.Orders[0];
db.Orders.Remove(order);
order = new Order { OrderId = 12345 };
cust.Orders.Add(order);
db.SubmitChanges();
Bennett Adelson
The SQLMetal Utility
• Command line utility provided with current
CTP to automate creation of annotated entity
classes to match a database schema
SqlMetal /server:.\SQLExpress
/database:Northwind
/delayfetch
/pluralize
/namespace:nwind
/code:Northwind.cs
Bennett Adelson
XLINQ
Bennett Adelson
XLINQ: Using LINQ with XML
• Intended to be a complete XML API with
all features of WC3 DOM
– Simpler and more efficient
• Integrates XML with other data sources
supported by LINQ
• Still supports traditional DOM-style
programming
Bennett Adelson
XLINQ (XElement) vs. DOM
(XmlElement)
• XElement needn’t be part of a “document” object
– An XDocument can be created if needed, but XElements
can exist without it
• XElement.Name property doesn’t include a namespace
prefixes
– XLINQ resolves namespaces and prefixes at serialization
time
• Leaf elements can be cast directly into data
int age = (int)xAge;
// XLINQ
int age = (int)xAge.InnerText; //DOM
• XElement.ToString() outputs the underlying XML
– doh!
Bennett Adelson
Creating XML in Memory with WC3
DOM
XmlDocument doc = new XmlDocument();
XmlElement name = doc.CreateElement("name");
name.InnerText = “Fred Blogs";
XmlElement phone1 = doc.CreateElement("phone");
phone1.SetAttribute("type", "home");
phone1.InnerText = "206-555-0144";
XmlElement postal = doc.CreateElement("postal");
postal.InnerText = "68042";
XmlElement address = doc.CreateElement("address");
address.AppendChild(postal);
XmlElement contact = doc.CreateElement("contact");
contact.AppendChild(name);
contact.AppendChild(phone1);
contact.AppendChild(address);
XmlElement contacts = doc.CreateElement("contacts");
contacts.AppendChild(contact);
doc.AppendChild(contacts);
Bennett Adelson
Creating XML in Memory with XLINQ
in C#
XElement contacts =
new XElement("contacts",
new XElement("contact",
new XElement("name", “Fred Blogs"),
new XElement("phone", "206-555-0144",
new XAttribute("type", "home")),
new XElement("phone", "425-555-0145",
new XAttribute("type", "work")),
new XElement("address",
new XElement("street1", "123 Main St"),
new XElement("city", "Mercer Island"),
new XElement("state", "WA"),
new XElement("postal", "68042")
)
)
);
•
This is accomplished thanks to the XElement constructor:
public XElement(XName name, params object[] contents)
Bennett Adelson
Traditional XML Programming with
XLINQ
•
Populate from a string using XElement.Parse(), or from a file,
XmlReader or TextReader using XElement.Load()
XDocument doc = XElement.Load(@"c:\myContactList.xml");
•
Traverse the object hierarchy and manipulate contents
XElement contacts = doc.Element(“root”).Element(“contacts”);
foreach ( XElement contact in contacts.Content() )
{
if ( “Joe” == (string)contact.Element(“name”)
{
contact.Element(“zip").ReplaceContent(“44125");
}
}
Bennett Adelson
LINQ Query on XLINQ Object
XDocument doc = XDocument.Load("customers.xml");
var query =
from c in
doc.Element("Root")
.Elements("Customers")
where
c.Element("FullAddress")
.Element("Country")
.Value == "Germany"
select c;
foreach (XElement result in query)
Console.WriteLine(result);
Bennett Adelson
Casting and Filtering on an Attribute
string xml = "<order >" +
"<item price='150'>Motor</item>" +
"<item price='50'>Cable</item>" +
"<item price='50'>Modem</item>" +
"<item price='250'>Monitor</item>" +
"<item price='10'>Mouse</item>" +
"</order>";
XElement order = XElement.Parse(xml);
var query =
from
i in order.Elements("item")
where
(int)i.Attribute("price") > 100
select i;
foreach(var result in query)
Console.WriteLine("Expensive Item {0} costs {1}",
(string)result,
(string)result.Attribute("price"));
Bennett Adelson
Transforms with XLINQ
XDocument doc =XDocument.Load("customers.xml");
var header =new [] {
new XElement("th", "customer id"),
new XElement("th", "contact name")};
var rows =
from
customer in doc.Descendants("Customers")
where
(decimal)customer.Element(“salesYTD”) > 500,000
select
new XElement("tr",
new XElement("td", (string)customer.Attribute("CustomerID")),
new XElement("td", (string)customer.Element("Contact")));
XElement html = new XElement("html",
new XElement("table", header, rows));
Console.Write(html);
Bennett Adelson
XLINQ Enhancement for VB Only:
XML Literals
• VB can create XElements in memory with XElement
constructors, same as in C#
Dim contacts As XElement = _
New XElement("contacts", _
New XElement("contact", _
New XElement("name", “Fred Blogs"), _
New XElement("phone", "206-555-0144", _
New XAttribute("type", "home")), _
New XElement("phone", "425-555-0145", _
New XAttribute("type", "work")), _
New XElement("address", _
New XElement("street1", "123 Main St"), _
New XElement("city", "Mercer Island"), _
New XElement("state", "WA"), _
New XElement("postal", "98040"))))
Bennett Adelson
Creating an XElement from an XML
Literal
•
Wouldn’t you rather do this?
Dim contacts As XElement = _
<contacts>
<contact>
<name>Fred Blogs</name>
<phone type="home">206-555-0144</phone>
<phone type="work">425-555-0145</phone>
<address>
<street1>123 Main St</street1>
<city>Mercer Island</city>
<state>WA</state>
<postal>98040</postal>
</address>
</contact>
</contacts>
•
•
Compiler converts this to same code as previous slide
No need for line continuation characters within the XML
Bennett Adelson
VB XML Literals Can Contain
Expressions Evaluated at Runtim
•
Element Value from Variable
Dim name As XElement =
<name><%=someVariable%></name>
•
Element Value from Function Call
Dim name As XElement =
<name><%=SomeFunction()%></name>
•
Attribute Value from Variable
Dim phone as XElement =
<phone type=(someVariable)>234-5678</phone>
•
Element Name from Variable
Dim contact as XElement =
<contact>
<(someVariable)>Foo</>
</contact>
Bennett Adelson
Embedding XLINQ Query Inside XML
Literal
Dim contacts as XElement = _
<contacts>
<%=
_Select _
<contact>
<name><%=CStr(c.name(0))%></name>
<%=c.phone %>
<address><%=c.address%></address>
<contact/>
From c In contacts
%>
</contacts>
Bennett Adelson
XLINQ Enhancement for VB Only:
Late Bound XML
• VB can apply three XPath-style
navigational operators to an XElement
– Child axis operator: “.”
– Descendants axis operator: “…”
– Attribute axis operator: “@”
• This makes the syntax simpler than the
index-based XElement methods
– However, VB applies these operators at
runtime using late binding, so their use is
not type-safe
Bennett Adelson
Late Bound XML Example
For Each Dim phone In contact.phone
Console.WriteLine(CStr(phone.@type))
Next
Console.WriteLine(CStr(contacts...city(0)))
• … which is equivalent to
For Each Dim phone In Contact.Element("phone")
Console.WriteLine(CStr(phone.Attribute("type"))
Next
Console.WriteLine( _
CStr(ElementAt(contact.Descendants("city"),0)))
Bennett Adelson
Schema-Aware XLINQ?
• Current XLINQ CTPs are unaware of XML
structure at compile time
– Nodes must be located at run time using indexes,
and values must be cast to correct data type
string zip = (string)name.Element(“zip”)
• Microsoft is investigating possibility of using
XML Schemas at compile time/design time to
construct “strongly typed” XML objects
string zip = name.zip
Bennett Adelson
The Adventure Continues!
Bennett Adelson
LINQ Resources on the Web
• LINQ Official Site
– http://msdn.microsoft.com/netframework/f
uture/linq
• Barry Gervin’s LINQ Resources
– http://objectsharp.com/blogs/barry/archive
/2005/09/13/3395.aspx
Bennett Adelson
Q&A
Bennett Adelson