Professional Documents
Culture Documents
Csharp 3.0 and LINQ - 2007
Csharp 3.0 and LINQ - 2007
5: A Brief
Overview
Pavel Ježek
DISTRIBUTED SYSTEMS RESEARCH GROUP
http://nenya.ms.mff.cuni.cz
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)
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);
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
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;
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; } }
}
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);
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; } }
}
• 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)
Pavel Ježek
C# 3.0 and .NET 3.5
ADO.NET: Connection-oriented Access
1.) Declare connection
try {
1.) Request connection to database
while (reader.Read()) {
int cols = reader.GetValues(dataRow);
for (int i = 0; i < cols; i++) Console.Write("| {0} " , dataRow[i]);
Console.WriteLine();
}
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:
Pavel Ježek
C# 3.0 and .NET 3.5
Query Expressions – Examples
• Query expression:
Pavel Ježek
C# 3.0 and .NET 3.5
Query Expressions – Examples
• Query expression:
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 }
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(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
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) {}
}
Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Query Execution
• Queries are not imperative – they are not executed immediately
Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Advantage of Deferred Execution
• Partial query contruction:
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;
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:
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;
[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:
Pavel Ježek
C# 3.0 and .NET 3.5
DLinq – Modifying Entities
Northwind db = new Northwind("c:\\northwind\\northwnd.mdf");
// Removing it from the table also removes it from the Customer’s list
db.Orders.Remove(ord0);
// 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;
}
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;
}
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>();
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:
• Instead of explicit alias support, existing language facilities should be used. Typical pattern:
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")),
)
)
);
<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:
<state>WA</state>
<postal>68042</postal>
</address>
<netWorth>10</netWorth>
</contact>
</contacts>");
• From file:
Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Creating XElements
• XElement’s constructor:
Pavel Ježek
C# 3.0 and .NET 3.5
XLinq – Adding IEnumerables to XElement
class Person { public string Name; public string[] PhoneNumbers; }
• 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:
Pavel Ježek
C# 3.0 and .NET 3.5
XLing – Element Text as Value (2)
• All element text is merged together:
Output:
Console.WriteLine(nameString);
Console.WriteLine((string) nameString);
Output:
<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:
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")
));
contact.Element("phone").Remove();
contact.Elements("phone").Remove();
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:
• With schema (currently neither implemented nor specified – probable XLinq extension in final version):
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"))))
Pavel Ježek
C# 3.0 and .NET 3.5