Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 53

C# 3.0 and .NET 3.

5: A Brief
Overview

Pavel Ježek
DISTRIBUTED SYSTEMS RESEARCH GROUP
http://nenya.ms.mff.cuni.cz

CHARLES UNIVERSITY PRAGUE


Faculty of Mathematics and Physics
C# 3.0 Features
• Implicitly Typed Local Variables
• Extension Methods
• Lambda Expressions
• Object Initializers
• Collection Initializers
• Anonymous Types
• Expression Trees
• Query Expressions

• Beta versions of C# 3.0 compiler, libraries and Visual Studio 2005


integration can be downloaded at:
http://download.microsoft.com/download/4/7/0/4703eba2-78c4-4b09-8912-
69f6c38d3a56/linq%20preview.msi
• .NET Framework 3.5 Beta 1:
http://www.microsoft.com/downloads/details.aspx?FamilyId=E3715E6F-
E123-428B-8A0F-028AFB9E0322&displaylang=en
• Visual Studio code name “Orcas” Beta 1:
http://msdn2.microsoft.com/en-us/vstudio/aa700831.aspx
Pavel Ježek
C# 3.0 and .NET 3.5
Implicitly Typed Local Variables
Examples:
var i = 5;
var s = "Hello";
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();

Are equivalent to:


int i = 5;
string s = "Hello";
double d = 1.0;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();

Errors:
var x; // Error, no initializer to infer type from
var y = {1, 2, 3}; // Error, collection initializer not permitted
var z = null; // Error, null type not permitted

Pavel Ježek
C# 3.0 and .NET 3.5
Extension Methods
• Declaration:
public static partial class Extensions {
public static int ToInt32(this string s) {
return Int32.Parse(s);
}
}

• Usage:
string s = "1234";
int i = s.ToInt32(); // Same as Extensions.ToInt32(s)

• Instance methods take precedence over extension methods


• Extension methods imported in inner namespace declarations take
precedence over extension methods imported in outer namespace
declarations

Pavel Ježek
C# 3.0 and .NET 3.5
Extension Methods (2)
• Declaration:
public static partial class Extensions {
public static T[] Slice<T>(this T[] source, int index, int count) {
if (index < 0 || count < 0 || source.Length – index < count)
throw new ArgumentException();
T[] result = new T[count];
Array.Copy(source, index, result, 0, count);
return result;
}
}

• Usage:
int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] a = digits.Slice(4, 3); // Same as Extensions.Slice(digits, 4, 3)

Pavel Ježek
C# 3.0 and .NET 3.5
Lambda Expressions
• Example of C# 2.0 anonymous method:
class Program {
delegate T BinaryOp<T>(T x, T y);

static void Method<T>(BinaryOp<T> op, T p1, T p2) {


Console.WriteLine(op(p1, p2));
}

static void Main() {


Method(delegate(int a, int b) { return a + b; }, 1, 2);
}
}
• C# 3.0 lambda expressions provide further simplification:
class Program {
delegate T BinaryOp<T>(T x, T y);

static void Method<T>(BinaryOp<T> op, T p1, T p2) {


Console.WriteLine(op(p1, p2));
}

static void Main() {


Method((a, b) => a + b, 1, 2);
}
}

Pavel Ježek
C# 3.0 and .NET 3.5
Lambda Expressions (2)
• Expression or statement body
• Implicitly or explicitly typed parameters
• Examples:
x => x + 1 // Implicitly typed, expression body
x => { return x + 1; } // Implicitly typed, statement body
(int x) => x + 1 // Explicitly typed, expression body
(int x) => { return x + 1; } // Explicitly typed, statement body
(x, y) => x * y // Multiple parameters
() => Console.WriteLine() // No parameters

• A lambda expression is a value, that does not have a type but can be
implicitly converted to a compatible delegate type

delegate R Func<A,R>(A arg);


Func<int,int> f1 = x => x + 1; // Ok
Func<int,double> f2 = x => x + 1; // Ok
Func<double,int> f3 = x => x + 1; // Error – double cannot be
// implicitly converted to int

Pavel Ježek
C# 3.0 and .NET 3.5
Lambda Expressions (3)
• Lambda expressions participate in inference process of type
arguments of generic methods
• In initial phase, nothing is inferred from arguments that are lambda
expressions
• Following the initial phase, additional inferences are made from
lambda expressions using an iterative process

Pavel Ježek
C# 3.0 and .NET 3.5
Lambda Expressions (4)
• Generic extension method example:
public class List<T> : IEnumerable<T>, … { … }
public static class Sequence {
public static IEnumerable<S> Select<T,S>(
this IEnumerable<T> source,
Func<T, S> selector)
{
foreach (T element in source) yield return selector(element);
}
}
• Calling extension method with lambda expression:
List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);
• Rewriting extension method call:
IEnumerable<string> names = Sequence.Select<T, S>(customers, c => c.Name);
• T type argument is inferred to Customer based on source argument type
Sequence.Select<Customer, S>(customers, c => c.Name)
• c lambda expression argument type is infered to Customer
Sequence.Select<Customer, S>(customers, (Customer c) => c.Name)
• S type argument is inferred to string based on return value type of the lambda expression
Sequence.Select<Customer, string>(customers, (Customer c) => c.Name)

Pavel Ježek
C# 3.0 and .NET 3.5
Object Initializers
• Class representing a point:
public class Point {
private int x, y;

public int X { get { return x; } set { x = value; } }


public int Y { get { return y; } set { y = value; } }
}

• New instance can be created using object initializer:


var a = new Point { X = 0, Y = 1 };

• Which is equivalent to:


var a = new Point();
a.X = 0;
a.Y = 1;

Pavel Ježek
C# 3.0 and .NET 3.5
Object Initializers (2)
• Class representing a rectangle:
public class Rectangle {
private Point p1, p2;
public Point P1 { get { return p1; } set { p1 = value; } }
public Point P2 { get { return p2; } set { p2 = value; } }
}

• New instance can be created using object initializer:


var r = new Rectangle {
P1 = new Point { X = 0, Y = 1 },
P2 = new Point { X = 2, Y = 3 }
};

• Which is equivalent to:


var r = new Rectangle();
var __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
r.P1 = __p1;
var __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
r.P2 = __p2;
Pavel Ježek
C# 3.0 and .NET 3.5
Object Initializers (3)
• Class representing a rectangle with constructor that allocates p1 and p2:
public class Rectangle {
private Point p1 = new Point();
private Point p2 = new Point();
public Point P1 { get { return p1; } }
public Point P2 { get { return p2; } }
}

• New instance can be created using object initializer:


var r = new Rectangle {
P1 = { X = 0, Y = 1 },
P2 = { X = 2, Y = 3 }
};

• Which is equivalent to:


var r = new Rectangle();
r.P1.X = 0;
r.P1.Y = 1;
r.P2.X = 2;
r.P2.Y = 3;

Pavel Ježek
C# 3.0 and .NET 3.5
Collection Initializers
• Example:
List<int> digits = new List<int> { 0, 1, 2};

• Is equivalent to:
List<int> digits = new List<int>();
digits.Add(0);
digits.Add(1);
digits.Add(2);

• List<T> has to implement System.Collections.Generic.ICollection<T> interface with


the Add(T) method

Pavel Ježek
C# 3.0 and .NET 3.5
Combining Object and Collection Initializers
• Class representing a contact with name and list of phone numbers:
public class Contact {
private string name;
private List<string> phoneNumbers = new List<string>();
public string Name { get { return name; } set { name = value; } }
public List<string> PhoneNumbers { get { return phoneNumbers; } }
}

• List of contacts can be created and initialized with:


var contacts = new List<Contact> {
new Contact {
Name = "Chris Smith",
PhoneNumbers = { "206-555-0101", "425-882-8080" }
},
new Contact {
Name = "Bob Harris",
PhoneNumbers = { "650-555-0199" }
}
};

• Which is equivalent to:


var contacts = new List<Contact>();
var __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
contacts.Add(__c1);
var __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
Pavel Ježek contacts.Add(__c2);
C# 3.0 and .NET 3.5
Anonymous Types
• Following expression:
new { p1 = e1 , p2 = e2 , … pn = en }
• Can be used to define an anonymous type :
class __Anonymous1 {
private T1 f1 ;
private T2 f2 ;

private Tn fn ;

public T1 p1 { get { return f1 ; } set { f1 = value ; } }


public T2 p2 { get { return f2 ; } set { f2 = value ; } }

public T1 p1 { get { return f1 ; } set { f1 = value ; } }
}
• And create its instance using object initializer

• Different anonymous object initilizers that define properties with same names in the same order
generate the same anonymous type:
var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

Pavel Ježek
C# 3.0 and .NET 3.5
Expression Trees
• Permit lambda expressions to be represented as data structures instead of
executable code
• Lambda expression convertible to delegate D (assignment causes code generation)
is also convertible to expression tree (abstract syntax tree) of type
System.Query.Expression<D> (assignment causes expression tree generation)

Func<int,int> f = x => x + 1; // Code


Expression<Func<int,int>> e = x => x + 1; // Data

• Full specification is not available yet (May 2006)

Pavel Ježek
C# 3.0 and .NET 3.5
ADO.NET: Connection-oriented Access
1.) Declare connection
try {
1.) Request connection to database

2.) Execute SQL commands

3.) Process result

4.) Release Resources


} catch ( Exception ) {
Handle exception
} finally {
try {
4.) Close connection
} catch (Exception)
{ Handle exception }
}
© University of Linz, Institute for System Software, 2004
Pavel Ježek published under the Microsoft Curriculum License
C# 3.0 and .NET 3.5 (http://www.msdnaa.net/curriculum/license_curriculum.aspx)
ADO.NET Example: EmployeeReader (1)
using System;
using System.Data;
using System.Data.OleDb;

public class EmployeeReader {


public static void Main() {

1) Declare and request connection to database


string connStr = "provider=SQLOLEDB; data source=(local)\\NetSDK; " +
"initial catalog=Northwind; user id=sa; password=; ";
IDbConnection con = null; // declare connection object
try {
con = new OleDbConnection(connStr); // create connection object
con.Open(); // open connection

2) Execute SQL commands


// create SQL command
IDbCommand cmd = con.CreateCommand();
cmd.CommandText = "SELECT EmployeeID, LastName, FirstName” +
“FROM Employees";
// execute SQL command; result is an OleDbDataReader
IDataReader reader = cmd.ExecuteReader();
© University of Linz, Institute for System Software, 2004
Pavel Ježek published under the Microsoft Curriculum License
C# 3.0 and .NET 3.5 (http://www.msdnaa.net/curriculum/license_curriculum.aspx)
ADO.NET Example: EmployeeReader (2)
3) Read and process data rows
IDataReader reader = cmd.ExecuteReader();
object[] dataRow = new object[reader.FieldCount];

while (reader.Read()) {
int cols = reader.GetValues(dataRow);
for (int i = 0; i < cols; i++) Console.Write("| {0} " , dataRow[i]);
Console.WriteLine();
}

4) Release resources and close connection


reader.Close();
} catch (Exception e) {
Console.WriteLine(e.Message);
} finally {
try {
if (con != null)
// ----- close connection
con.Close();
} catch (Exception ex) { Console.WriteLine(ex.Message); }
} } }

© University of Linz, Institute for System Software, 2004


Pavel Ježek published under the Microsoft Curriculum License
C# 3.0 and .NET 3.5 (http://www.msdnaa.net/curriculum/license_curriculum.aspx)
Query Expressions
• Query expressions or LINQ (Language INtergrated Queries) are the key feature of
.NET 3.5
• Query expressions are translated to method calls – works on classes like:

delegate R Func<A,R>(A arg);

class C<T>
{
public C<T> Where(Func<T,bool> predicate);
public C<S> Select<S>(Func<T,S> selector);
public C<S> SelectMany<S>(Func<T,C<S>> selector);
public O<T> OrderBy<K>(Func<T,K> keyExpr);
public O<T> OrderByDescending<K>(Func<T,K> keyExpr);
public C<G<K,T>> GroupBy<K>(Func<T,K> keyExpr);
public C<G<K,E>> GroupBy<K,E>(Func<T,K> keyExpr, Func<T,E>
elemExpr);
}

Pavel Ježek
C# 3.0 and .NET 3.5
Query Expressions – Examples
• Query expression:

from c in customers where c.City == "London“ select c

• Gets translated to:

customers.Where(c => c.City == "London")

Pavel Ježek
C# 3.0 and .NET 3.5
Query Expressions – Examples
• Query expression:

from c in customers where c.City == "London“ select c.Name

• Gets translated to:

customers.Where(c => c.City == "London").Select(c => c.Name)

Pavel Ježek
C# 3.0 and .NET 3.5
Query Expressions – Examples
• Query expression:

from c in customers orderby c.Name select new { c.Name, c.Phone }

• Gets translated to:

customers.OrderBy(c => c.Name).Select(c => new { Name = c.Name,


Phone = c.Phone })

Pavel Ježek
C# 3.0 and .NET 3.5
Query Expressions – Examples
• Query expression:

from c in customers
where c.City == "London"
from o in c.Orders
where o.OrderDate.Year == 2005
select new { c.Name, o.OrderID, o.Total }

• Gets translated to:

customers.Where(c => c.City == "London").


SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new {
Name = c.Name, OrderID = o.OrderID, Total = o.Total
})
)

Pavel Ježek
C# 3.0 and .NET 3.5
BCL Extended by Query Expressions
• Set of generic extension methods (Select, Where, OrderBy, … + others) implemented for IEnumerable<T>
interface, example:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numberGroups = from n in numbers group n by n % 5 into g
select new {Remainder = g.Key, Numbers = g.Group};
foreach (var g in numberGroups) {
Console.WriteLine(
"Numbers with a remainder of {0} when divided by 5:", g.Remainder);
);
foreach (int n in g.Numbers) {
Console.WriteLine(n);
} Numbers with a remainder of 0 when divided by 5:
} 5
0
Numbers with a remainder of 4 when divided by 5:
4
• DLinq – Classes for SQL data access using query expressions 9
• XLinq - Classes for XML data access using query expressions Numbers with a remainder of 1 when divided by 5:
1
6
Numbers with a remainder of 3 when divided by 5:
3
8
Numbers with a remainder of 2 when divided by 5:
7
Pavel Ježek
C# 3.0 and .NET 3.5 2
DLinq – Creating Entity Classes
• Only instances of “entity classes”, i.e. “entities”, can be stored in/retrieved from databases

[Table] // Table is named “Customer”


public class Customer
{
public string CustomerID; // Transient data not stored in DB
public string City; // Transient data not stored in DB
}

[Table(Name="Customers")]
public class Customer
{
public string CustomerID; // Transient data not stored in DB
public string City; // Transient data not stored in DB
}

[Table(Name="Customers")]
public class Customer
{
[Column(Id=true)] // Part of database primary key
public string CustomerID;
[Column]
public string City;
}
Pavel Ježek
C# 3.0 and .NET 3.5
DLinq - DataContext
• DataContext is equivalent of ADO.NET connection
• It retrieves data (objects) from the database and submits changes
• It translates requests (queries) for objects into SQL queries

• Using the DataContext to retrieve customer objects whose city is London:

// DataContext takes a connection string


DataContext db = new DataContext("c:\\northwind\\northwnd.mdf");

// Get a typed table to run queries


Table<Customer> Customers = db.GetTable<Customer>();

// Query for customers from London


var q = from c in Customers where c.City == "London“ select c;

foreach (var cust in q) {


Console.WriteLine("id = {0}, City = {1}", cust.CustomerID,
cust.City);
}

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Strongly Typed DataContext
public partial class Northwind : DataContext
{
public Table<Customer> Customers;
public Table<Order> Orders;
public Northwind(string connection): base(connection) {}
}

• Retrieving customer objects whose city is London more easily:

Northwind db = new Northwind("c:\\northwind\\northwnd.mdf");

var q = from c in db.Customers where c.City == "London“ select c;

foreach (var cust in q) {


Console.WriteLine("id = {0}, City = {1}",cust.CustomerID,
cust.City);
}

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Query Execution
• Queries are not imperative – they are not executed immediately

var q = from c in db.Customers


where c.City == "London"
select c;

• Only creates an instance of Query<Customer> type and assigns it to q


• When the application tries to enumerate the contents of a query, it gets executed
(i.e. deferred execution):

// Execute first time


foreach (Customer c in q) Console.WriteLine(c.CompanyName);

// Execute second time


foreach (Customer c in q) Console.WriteLine(c.CompanyName);

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Advantage of Deferred Execution
• Partial query contruction:

var q = from c in db.Customers


where c.City == "London"
select c;

if (orderByLocation) {

q = from c in q
orderby c.Country, c.City
select c;

} else if (orderByName) {

q = from c in q
orderby c.ContactName
select c;

foreach (Customer c in q) Console.WriteLine(c.CompanyName);

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Avoiding Deferred Execution
• Query can be converted to standard collection classes using ToList() or
ToArray() Standard Query Operators (or converted to DataSet’s DataTable using
ToDataTable()) – leads to immediate execution:

var q = from c in db.Customers


where c.City == "London"
select c;

// Execute once using ToList(), ToArray() or ToDataTable()


var list = q.ToList();

foreach (Customer c in list) Console.WriteLine(c.CompanyName);

foreach (Customer c in list) Console.WriteLine(c.CompanyName);

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Defining Relationships
[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); }
}
}

[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; }
}
}

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Querying Across Relationships
• Query using the Orders property to form the cross product between customers and
orders, producing a new sequence of Customer and Order pairs:

var q = from c in db.Customers


from o in c.Orders
where c.City == "London"
select new { c, o };

• Another query producing the same result:

var q = from o in db.Orders


where o.Customer.City == "London"
select new { c = o.Customer, o };

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Modifying Entities
Northwind db = new Northwind("c:\\northwind\\northwnd.mdf");

// Query for a specific customer


string id = "ALFKI";
var cust = db.Customers.Single(c => c.CustomerID == id);

// Change the name of the contact


cust.ContactName = "New Contact";

// Delete an existing Order


Order ord0 = cust.Orders[0];

// Removing it from the table also removes it from the Customer’s list
db.Orders.Remove(ord0);

// Create and add a new Order to Orders collection


Order ord = new Order { OrderDate = DateTime.Now };
cust.Orders.Add(ord);

// Ask the DataContext to save all the changes – generates appropriate SQL command
db.SubmitChanges();

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Creating Simple Databases
[Table(Name="DVDTable")]
public class DVD
{
[Column(Id = true)]
public string Title;
[Column]
public string Rating;
}

public class MyDVDs : DataContext


{
public Table<DVD> DVDs;
public MyDVDs(string connection) : base(connection) {}
}

// Creating a new database


MyDVDs db = new MyDVDs("c:\\mydvds.mdf");
db.CreateDatabase();

// Replacing an existing one


MyDVDs db = new MyDVDs("c:\\mydvds.mdf");

if (db.DatabaseExists()) {
db.DeleteDatabase();
}

db.CreateDatabase();

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Inheritance
[Table]
[InheritanceMapping(Code = "C", Type = typeof(Car))]
[InheritanceMapping(Code = "T", Type = typeof(Truck))]
[InheritanceMapping(Code = "V", Type = typeof(Vehicle), IsDefault = true)]
public class Vehicle {
[Column(IsDiscriminator = true)]
public string Key;
[Column(Id = true)]
public string VIN;
[Column]
public string MfgPlant;
}

public class Car : Vehicle {


[Column]
public int TrimCode;
[Column]
public string ModelName;
}

public class Truck : Vehicle {


[Column]
public int Tonnage;
[Column]
public int Axles;
}

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Inheritance
•Example queries:
var q = db.Vehicle.Where(p => p is Truck);

var q = db.Vehicle.OfType<Truck>();

var q = db.Vehicle.Select(p => p as Truck).Where(p => p != null);


foreach (Truck p in q)
Console.WriteLine(p.Axles);

Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Query Visualizer in VS
static void Main(string[] args) {
Northwnd db = new Northwnd(@"C:\program files\linq preview\data\northwnd.mdf");
var q =
from c in db.Customers
where c.City == "London"
select c;
} // Breakpoint

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Example XML
<contacts>
<contact>
<name>Patrick Hines</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>68042</postal>
</address>
<netWorth>10</netWorth>
</contact>
<contact>
<name>Gretchen Rivas</name>
<phone type="mobile">206-555-0163</phone>
<address>
<street1>123 Main St</street1>
<city>Mercer Island</city>
<state>WA</state>
<postal>68042</postal>
</address>
<netWorth>11</netWorth>
</contact>
<contact>
<name>Scott MacDonald</name>
<phone type="home">925-555-0134</phone>
<phone type="mobile">425-555-0177</phone>
<address>
<street1>345 Stewart St</street1>
<city>Chatsworth</city>
<state>CA</state>
<postal>91746</postal>
</address>
<netWorth>500000</netWorth>
</contact>
</contacts>

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Creating XML via DOM (without XLinq)
XmlDocument doc = new XmlDocument();
XmlElement name = doc.CreateElement("name");
name.InnerText = "Patrick Hines";
XmlElement phone1 = doc.CreateElement("phone");
phone1.SetAttribute("type", "home");
phone1.InnerText = "206-555-0144";
XmlElement phone2 = doc.CreateElement("phone");
phone2.SetAttribute("type", "work");
phone2.InnerText = "425-555-0145";
XmlElement street1 = doc.CreateElement("street1");
street1.InnerText = "123 Main St";
XmlElement city = doc.CreateElement("city");
city.InnerText = "Mercer Island";
XmlElement state = doc.CreateElement("state");
state.InnerText = "WA";
XmlElement postal = doc.CreateElement("postal");
postal.InnerText = "68042";
XmlElement address = doc.CreateElement("address");
address.AppendChild(street1);
address.AppendChild(city);
address.AppendChild(state);
address.AppendChild(postal);
XmlElement contact = doc.CreateElement("contact");
contact.AppendChild(name);
contact.AppendChild(phone1);
contact.AppendChild(phone2);
contact.AppendChild(address);
XmlElement contacts = doc.CreateElement("contacts");
contacts.AppendChild(contact);
doc.AppendChild(contacts);

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Creating XML via XLinq
• Functional construction:

XElement contacts =
new XElement("contacts",
new XElement("contact",
new XElement("name", "Patrick Hines"),
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")
)
)
); Resulting XML:
<?xml version="1.0" standalone="yes"?>
<!--XLinq Contacts XML Example-->
<?MyApp 123-44-4444?>
• Imlicitly no XML document <contacts>
• Can be added if some processing information is needed: <contact>
<name>Patrick Hines</name>
XDocument contactsDoc = …
new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XComment("XLinq Contacts XML Example"),
new XProcessingInstruction("MyApp", "123-44-4444"),
new XElement("contacts",
new XElement("contact",
Pavel Ježek new XElement("name", "Patrick Hines"),
C# 3.0 and .NET 3.5

XLinq – XML Namespaces
• There is a support for XML namespaces, but namespace aliases are always expanded into full names
• Creating element from a “http://myCompany.com” namespace:

XElement contacts = new XElement("{http://myCompany.com}contacts", …);

• Instead of explicit alias support, existing language facilities should be used. Typical pattern:

string myNs = "{http://mycompany.com}";

XElement contacts =
new XElement(myNs+"contacts",
new XElement(myNs+"contact",
new XElement(myNs+"name", "Patrick Hines"),
new XElement(myNs+"phone", "206-555-0144",
new XAttribute("type", "home")),
)
)
);

• Results in following XML:

<contacts xmlns="http://mycompany.com">
<contact>
<name>Patrick Hines</name>
<phone type="home">206-555-0144</phone>
</contact>
</contacts>

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Loading Existing XML Data
• From string:

XElement contacts = XElement.Parse(


@"<contacts>
<contact>
<name>Patrick Hines</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>68042</postal>
</address>
<netWorth>10</netWorth>
</contact>
</contacts>");

• From file:

XElement contactsFromFile = XElement.Load(@"c:\myContactList.xml");

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Creating XElements
• XElement’s constructor:

public XElement(XName name, params object[] contents)

• Any of the parameters passed to contents can be:


1. A string, which is added as text content
2. An XElement, which is added as a child element
3. An XAttribute, which is added as an attribute
4. An XProcessingInstruction, XComment, or XCData, which is added as child content
5. An IEnumerable, which is enumerated, and these rules are applied recursively
6. Anything else, ToString() is called and the result is added as text content
7. null, which is ignored

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Adding IEnumerables to XElement
class Person { public string Name; public string[] PhoneNumbers; }

var persons = new [] {


new Person { Name="Patrick Hines",
PhoneNumbers = new string[] {"206-555-0144", "425-555-0145"} },
new Person { Name="Gretchen Rivas",
PhoneNumbers = new string[] {"206-555-0163"} }
};

• Following code using Standard Query Operators on IEnumerable<T> can be used to transform a datastructure
to XML:

XElement contacts =
new XElement("contacts",
from p in persons
select new XElement("contact",
new XElement("name", p.Name),
from ph in p.PhoneNumbers
select new XElement("phone", ph)
) Output:
<contacts>
);
<contact>
<name>Patrick Hines</name>
Console.WriteLine(contacts); <phone>206-555-0144</phone>
<phone>425-555-0145</phone>
</contact>
<contact>
<name>Gretchen Rivas</name>
<phone>206-555-0163</phone>
Pavel Ježek </contact>
C# 3.0 and .NET 3.5 </contacts>
XLing – Element Text as Value (1)
• XElement (text inside XElement) can be explicitly converted to a value:

XElement name = new XElement("name", "Patrick Hines");


Output:
string nameString = (string) name;
Console.WriteLine(nameString); Patrick Hines

• ToString() returns the XML representation of the whole element:

string nameString = name.ToString(); Output:


Console.WriteLine(nameString); <name>Patrick Hines</name>

• Explicit type operators are provided for following types:


string, bool, bool?, int, int?, uint, uint?, long, long?, ulong, ulong?, float,
float?, double, double?, decimal, decimal?, DateTime, DateTime?, TimeSpan,
TimeSpan?, GUID, GUID?

Pavel Ježek
C# 3.0 and .NET 3.5
XLing – Element Text as Value (2)
• All element text is merged together:

XElement xhtml = new XElement(“body",


1234,
new XElement("br", null),
5678,
"some text",
new XElement("br", ""),
new XElement("em", "EMPHASED TEXT"),
"other text\nwith newline"
);

Output:

<body>1234<br />5678some text<br></br><em>EMPHASED TEXT</em>other text


with newline</body>

Console.WriteLine(nameString);

Console.WriteLine((string) nameString);

Output:

12345678some textEMPHASED TEXTother text


Pavel Ježek
with newline
C# 3.0 and .NET 3.5
XLinq – Traversing XML
• XML data:

<contact>
Met in 2005.
<name>Patrick Hines</name>
<phone>206-555-0144</phone>
<phone>425-555-0145</phone>
<!-- Avoid whenever possible -->
Met in 2005.
</contact>
<name>Patrick Hines</name>
• <phone>206-555-0144</phone>
Code examples:
<phone>425-555-0145</phone>
<!-- Avoid whenever possible -->
foreach (c in contact.Content()) {
Console.WriteLine(c);
}

foreach (c in contact.Content<XElement>()) {
Console.WriteLine(c) <name>Patrick Hines</name>
} <phone>206-555-0144</phone>
foreach (x in contact.Elements()) { <phone>425-555-0145</phone>
Console.WriteLine(x);
}

foreach (x in contact.Elements("phone")) {
Console.WriteLine(x);
}
<phone>206-555-0144</phone>
XElement name = contact.Element("name"); <phone>425-555-0145</phone>
string name = (string) contact.Element("name");
Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Manipulating XML
• Code samples:

XElement mobilePhone = new XElement("phone", "206-555-0168");


contact.Add(mobilePhone);
contact.AddFirst(mobilePhone);

contact.Add(new XElement("address",
new XElement("street", "123 Main St"),
new XElement("city", "Mercer Island"),
new XElement("state", "WA"),
new XElement("country", "USA"),
new XElement("postalCode", "68042")
));

XElement mobilePhone = new XElement("phone", "206-555-0168");


XElement firstPhone = contact.Element("phone");
firstPhone.AddAfterThis(mobilePhone);

contact.Element("phone").Remove();
contact.Elements("phone").Remove();

contact.Element("address").ReplaceContent(new XElement("street", "123 Brown Lane"),


new XElement("city", "Redmond"),
new XElement("state", "WA"),
new XElement("country", "USA"),
new XElement("postalCode", "68072"));

contact.SetElement("phone", "425-555-0155");
contact.SetElement(“phone", null);

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq - Queries
• Example – flattening contacts:
new XElement("contacts",
from c in contacts.Elements("contact")
select new object[] {
new XComment("contact"),
new XElement("name", (string)c.Element("name")),
c.Elements("phone"),
new XElement("address", c.Element("address"))
}
);

• Results in:

<contacts> <contacts>
<!-- contact --> <contact>
<name>Patrick Hines</name>
<name>Patrick Hines</name> <phone type="home">206-555-0144<
<phone type="work">425-555-0145<
<phone type="home">206-555-0144</phone> <address>
<phone type="work">425-555-0145</phone> <street1>123
<city>Mercer
<address> <state>WA</s
<address> </address>
<postal>6804

<state>WA</state> </contact>
<netWorth>10</netWorth>

</address> <contact>
</address> <name>Gretchen Rivas</name>
<phone type="mobile">206-555-016
<!-- contact --> <address>
<name>Gretchen Rivas</name> <street1>123
<city>Mercer
<address> <state>WA</s
<postal>6804
<address> </address>
<state>WA</state> </contact>
<netWorth>11</netWorth>

</address> <contact>
</address> <name>Scott MacDonald</name>
<phone type="home">925-555-0134<
<!-- contact --> <phone type="mobile">425-555-017
<address>
<name>Scott MacDonald</name> <street1>345
<phone type="home">925-555-0134</phone> <city>Chatsw
<state>CA</s
<phone type="mobile">425-555-0177</phone> <postal>9174
... </address>
<netWorth>500000</netWorth>
</contact>
</contacts>
Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Schema Aware programming
• Without schema:

public static double GetTotalByZip(XElement root, int zip) {


return (
from order in root.Elements("order"),
item in order.Elements("item"),
where (int) order.Element("address").Element("postal") == zip
select ((double) price * (int) qty)
).Sum();
}

• With schema (currently neither implemented nor specified – probable XLinq extension in final version):

public static double GetTotalByZip(Orders root, int zip) {


return (
from order in root.OrderCollection,
item in order.Items
where order.Address.Postal == zip
select (item.Price * item.Quantity)
).Sum();
}

Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – in Visual Basic 9 (1)
• Using C# 3.0 functional approach:
Dim contacts As XElement = _
New XElement("contacts", _
New XElement("contact", _
New XElement("name", "Patrick Hines"), _
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"))))

• Using XML literals:


Dim contacts As XElement = _
<contacts>
<contact>
<name>Patrick Hines</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>
Pavel Ježek </contacts>
C# 3.0 and .NET 3.5
XLinq – in Visual Basic 9 (2)
• Visual Basic expressions in XML literals:

Dim myName = "Patrick Hines“


Dim contact As XElement = <contact>
<name><%=myName%></name>
</contact>

Dim MyName = "Patrick Hines“


Dim elementName = "contact“
Dim contact As XElement = <(elementName)>
<name><%=MyName%></name>
</>

• Late bound XML:

For Each Dim phone In contact.phone


Console.WriteLine(CStr(phone.@type))
Next
Console.WriteLine(CStr(contacts...city(0)))

For Each Dim phone In contact.Element("phone")


Console.WriteLine(CStr(phone.Attribute("type")))
Next
Console.WriteLine(CStr(ElementAt(contact.Descendants("city"),0)))

Pavel Ježek
C# 3.0 and .NET 3.5

You might also like