You are on page 1of 63

LINQ

Language INtegrated Query


Uniform Programming Model for any kind of data

LINQ

Today, data managed by a program can belong to different

data domains: an array, an object graph, an XML document, a


database, a text file, a registry key, an e-mail message, Simple
Object Access Protocol (SOAP) message content, a Microsoft
Office Excel file

Each data domain has its own specific access model.

LINQ
To enable basic LINQ functionality
Add Reference to System.Core.dl
using directive or Imports statement for System.Linq
To use LINQ to XML
Add a reference to System.Xml.Linq and
System.Data.Linq.Mapping;
Add a using directive or Imports statement for
System.Xml.Linq.

To use LINQ to SQL


Add a reference to System.Data.Linq.
Add a using directive or Imports statement for
System.Data.Linq

LINQ
Consider simple Query
var query =
from c in Customers
where c.Country == "Italy"
select c.CompanyName;

foreach ( string name in query )


{
Console.WriteLine( name );
}

What is Customers?
SQL database
DataSet
An array of objects
.NET Collection

LINQ

LINQ -SQL

LINQ
Entity
[Table(Name="Customers")]
public class Customer
{
[Column(IsPrimaryKey=true), IsDBGenerated=true]
public string CustomerID;
[Column] public string CompanyName;
[Column] public string City;
[Column(Name="Region")] public string State;
[Column] public string Country;
}

LINQ
Querying into Entity
DataContext db = new DataContext("Database=Northwind");
Table<Customer> Customers = db.GetTable<Customer>();
var query =
from c in Customers
where c.Country == "USA && c.State == "WA"
select new {c.CustomerID, c.CompanyName, c.City };
foreach( var row in query )
{
Console.WriteLine( row );
}

LINQ
We can explore the generated SQL query
Console.WriteLine( db.GetQueryText( query ) );
ie:
SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[City]
FROM [Customers] AS [t0]
WHERE ([t0].[Country] = @p0) AND ([t0].[Region] = @p1)
An alternative way to get a trace of all SQL statements sent to the
database is to assign a value to the Log property of DataContext:
db.Log = Console.Out;

LINQ
Creating a Derived DataContext Class:
public class SampleDb : DataContext
{
public SampleDb(IDbConnection connection) : base( connection ) {}
public SampleDb(string fileOrServerOrConnection)
: base( fileOrServerOrConnection ) {}
public Table<Customer> Customers;
}

LINQ
Direct Access LINQ Query
Property Accessor User Code
[Column(Storage="_ProductName")]
public string ProductName
{
get
{
return this.ProductName;
}
set
{
this.OnPropertyChanging("ProductName");
this._ProductName = value;
this.OnPropertyChanged("ProductName");
}
}

LINQ

Entity Relations
EntityRef
EntitySet

LINQ
Entity Relation -- EntityRef
[Table(Name="Orders")]
public class Order
{
[Column(IsPrimaryKey=true)] public int OrderID;
[Column] private string CustomerID;
[Association(Storage="_Customer", ThisKey="CustomerID")]
public Customer Customer
{
get { return this._Customer.Entity; }
set { this._Customer.Entity = value; }
}
private EntityRef<Customer> _Customer;
}

LINQ
Querying
Table<Order> Orders = db.GetTable<Order>();
var query =
from o in Orders
where o.Customer.Country == "USA"
select o.OrderID;
var query =
from o in Orders
where o.OrderID == 10528
select o;
foreach( var row in query )
{
Console.WriteLine( row.Customer.Country );
}

LINQ
EntitySet
[Table(Name="Customers")]
public class Customer
{
[Column(IsPrimaryKey=true)] public string CustomerID;
[Column] public string CompanyName;
[Column] public string Country;
[Association(OtherKey="CustomerID")]

public EntitySet<Order> Orders;

LINQ
EntitySet
[Table(Name="Customers")]
public class Customer
{
[Column(IsPrimaryKey=true)] public string CustomerID;
[Column] public string CompanyName;
[Column] public string Country;

[Association(OtherKey="CustomerID", Storage="_Orders")]
public ICollection<Order> Orders
{
get { return this._Orders; }
set { this._Orders.Assign(value);
}
private EntitySet<Order> Orders;

LINQ
Querying Ex(1)
Table<Customer> Customers = db.GetTable<Customer>();
var query =

from c in Customers
where c.Orders.Count > 20
select c.CompanyName;

LINQ query is translated into an SQL query


SELECT [t0].[CompanyName]
FROM [Customers] AS [t0]
WHERE
( SELECT COUNT(*) FROM [Orders] AS [t1] WHERE [t1].[CustomerID] =
[t0].[CustomerID] )>20

Querying Ex(2)
var query =

LINQ

from c in Customers
where c.Orders.Count > 20
select c;

foreach( var row in query )


{
Console.WriteLine( row.CompanyName );
foreach( var order in row.Orders )
{
Console.WriteLine( order.OrderID );
}
}
This is using Deferred Loading every time the orders collection is accessed a SQL is
fired.

SELECT [t0].[OrderID], [t0].[CustomerID]


FROM [Orders] AS [t0]
WHERE [t0].[CustomerID] = @p0

LINQ
Querying Ex(2)

Force the inclusion of an EntitySet using a DataShape instance


Method 1 - Use of DataShape
DataContext db = new DataContext( ConnectionString );
Table<Customer> Customers = db.GetTable<Customer>();

DataShape ds = new DataShape();


ds.LoadWith<Customer>( c => c.Orders );
db.Shape = ds;
var query =

from c in Customers
where c.Orders.Count > 20
select c;

LINQ
Querying Ex(2)

Force the inclusion of an EntitySet using a DataShape instance


Method 2 - Explicit Inclusion
var query =
from c in Customers
where c.Orders.Count > 20
select new { c.CompanyName, c.Orders };

LINQ
Querying Ex(3)

Filtering the Many side table


customers who placed at least five orders in 1997, and we want to load and
see only these orders
DataShape ds = new DataShape();
ds.AssociateWith<Customer>
(
c => c.Orders.Where
(
o => o.OrderDate.Value.Year == 1997
)
);
db.Shape = ds;
var query =

from c in Customers
where c.Orders.Count > 5
select c;

Filtering (Client Side) LINQ


var query =
from c in Customers
select new {c.CompanyName, c.State, c.Country };
query =

DisplayTop( query, 10 );
from c in query
where c.Country == "USA"
select c;
DisplayTop( query, 10 );

static void DisplayTop<T>( IQueryable<T> query, int rows )


{
foreach( var row in query.Take(rows))
{
Console.WriteLine( row );
}
}

LINQ
Querying the Details Table
IEnumerable<Order> orders = db.Customers
.Where(c => c.Country == "USA" && c.Region == "WA")
.SelectMany(c => c.Orders);
foreach(Order item in orders)
Console.WriteLine("{0} - {1} - {2}",
item.OrderDate, item.OrderID, item.ShipName);

LINQ
Querying on Primary Key
var customer = db.Customers.Single( c => c.CustomerID == "ANATR" );
Console.WriteLine( "{0} {1}", customer.CustomerID,
customer.CompanyName );

LINQ

Different LINQ Query


Solution

Original Database SQL Query

SELECT SUM( od.Quantity ) AS TotalQuantity


FROM [Products] p LEFT JOIN [Order Details] od
ON od.[ProductID] = p.[ProductID]
WHERE p.ProductName = 'Chocolade'
GROUP BY p.ProductName

LINQ - Joins
var queryJoin =

from p in db.Products
join o in db.Order_Details
on p.ProductID equals o.ProductID
into OrdersProduct
where p.ProductName == "Chocolade"
select OrdersProduct.Sum( o => o.Quantity );
var quantityJoin = queryJoin.Single();
Console.WriteLine( quantityJoin );

LINQ Entity Access

var chocolade = db.Products.Single


( p => p.ProductName == "Chocolade" );
var quantityValue = chocolade.Order_Details.Sum( o => o.Quantity );

Console.WriteLine( quantityValue );

LINQ

Stored Procedures

CREATE PROCEDURE [dbo].[Customers By City]( @param1 NVARCHAR(20) )


AS BEGIN
SET NOCOUNT ON;
SELECT CustomerID, ContactName, CompanyName, City
FROM Customers AS c
WHERE c.City = @param1
END

class SampleDb : DataContext


{
[StoredProcedure( Name = "dbo.[Customers By City]" )]
public IEnumerable<CustomerInfo> CustomersByCity( string param1 )
{
return (IEnumerable<CustomerInfo>)
this.ExecuteMethodCall<CustomerInfo>
(
this,
((MethodInfo) (MethodInfo.GetCurrentMethod())),
param1
);
}
}

public class CustomerShortInfo


{
public string CustomerID;
public string CompanyName;
public string City;
}
public class CustomerInfo : CustomerShortInfo
{
public string ContactName;
}

Making a CALL

SampleDb db = new SampleDb( ConnectionString );


foreach( var row in db.CustomersByCity( "Seattle" ))
{
Console.WriteLine( "{0} {1}",
row.CustomerID, row.CompanyName );
}

LINQ

Stored Functions

Function Returning Scalar


class SampleDb : DataContext
{
[Function(Name="dbo.MinUnitPriceByCategory")]
public decimal MinUnitPriceByCategory( int categoryID )
{
IExecuteResults mc = this.ExecuteMethodCall
(
this,
(MethodInfo) (MethodInfo.GetCurrentMethod()),
categoryID
);
return (decimal)mc.ReturnValue;
}
}

Function Returning Scalar

Making a CALL
var query =
from c in Categories
select new
{
c.CategoryID, c.CategoryName,
MinPrice =
db.MinUnitPriceByCategory( c.CategoryID )
};

Function Returning ResultSet


class SampleDb : DataContext
{
[Function(Name="dbo.CustomersByCountry")]
public IQueryable<Customer> CustomersByCountry
( string country )
{
return (IQueryable<Customer>)
this.ExecuteMethodCall<Customer>
(
this,
(MethodInfo) MethodInfo.GetCurrentMethod(),
country
);
}
}

Making a CALL
Table<Order> Orders = db.GetTable<Order>();
var queryCustomers =
from c in db.CustomersByCountry( "USA" )
join o in Orders
on c.CustomerID equals o.CustomerID
into orders
select new { c.CustomerID, c.CompanyName, orders };

LINQ

Data Manipulation
Entity Updates

Changing a Customer Record


var customer = db.Customers.Single( c => c.CustomerID == "FRANS" );
customer.ContactName = "Marco Russo";
Console.WriteLine( db.GetChangeText() );
The output from the previous code is similar to that shown here:
UPDATE [Customers]
SET [ContactName] = "Marco Russo"
FROM [Customers]
WHERE ...

Adding and Removing a Customer Record


var customer = db.Customvar newCustomer =
new Customer { CustomerID = "DLEAP", CompanyName = "DevLeap",
Country = "Italy" };
db.Customers.Add( newCustomer );
var oldCustomer =
db.Customers.Single( c => c.CustomerID == "FRANS" );
db.Customers.Remove( oldCustomer );

Removing Record in One-Many Relationship


var order = db.Orders.Single( o => o.OrderID == 10248 );
db.Order_Details.RemoveAll( order.Order_Details );
db.Orders.Remove( order );
Related SQL Statements Fired
DELETE
DELETE
DELETE
DELETE

FROM
FROM
FROM
FROM

[Order Details] WHERE ([OrderID] = 10248) AND ([ProductID] = 11)


[Order Details] WHERE ([OrderID] = 10248) AND ([ProductID] = 42)
[Order Details] WHERE ([OrderID] = 10248) AND ([ProductID] = 72)
[Orders] WHERE [OrderID] = 10248

Submitting Changes to Databases


Northwind db = new Northwind(ConnectionString );
var customer = db.Customers.Single( c => c.CustomerID == "FRANS" );
customer.ContactName = "Marco Russo";
try {
db.SubmitChanges();
}
catch (ChangeConflictException ex)
{
Console.WriteLine( ex.Message );
}

Conflict Handling
DataContext has Refresh() function with following Enumerated
Parameter Values
KeepChanges
KeepCurrentValues
OverwriteCurrentValues

Conflict Handling
Each Column attribute can have an UpdateCheck argument that can
have one of the following three values:
Always Always use this column (which is the default) for conflict
detection.
Never Never use this column for conflict detection.
WhenChanged Use this column only when the member has been
changed by the application

Involving Transactions
using(TransactionScope ts = new TransactionScope())
{
Product prod = db.Products.Single
(p => p.ProductID == 42);
if (prod.UnitsInStock > 0)
prod.UnitsInStock--;

db.SubmitChanges();
ts.Complete();

LINQ

LINQ-DataSet

Querying DataSet
DataSet ds = LoadDataSetUsingDataAdapter();
DataTable orders = ds.Tables["Orders"];
DataTable orderDetails = ds.Tables["OrderDetails"];
var query =
from o in orders.AsEnumerable()
where o.Field<DateTime>( "OrderDate" ).Year >= 1998
orderby o.Field<DateTime>( "OrderDate" ) descending
select o;

LINQ-Join

DataSet ds = LoadDataSetUsingDataAdapter();
DataTable orders = ds.Tables["Orders"];
DataTable orderDetails = ds.Tables["OrderDetails"];
var query =
from o in orders.AsEnumerable()
join od in orderDetails.AsEnumerable()
on o.Field<int>( "OrderID" ) equals od.Field<int>( "OrderID" )
into orderLines
where o.Field<DateTime>( "OrderDate" ).Year >= 1998
orderby o.Field<DateTime>( "OrderDate" ) descending
select new { OrderID = o.Field<int>( "OrderID" ),
OrderDate = o.Field<DateTime>( "OrderDate" ),
Amount = orderLines.Sum(

};

od => od.Field<decimal>( "UnitPrice" ) * od.Field<short>( "Quantity" ) )

Leveraging DataSet Relationship


var query =
from o in orders.AsEnumerable()
where o.Field<DateTime>( "OrderDate" ).Year >= 1998
orderby o.Field<DateTime>( "OrderDate" ) descending
select
new
{ OrderID = o.Field<int>( "OrderID" ),
OrderDate = o.Field<DateTime>( "OrderDate" ),
Amount = o.GetChildRows( "OrderDetails" ).Sum
( od => od.Field<decimal>( "UnitPrice" ) * od.Field<short>( Quantity" ) ) };

LINQ

LINQ-XML

XML Data Fragment


C#.NET 9.0

XElement tag = new XElement("customer",


new XElement("firstName", "Paolo"));
XML DOM
XmlDocument doc = new XmlDocument();
XmlElement customerElement = doc.CreateElement("customer");
XmlElement firstNameElement = doc.CreateElement("firstName");
firstNameElement.InnerText = "Paolo";
customerElement.AppendChild(firstNameElement);
doc.AppendChild(customerElement);
In VB.NET 9.0
Dim customerName As String = "Paolo"
Dim tag As XElement = _
<customer>
<firstName><%= customerName %></firstName>
</customer>

TypeCast
XElement order = new XElement("order",
new XElement("quantity", 10),
new XElement("price", 50),
new XAttribute("idProduct", "P01"));
Decimal orderTotalAmount =
(Decimal)order.Element("quantity") * (Decimal)order.Element("price");
Console.WriteLine("Order total amount: {0}", orderTotalAmount);

Explicit Escaping
XElement notes = new XElement("notes", "Some special characters like & > < <div/>
etc.");
The result is encoded automatically using XmlConvert and looks like the following:
<notes>Some special characters like &amp; &gt; &lt; &lt;div/&gt; etc.</notes>

Loading XML Files


XElement customer = XElement.Load(@"..\..\customer.xml");
XElement firstAddress =
(customer.Descendants("addresses").Elements("address")).First();

Generating XML using LINQ


XElement xmlCustomers = new XElement("customers",
from c in customers
where c.Country == Countries.Italy
select new XElement("customer", new XAttribute("name", c.Name),
new XAttribute("city", c.City),
new XAttribute("country", c.Country)
));

Generating XML with Namespace


XNamespace ns = "http://schemas.devleap.com/Customer";
XElement customer =
new XElement(ns + "customer",
new XAttribute("id", "C01"),
new XElement(ns + "firstName", "Paolo"),
new XElement(ns + "lastName", "Pialorsi")
);
The Result:
<?xml version="1.0" encoding="utf-8"?>
<customer id="C01" xmlns="http://schemas.devleap.com/Customer">
<firstName>Paolo</firstName>
<lastName>Pialorsi</lastName>
</customer>

Generating XML with Namespace and Prefix


XNamespace ns = "http://schemas.devleap.com/Customer";
XElement customer =
new XElement(ns + "customer",
new XAttribute(XNamespace.Xmlns + "c", ns),
new XAttribute("id", "C01"),
new XElement(ns + "firstName", "Paolo"),
new XElement(ns + "lastName", "Pialorsi")
);
The Result:
<?xml version="1.0" encoding="utf-8"?>
<c:customer xmlns:c="http://schemas.devleap.com/Customer" id="C01">
<c:firstName>Paolo</c:firstName>
<c:lastName>Pialorsi</c:lastName>
</c:customer>

LINQ

Reading XML
Thru LINQ

var customersFromXml =
from c in xmlCustomers.Elements("customer")
where (String)c.Attribute("country") == "Italy"
orderby (String)c.Element("name")
select new
{ Name = (String)c.Element("name"),
City = (String)c.Attribute("city")
};
foreach (var customer in customersFromXml)
{
Console.WriteLine(customer);
}
The result is shown in the following output block:
{ Name = Marco, City = Torino }
{ Name = Paolo, City = Brescia }

Joining XML and SQL


var ordersWithCustomersFromXml =
from c in xmlCustomers.Elements("customer")
join o in orders
on (String)c.Element("name") equals o.Name
orderby (String)c.Element("name")
select new
{ Name = (String)c.Element("name"),
City = (String)c.Attribute("city"),
ProductID = o.Productid ,
Quantity = o.Quantity
};

Joining XML and SQL ( Using let )


var ordersWithCustomersFromXml =
from c in xmlCustomers.Elements("customer")
let xName = (String)c.Element("name")
let xCity = (String)c.Attribute("city")
join o in orders
on xName equals o.Name
orderby xName
select new { Name = xName,
City = xCity,
ProductID = o.ProductId,
Quantity = o.Quantity
};

XElement root = XElement.Load("TestConfig.xml");

IEnumerable<XElement> tests =
from el in root.Elements("Test")
where (string)el.Element("CommandLine") == "Examp2.EXE"
select el;
foreach (XElement el in tests)
Console.WriteLine((string)el.Attribute("TestId"));

XElement books = XElement.Parse(


@"<books>
<book>
<title>Pro LINQ: Language Integrated Query in C# 2008</title>
<author>Joe Rattz</author>
</book>
<book>
<title>Pro WF: Windows Workflow in .NET 3.0</title>
<author>Bruce Bukovics</author>
</book>
<book>
<title>Pro C# 2005 and the .NET 2.0 Platform, Third
Edition</title>
<author>Andrew Troelsen</author>
</book>
</books>");

var titles =
from book in books.Elements("book")
where (string) book.Element("author") == "Joe Rattz"
select book.Element("title");
foreach(var title in titles)
Console.WriteLine(title.Value);

You might also like