SlideShare a Scribd company logo
1 of 64
LINQ
Adam Abonyi
Jan Ambrož
Agenda




Co je to LINQ?
Jak lze LINQ použít?
Co musím znát, abychom se mohli dotazovat v
LINQu?

Pokračování




LINQ to Objects (anything and everything)
LINQ to XML
LINQ to ADO.NET (to SQL, to DataSets)
LINQ - Language INtegrated Query


Vlastní pro rodinu .NET jazyků


Visual Basic, C# a další



Dotazování jako součást programovacího jazyka



Dotazování nad „libovolným“ datovým zdrojem




SQL, XML, IEnumerable, další...

Využití znalosti metadat


Typová kontrola, našeptávání (Intellisense), kontrola
syntaxe
LINQ - co můžeme použít?


Základní operace




Třízení




GroupBy

Agregační funkce




OrderBy, OrderByDescending

Seskupování (Group)




Select, From, Where

Count, Min, Max, Average, ...

Spojování pomocí operátoru Join a další operace
LINQ - co musíme znát?


Lambda výrazy



Expression trees (Stromy výrazů)



Extension Metody



Lokální dedukce typů



Anonymní typy



Inicializace dat



Lambda Výrazy – Kód jako data
LINQ - lambda výrazy


Předání metody jako parametr jiné metodě




Doplnění nějakého obecného chování
(např. jak se mají porovnávat dva řetězce)

Zápis lambda výrazů v C# 3.0



Generické delegáty jsou součástí .NET knihoven
Func<…>, liší se počtem typových parametrů
Možnost zápisu anonymních delegátů (arg1, arg2) => výraz
delegate TResult Func<TResult>();
delegate TResult Func<T1, TResult>(T1 a1);
delegate TResult Func<T1, T2, TResult>(T1 a1, T2 a2);
Func<string, string, string> add =
(a, b) => a + " " + b;
string res = add("Hello", "world");
Console.WriteLine(res);
LINQ - lambda výrazy
• Dva typy podle výrazu za šipkou
– Výraz – tedy to co lze napsat v přiřazení ( a = <výraz>; )
– Blok – umožňuje zapsat více-řádkový kód
Func<int, int, int> add = (a, b) => a + b;
Func<int, int, int> add = (a, b) => {
Console.WriteLine("scitam!");
return a + b;
};

• Typy parametrů C# pozná (většinou) automaticky
– Ale lze je případně doplnit
Func<int, int, int> add =
(int a, int b) => a + b;
LINQ - expression trees


„kód jako data“



Lambda výrazy typu Expression<Func<…>>
Neformálně: Vrátí objekty, které popisují to, co jsme do výrazu
napsali - něco jako vrátit zdrojový kód výrazu
Expression<Func<int, int, int>> exprAdd =
(a, b) => a + b;



O tom později



.. je to pro LINQ klíčové!
Stojí na tom implementace „LINQ to SQL“
LINQ – extension metody


Zápis
static class StringUtils {
static public string Reverse(this string s) {
StringBuilder sb = new StringBuilder();
for (int i = s.Length - 1; i >= 0; i--)
sb.Append(s[i]);
return sb.ToString();
}
}



Užití
string s = "dlrow olleH";
string r = s.Reverse();
LINQ - extension metody


Jak to přesně funguje?








Musí být statická, ve statické třídě
První parametr označen klíčovým slovem this
Musí být v jmenném prostoru otevřeném pomocí using
Existuje-li normální metoda se stejným jménem, volá se
ta.

Je to bezpečné?



Samozřejmě, extension metoda má práva přistupovat jen
k public členům původní třídy
Funguje to pouze jako „syntactic sugar“
Enumerable.Select(
Enumerable.Where(t, n => n % 2 == 0), n => n * n);
vs.
t.Where(n => n % 2 == 0).Select(n => n * n);
LINQ - dedukce typu


Klíčové slůvko var pro deklaraci proměnné





Typ proměnné se odvodí z výrazu na pravé straně
Je to typově bezpečné a typ je vždy známý
Výraz na pravé straně nesmí být null
Lze použít i ve for a foreach cyklech
// C# 3.0
var i = 5;
var s = "Hello";
var d = 1.0;
var numbers = new int[] { 1, 2, 3 };
var orders = new Dictionary<int, Order>();

' Visual Basic 9
Dim e = 10
LINQ - dedukce typu


Pro Visual Studio se jedná o obyčejné proměnné




… a zobrazuje jejich typ

C# kompilátor zná typ
LINQ - dedukce typu
• Vše je stále typově bezpečné
– Nejedná se tedy o typ object!
' Visual Basic 9
Dim e = 10
Chyba: Neznámý typ!
// C#
var e
var n
var d

3.0
Chyba: n je typu ‘int’
Vhodné použití 
= null;
= 10; n.SomeFunction();
= new Dictionary<string, ICollection<string>>();

• Používejte ji rozumně!
– Inicializace „ošklivých“ typů
– Klíčové pro anonymní typy (uvidíme později)
LINQ - anonymní typy
var person = new { Name = „Adam", City = "Praha"};
Console.WriteLine(person.City);
Dim person = New With {.Name = „Adam", .City = "Praha"}
person.City = "Brno"
Console.WriteLine(person.City)




Vytváří typ, který má uvedené vlastnosti
Jméno typu nelze zapsat…






Lze je používat pouze lokálně díky dedukcí typů
Nelze vrátit z metody (pouze jako object)

(pozn.: v C# mají pouze get, ve VB i set)
LINQ - inicializace dat


Inicializace objektů


Podobná syntaxe jako při zápisu anonymních typů
// Anonymní typ
var p = new { Name = „Adam", City = "Praha" };
// Pojmenovaný typ
var p = new Person { Name = „Adam", City = "Praha" };



Lze použít na libovolné .NET typy s vlastnostmi
var f = new Form { BackColor = Color.Green, Text = "Hello!" };



Je možné volat vlastní konstruktor při vytváření
var c = new Customer("John", "Doe") { City = "New York" };
LINQ - inicializace objektů


Jak to funguje?


Úsporný zápis pro obyčejné nastavování vlastností

class Customer {
public string Name { get; set; }
public string Country { get; set; }
public string City { get; set; }
}
var c = new Customer { Name = "Jose",
Country = "Spain", City="Barcelona" }
Dim c = New Customer With { .Name = "Jose",
.Country = "Spain", .City="Barcelona" }
Customer _temp = new Customer();
_temp.Name = "Jose";
_temp.Country = "Spain";
_temp.City = "Barcelona";
Customer c = _temp;
LINQ - inicializace kolekcí


Podobné, jako inicializace pole, ale pro ostatní kolekce
var numsA = new int[] { 1, 2, 3, 4, 5, 6 };
var numsL = new List<int> { 1, 2, 3, 4, 5, 6 };


Samozřejmě, možno kombinovat s inicializací objektů
var custs = new List<Customer> {
new Customer { Name = "John",
Surname = "Doe", City = "New York" },
new Customer { Name = "Tomas",
Surname="Petricek", City = "Prague" }
};



Lze inicializovat i komplikovanější kolekce
var spellings = new Dictionary<int, string> {
{ 0, "Zero" }, { 1, "One" },
{ 2, "Two" }, { 3, "Three" } };
LINQ - kód jako data


Druhá možnost, jak deklarovat lambda výrazy



Místo Func<…> použijeme Expression<Func<…>>
To je reprezentace zdrojového kódu výrazu
using System.Linq.Expressions;
// Visual Basic
Dim square As Expression(Of Func(Of Integer, String)) = _
Function(i) "Number: " + (i.ToString())
// C#
Expression<Func<int, string>> square =
(i) => "Number: " + (i.ToString());



Použijeme-li WriteLine/ToString, získáme pseudokód:

Console.WriteLine(square);
// Vypíše: i => ("Number: " + i.ToString())
LINQ - kód jako data


Volba mezi stromem a delegátem na základě typu




Při deklaraci nelze používat var!
Func<...>
Kompiluje se jako delegát
Expression<Func<...>> Uloží se jako data (strom)

Kód funkce se uloží jako data (strom)





Kód (strom) se např. dá přeložit do jazyka SQL
Stromy lze konstruovat z kódu
Stromy lze upravovat (vytvořit změněnou kopii)
Stromy lze kompilovat na spustitelný kód (za běhu)
LINQ - kód jako data


PŘÍKLAD: Strom následujícího výrazu:

Expression<Func<int, string>> square =
(i) => "Number: " + (i.ToString());

ParameterExpression
(Type=int, Name =“i”)
LambdaExpression
(params => body)

ConstantExpression
(“Number: “)

BinaryExpression
(Type=“Add”)
MethodCallExpression
(Object.ToString)



ParameterExpression
(Type=int, Name=“i”)

Výraz (strom) bude k dispozici za běhu programu


Může být různě zpracován (např. přeložen do SQL)
LINQ - dotazy


Pomocí lambda výrazů a extension metod
lze zapsat některé obvyklé operace s daty:
var q = db.Products
.Where(p => p.ProductName.StartsWith("C"))
.Select(p => p.ProductName);


Ale je to dost dobré? … jak by vypadal Join nebo GroupBy?
var q = from p in db.Products
where p.ProductName.StartsWith("C“)
select p.ProductName;




Pro tyto běžné příklady má C# (a VB) rozšíření, které přidává
dotazy přímo do programovacího jazyka
Vnitřně se stále vše překládá pomocí extension metod
LINQ - základní dotazy


Standardní dotazy s Where a Select:


ID kategorií, které se jmenují Beverages
var q = from c in db.Categories
where c.CategoryName == "Beverages"
select c.CategoryID;



Chceme pouze první takovou…
var q = (from c in db.Categories
where c.CategoryName == "Beverages"
select c.CategoryID).First();



Vracíme anonymní typ – pouze jméno a cenu produktu:
var q = from p in db.Products
where p.CategoryID == 1
select new { p.ProductName, Price = p.UnitPrice };
LINQ - dotazy


Řazení položek pomocí klauzule OrderBy


Řazení produktů podle jména:
var q = from p in db.Products
orderby p.ProductName
select p;



Vzestupné (ascending) a sestupné (descending)
pořadí:
var q = from p in db.Products
orderby p.ProductName ascending,
p.UnitPrice descending
select p;
LINQ - dotaz
Začíná klíčovým
slovem from

Následují klauzule
from, join, let,
where, orderby

from id in source
{ from id in source |
join id in source on expr equals expr [ into id ] |
let id = expr |
Na konec select
where condition |
nebo group by
orderby ordering, ordering, … }
select expr | group expr by key
[ into id query ]
Může následovat
klauzule into
LINQ - dotazy


Spojení více zdrojů dat pomocí operace Join


Použití více klauzulí from v dotazu:
var q = from p in db.Products
from c in db.Categories
where p.CategoryID == c.CategoryID
select new { Category = c.CategoryName,
Product = p.ProductName };



Totéž pomocí operátoru Join (inner join)
var q = from p in db.Products
join c in db.Categories
on p.CategoryID equals c.CategoryID
select new { Category = c.CategoryName,
Product = p.ProductName };



Pozn.: Outer join lze zapsat pomocí metody DefaultIfEmpty()
LINQ - dotazy


Seskupování pomocí group … by


Narozdíl od SQL vrací objekty skutečně po skupinách
(nikoliv jako seznam, kde jsou skupiny po sobě)
var q = from p in db.Products
group p by p.CategoryID;
foreach (var group in q) {
Console.WriteLine(group.Key);
foreach (var prod in group)
Console.WriteLine(prod.ProductName);
}



Použití klauzule into pro další práci se skupinami:
var q = from p in db.Products
group p by p.CategoryID into g
select new { g.Key, Count =
(from p in g where p.UnitPrice > 10.0M select g)
.Count() };
LINQ - dotazy


Zápis dotazů pomocí volání metod


Počet produktů v dané kategorii:
var q = db.Products
.Where(p => p.CategoryID == 2)
.Count();



Seřadíme podle jména a vrátíme index produktu a jméno:
var q = db.Products
.OrderBy(p => p.ProductName)
.Select((p, index) =>
new { Index = index, Name = p.ProductName });



„Stránkování“ pomocí operátorů Skip a Take:
var q = db.Products
.OrderBy(p => p.ProductName)
.Skip(5*page).Take(page);
Část druhá
LINQ to *
Jan Ambrož
LINQ obecně


Proč potřebujeme nové technologie pro práci s daty?
C# 3.0

VB 9.0

Others…

LINQ: Langauge INtegrated Query
LINQ to
Objects

LINQ to
DataSets

LINQ to
SQL

LINQ to
Entities

LINQ to
XML
<book>
<title/>
<author/>
<year/>
<price/>
</book>

Objekty

Databáze

XML
LINQ – pod pokličkou


Většina použití LINQu se týká kolekcí dat





tedy rozhraní IEnumerable<T> pod .NET

Základní implementace ve statické třídě Enumerable
Pro použití nutné importovat namespace
System.Linq





tím je umožněno využívat Extension Methods definované
statickou třídou Enumerable
tato třída definuje metody pro všechny potřebné operátory
použití každého operátoru vrací mírně modifikované
rozhraní IEnumerable<T>
výsledkem je tedy opět rozhraní IEnumerable<T>, tedy
opět kolekce dat
LINQ – pod pokličkou


Ne vždy je však toto řešení ideální




např. LINQ to SQL – nechceme nahrávat obsah celé
tabulky a pak filtrovat a řadit data

Řešením je použití rozhraní IQueryable<T>






tvorba Expression Tree používáním LINQ operátorů
využití Extension Methods definovaných třídou
Queryable
tzv. Provider následně transformuje Expression Tree do
výsledné podoby vlastního dotazu
funkčnost není omezena, rozhraní je potomkem
IEnumerable<T>, stále tedy lze využít např. foreach
cyklus k procházení výsledků
LINQ – pod pokličkou


Použití IQueryable<T>





LINQ to SQL – nejprve je vytvořen dotaz, který je
následně transformován do SQL jazyka
LINQ to Anything – dotazy vůči webovým službám,
webovým stránkám, ...

Další možností je manuální implementace operátorů






definice vlastní třídy obsahující metody Where, Select, ...
díky tomu, že kompilátor překládá jazykové konstrukce
LINQu na volání metod, vše funguje automaticky
výhodou je absolutní kontrola nad prováděním dotazu
nevýhodou pak samozřejmě implementační náročnost
LINQ – pod pokličkou
IEnumerable<T>
(data)

where

IQueryable<T>
(data)

(objekty)

where

orderby

(objekty)

orderby

(dotaz)

select

(výsledek)

select

(výsledek)
LINQ to Objects


Možnost použít LINQ na libovolné kolekce dat









v prostředí .NETu tedy každý objekt implementující
IEnumerable<T> (všechny třídy jako List<T>, Queue<T>,
Stack<T>, ale také (a hlavně) libovolné pole)
ve většině případů je zápis pomocí LINQu úspornější a
čitelnější
některé operace, jako např. řazení dat nebo použití
operátoru Join, jsou bez použití LINQ velmi krkolomné
použití LINQu přibližuje klasické programování
deklarativnímu
mohou však nastat případy, kdy použití LINQ není
optimální
LINQ to Objects – absurdní příklad


Následující příklad ukazuje, že téměř vše lze zapsat
jako LINQ dotaz
int i = 5;
var q = from t in new int[] { i }
where t == 6
select t;
if (q.Any()) Console.WriteLine(“i je 6”);



Totéž lze samozřejmě jednoduše vyjádřit jako
int i = 5;
if (i == 6) Console.WriteLine(“i je 6”);
LINQ to Objects – příklady


Dotaz na znaky v řetězci
string aString = "ABCDE99F-J74-12-89A";
// Select only those characters that are numbers
IEnumerable<char> stringQuery =
from ch in aString
where Char.IsDigit(ch)
select ch;
// Execute the query
foreach (char c in stringQuery)
Console.Write(c + " ");
// Call the Count method on the existing query.
int count = stringQuery.Count();
Console.WriteLine("Count = {0}", count);
LINQ to Objects – příklady


LINQ v kombinaci s Regular Expressions
// Create the regular expression to find all things "Visual".
Regex searchTerm =
new Regex(@"Visual(Basic|C#|C++|J#|SourceSafe|Studio)");
// Search the contents of each .htm file. Remove the where clause // to find even more matches! This query produces a list of files
// where a match was found, and a list of the matches in that file.
// Note: Explicit typing of "Match" in select clause. This is
// required because MatchCollection is not a generic IEnumerable
// collection.
var queryMatchingFiles =
from file in fileList
where file.Extension == ".htm”
let fileText = File.ReadAllText(file.FullName)
let matches = searchTerm.Matches(fileText)
where searchTerm.Matches(fileText).Count > 0
select new {
name = file.FullName,
matches = from Match match in matches
select match.Value
};
LINQ to Objects – příklady


Seskupení souborů podle velikosti
// Get all file names at the selected path
var fileNames = Directory.GetFiles(path, "*.*",
SearchOption.AllDirectories);
// Transform file names into objects describing the file
var fileList =
from name in fileNames
select new FileInfo(name);

// Group the files according to their size, leaving out
// files that are less than 200000 bytes.
var querySizeGroups =
from file in fileList
let len = file.Length
where len > 0
group file by (len / 100000) into fileGroup
where fileGroup.Key >= 2
orderby fileGroup.Key descending
select fileGroup;
LINQ to Objects – příklady


Ukázka použití operátoru Join
string[] names = File.ReadAllLines(@"../../../names.csv");
string[] scores = File.ReadAllLines(@"../../../scores.csv");
// Merge the data sources using a named type.
// Note the dynamic creation of a list of ints for the
// TestScores member. We skip 1 because the first string
// in the array is the student ID, not an exam score.
var queryNamesScores =
from name in names
let x = name.Split(',')
from score in scores
let s = score.Split(',')
where x[2] == s[0]
select new Student() {
FirstName = x[0],
LastName = x[1],
ID = Convert.ToInt32(x[2]),
ExamScores = (from scoreAsText in s.Skip(1)
select Convert.ToInt32(scoreAsText.ToList())
};
LINQ to XML


Změna přístupu ke XML dokumentům v .NETu



nové, vhodně navržené objekty
zabudování podpory XML přímo do jazyku Visual Basic,
podpora při kompilaci
Dim doc As XElement = _
<rss><channel>
<%= From post In MyData.Posts Take 5 Select _
<item><title><%= post.Title %></title></item> %>
</channel></rss>



Technika dotazování shodná jako u LINQ to Objects


nové objekty jen lépe vyhovují modelu LINQ dotazů
LINQ to XML


Přístup k elementům v XML dokumentu
<authors>
<author name="Ludwig" surname="Wittgenstein">
<book title="Tractatus Logico-Philosophicus" />
<book title="Philosophical Investigations" />
</author>
<author name="Rene" surname="Descartes">
<book title="Discourse on Method and the Meditations" />
<book title="Meditations and Other Metaphysical Writings" />
</author>
</authors>







– vrací všechny elementy
doc.Descendants("book") – vrací všechny knihy
doc.Element("authors") – vrací kořenový element
doc.Element("authors").Elements() – všechny autory
el.Attribute("title“) – vrací hodnotu atributu
doc.Descendants()
LINQ to XML – příklad dokumentu


Jednoduchý příklad XML dokumentu
<Contacts>
<Contact>
<Name>Patrick Hines</Name>
<Phone Type="Home">206-555-0144</Phone>
<Phone Type="Work">425-555-0145</Phone>
<Address>
<Street>123 Main St</Street>
<City>Mercer Island</City>
<State>WA</State>
<Postal>68042</Postal>
</Address>
</Contact>
</Contacts>
LINQ to XML – předchozí generace


Postup pro vytvoření XML dokumentu v .NET 2.0
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);
LINQ to XML – nová generace


Postup pro vytvoření XML dokumentu pomocí LINQ
to XML
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")
)
)
);
LINQ to XML – XPath (porovnání)


XPath dotaz a jeho ekvivalent











child (the default axis) – XContainer.Elements
Parent (..) – XObject.Parent
attribute axis (@) – XElement.Attribute nebo
Xelement.Attributes
ancestor axis – XNode.Ancestors
ancestor-or-self axis – XElement.AncestorsAndSelf
descendant axis (//) – XContainer.Descendants nebo
XContainer.DescendantNodes
descendant-or-self – XElement.DescendantsAndSelf nebo
XElement. DescendantNodesAndSelf
following-sibling – XNode.ElementsAfterSelf nebo
XNode.NodesAfterSelf
preceding-sibling – XNode.ElementsBeforeSelf nebo
XNode.NodesBeforeSelf
LINQ to SQL
NorthwindDataContext db = new NorthwindDataContext();
var q = from c in db.Customers
where c.Country == "UK"
select new { Name = c.ContactName, c.City }



Používá dva klíčové principy LINQu






Dotazy jako konstrukce jazyka C# 3.0
Kód jako data (Lambda výrazy & Expression Trees)

Jak to funguje?




Třídy popisují strukturu DB (NorthwindDataContext,…)
Dotaz se překládá do jazyka SQL a pouští na SQL
Serveru (využití rozhraní IQueryable<T>)
LINQ to SQL – struktura databáze


Model databáze lze vytvořit pomocí nástroje Object
Relational Designer (O/R Designer)




existují další nástroje, jako např. SQLMetal, nebo lze
popsat strukturu vlastním kódem podle přesných pravidel

Vše je zapouzdřeno v potomku třídy DataContext


jsou případy, kdy je na místě uvažovat o zapouzdření
vygenerovaného kódu pro omezení dostupných funkcí
pro uživatele kódu či omezení potenciálních chyb, lepší
podpoře transakcí atd.
LINQ to SQL – struktura databáze


Generuje se jedna třída pro každou tabulku



Atributy popisují mapování mezi objektem a databází
„Column“ mapuje sloupeček v DB a vlastnost objektu
[Table(Name="dbo.Customers")]
public partial class Customer
: INotifyPropertyChanging, INotifyPropertyChanged
private string _CustomerID;

{

[Column(Storage = "_CustomerID", DbType = "NChar(5)")]
public string CustomerID {
get { /* ... */ }
set { /* ... */ }
}
/* ... */
}
LINQ to SQL – struktura databáze


Třída „DataContext“ reprezentuje celou databázi



Obsahuje reprezentace tabulek (Table<Category>)
Je možné nahradit implementaci základních operací
[DatabaseAttribute(Name = "Northwind")]
public partial class NwindDataContext : DataContext
{
public Table<Category> Categories
{ get { /* ... */ } }
partial void InsertCategory(Category instance);
partial void UpdateCategory(Category instance);
partial void DeleteCategory(Category instance);
}



Případně obsahuje metody pro volání uložených procedur
LINQ to SQL – parametrické dotazy
1) Skládání částí dotazu jako řetězců




Od Microsoftu (hledejte „Dynamic LINQ library“)
Flexibilní, ale chybí kontrola v době kompilace
Umožňuje následující zápis:
var s = db.Products
.Where("UnitPrice > 10.0 and CategoryID = 42");
var q = from p in s
orderby p.ProductName select p;



Řetězce lze kombinovat se silně typovanými dotazy

2) Skládání lambda výrazů
LINQ to SQL – parametrické dotazy
1) Skládání částí dotazu jako řetězců
2) Skládání lambda výrazů



Hledejte „LINQ Queries at Runtime“
Zachová typovou kontrolu, ale více „myšlenkově
náročné“,
protože využívá pokročilejší vlastnosti lambda funkcí

ConditionCombinator combineOr =
(f, g) => (c) => f.Call(c) || g.Call(c);
Expression<Func<Product, bool>> cond1 = (p) => p.UnitPrice > 10.0M;
Expression<Func<Product, bool>> cond2 = (p) => p.CategoryID == 42;
var q = from p in db.Products
where combineOr.Call(cond1, cond2)
orderby p.ProductName select p;
LINQ to SQL – kompilované dotazy


LINQ musí pokaždé přeložit „strom“ na SQL dotaz



Lze se tomu nějak vyhnout, optimalizovat to?
Ano! LINQ umí tzv. „kompilované dotazy“
// Deklarace – lze např jako vlastnost ve třídě
Func<NorthwindDataContext, string,
IQueryable<Product>> ProductsByCategory;

// Vytváření předkompilovaného dotazu
ProductsByCategory = CompiledQuery.Compile(
(NorthwindDataContext db, string catName) =>
from p in db.Products
where p.Category.CategoryName == catName
select p);
// Volání předkompilovaného dotazu
var q = ProductsByCategory(db, "Beverages");
LINQ to SQL – implicitní řetězení dotazů


Jednoduché načítání souvisejících dat




potřebujeme např. ke každému zpracovávanému
zákazníkovi vždy znát i seznam jeho objednávek
implicitní využívání cizích klíčů je schopné data zajistit,
ale budeme mít množství dotazů směřujících do databáze
existuje řešení
Northwnd db = new Northwnd(@"c:northwnd.mdf");
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Customer>(c => c.Orders);
db.LoadOptions = dlo;
var londonCustomers =
from cust in db.Customers
where cust.City == “London“
select cust;
foreach (var custObj in londonCustomers) {
if (custObj.Orders.Any()) {
Console.WriteLine(custObj.CustomerID);
}
}
LINQ to SQL – implicitní filtrování dat


Jednoduché filtrování načítaných dat




pokud nás např. zajímají vždy jen objednávky neodeslané
dnes
nechceme tedy načítat vždy všechny objednávky, ani na
všech místech v kódu uvádět stejnou podmínku
existuje řešení
Northwnd db = new Northwnd(@"c:northwnd.mdf");
DataLoadOptions dlo = new DataLoadOptions();
dlo.AssociateWith<Customer>(c => c.Orders.Where(p =>
p.ShippedDate != DateTime.Today));
db.LoadOptions = dlo;
var custOrderQuery =
from cust in db.Customers
where cust.City == “London“
select cust;
LINQ to SQL – identita entit


Různé dotazy vrací „stejnou“ instanci



Stejný řádek podle primárního klíče v databázi
„DataContext“ má cache a vrátí stejný .NET objekt!
var db = new NwindDataContext();
var c1 = (from c in db.Categories
where c.CategoryName = "Beverages" select c).First();
c1.CategoryName = "Drinks";
var c2 = (from c in db.Categories
where c.CategoryID = 1 select c).First();
Console.WriteLine(c2.CategoryName);



Chování je možné pro zrychlení vypnout


…a pokud pouze načítáte data, pak je to výhodné:
db.ObjectTrackingEnabled = false;
LINQ to SQL – modifikace dat


Přímá podpora pouze pomocí načtených instancí
NwindDataContext db = new NwindDataContext();
var cat = (from c in db.Categories
where c.CategoryID == 42 select c).First();
db.Categories.DeleteOnSubmit(cat);
db.SubmitChanges();





„SubmitChanges“ ukládá změny do DB (nenastala-li
kolize)

Možnosti pro usnadnění a optimalizaci



Pomocí uložených procedur (není třeba nejprve načítat)
Jako metody v „partial“ třídě „DataContext“
LINQ to SQL – modifikace dat


Příklad vkládání dat
// Create a new Order object.
Order ord = new Order
{
OrderID = 12000,
ShipCity = "Seattle",
OrderDate = DateTime.Now
// …
};
// Add the new object to the Orders collection.
db.Orders.InsertOnSubmit(ord);
// Submit the change to the database.
db.SubmitChanges();
LINQ to SQL – transakční zpracování


Standardně používá „optimistic concurrency“





Při načtení se zapamatuje původní stav entit
Načtené entity se upravují pouze v paměti
Zavoláním „SubmitChanges()“ se změny zapisují do DB
Před zápisem kontroluje, zda došlo ke změně a
případná kolize způsobí výjimku
NwindDataContext db = new NwindDataContext();
var cat = db.Categories.First(c => c.CategoryID == 1);
cat.CategoryName = "Drinks";
db.SubmitChanges();
// LINQ zapíše změny do DB



Pomocí transakcí lze „pesimistic concurrency“
LINQ to SQL – transakční zpracování


Standardně používá „optimistic concurrency“



Pomocí transakcí lze „pesimistic concurrency“




Zamyká přistup k tabulce/řádku po dobu práce s ním
Může nastat „deadlock“ (a jeden z čekajících je zrušen)
např. pomocí: System.Transactions.TransactionScope
NwindDataContext db = new NwindDataContext();
using (TransactionScope ts = new TransactionScope())
{
var cat = db.Categories.First(c => c.CategoryID == 1);
cat.CategoryName = "Drinks";
db.SubmitChanges();
// LINQ zapíše změny do DB
ts.Complete();
// COMMIT transakce v DB
}
LINQ to SQL – n-vrstvá architektura


LINQ lze obecně použít kdekoli, ale ...





Nepovolit programátorům napsat hloupost
(např. stahování obsahu celé tabulky)
Ale chceme flexibilitu při psaní dotazů

Zapouzdříme vygenerovaný DataContext




Zpřístupníme pouze rozumné chování!
Např.: Produkty lze vyhledávat pouze podle kategorie
NorthwindDAL nd = new NorthwindDAL();
var productNames =
from p in nd.GetProductsByCategory(beveragesId)
select p.ProductName;
LINQ to SQL a ADO.NET


Využití jednoho připojení k databázi
SqlConnection nwindConn = new SqlConnection(connString);
nwindConn.Open();
Northwnd interop_db = new Northwnd(nwindConn);
SqlTransaction nwindTxn = nwindConn.BeginTransaction();
// ...
interop_db.Transaction = nwindTxn;
// ...
interop_db.SubmitChanges();
nwindTxn.Commit();
LINQ to DataSets


Podpora LINQ dotazů při práci s DataSety



Pomocí extension metod „Field“ a „AsEnumerable“
Existuje i varianta pro typované DataSety (generované
třídy již podporují všechna potřebná rozhraní)
using System.Data; // Potřebné extension metody!
var q = from r in ds.Tables["Customers"].AsEnumerable()
where r.Field<string>("City") == "London"
select new { Name = r.Field<string>("Name"),
City = r.Field<string>("City") };





Lze jednoduše řešit i případy Nullable-sloupců

Pozn.: Extension metody umožňují přidat podporu
pro LINQ k libovolným existujícím třídám!
LINQ to DataSets


Co když potřebujeme opakovat parametrické dotazy
mnohokrát?




dotazy do číselníků v paměti atd.

ADO.NET podporují DataView



LINQ dotaz nad DataSet není reprezentován jako
DataView
lze jednoduše řešit pomocí metody „AsDataView()“
var q = from row in data.AsEnumerable()
orderby row.Field<DateTime>(“Date”)
select row;
DataView rowsByDate = q.AsDataView();



má své limitace (nullable-sloupce apod.)
Zdroje



MSDN
Tomáš Petříček – Microsoft MVP pro C#

Programátorské večery


V příštím semestru sledujte nástěnku. Uskuteční se
přednáška na téma LINQ, kde se dostaneme i k
praktickým ukázkám či podrobnějšímu vysvětlení.

More Related Content

Viewers also liked (9)

Rozpoznání jazyků
Rozpoznání jazykůRozpoznání jazyků
Rozpoznání jazyků
 
Ilustracje materiały dodatkowe
Ilustracje   materiały dodatkoweIlustracje   materiały dodatkowe
Ilustracje materiały dodatkowe
 
Coq Au Win
Coq Au WinCoq Au Win
Coq Au Win
 
Pasar grosir di jakarta
Pasar grosir di jakartaPasar grosir di jakarta
Pasar grosir di jakarta
 
Coordenadas geograficas
Coordenadas geograficasCoordenadas geograficas
Coordenadas geograficas
 
Apishaya
ApishayaApishaya
Apishaya
 
Pory roku zdjecia kobiet i pejzaży
Pory roku   zdjecia kobiet i pejzażyPory roku   zdjecia kobiet i pejzaży
Pory roku zdjecia kobiet i pejzaży
 
Artificial life
Artificial lifeArtificial life
Artificial life
 
Variyapron junthong
Variyapron junthongVariyapron junthong
Variyapron junthong
 

Similar to Linq

Tv 06
Tv 06Tv 06
Tv 06
352
 
Develconf coffeescript
Develconf coffeescriptDevelconf coffeescript
Develconf coffeescript
Martin Maly
 
Dark Side of iOS [mDevCamp 2013]
Dark Side of iOS [mDevCamp 2013]Dark Side of iOS [mDevCamp 2013]
Dark Side of iOS [mDevCamp 2013]
Kuba Břečka
 

Similar to Linq (12)

Clean code
Clean codeClean code
Clean code
 
201502.ReinIT.Dev
201502.ReinIT.Dev201502.ReinIT.Dev
201502.ReinIT.Dev
 
INPTP Rekapitulace
INPTP Rekapitulace INPTP Rekapitulace
INPTP Rekapitulace
 
Technologie užívané při vývoji velkých e-shopů
Technologie užívané při vývoji velkých e-shopůTechnologie užívané při vývoji velkých e-shopů
Technologie užívané při vývoji velkých e-shopů
 
Tv 06
Tv 06Tv 06
Tv 06
 
CQRS v rohlik.cz
CQRS v rohlik.czCQRS v rohlik.cz
CQRS v rohlik.cz
 
PZ2021
PZ2021PZ2021
PZ2021
 
Představení Ruby on Rails
Představení Ruby on RailsPředstavení Ruby on Rails
Představení Ruby on Rails
 
.NET v SQL Serveru
.NET v SQL Serveru.NET v SQL Serveru
.NET v SQL Serveru
 
Dependency injection v .Net Frameworku
Dependency injection v .Net FrameworkuDependency injection v .Net Frameworku
Dependency injection v .Net Frameworku
 
Develconf coffeescript
Develconf coffeescriptDevelconf coffeescript
Develconf coffeescript
 
Dark Side of iOS [mDevCamp 2013]
Dark Side of iOS [mDevCamp 2013]Dark Side of iOS [mDevCamp 2013]
Dark Side of iOS [mDevCamp 2013]
 

Linq

  • 2. Agenda    Co je to LINQ? Jak lze LINQ použít? Co musím znát, abychom se mohli dotazovat v LINQu? Pokračování    LINQ to Objects (anything and everything) LINQ to XML LINQ to ADO.NET (to SQL, to DataSets)
  • 3. LINQ - Language INtegrated Query  Vlastní pro rodinu .NET jazyků  Visual Basic, C# a další  Dotazování jako součást programovacího jazyka  Dotazování nad „libovolným“ datovým zdrojem   SQL, XML, IEnumerable, další... Využití znalosti metadat  Typová kontrola, našeptávání (Intellisense), kontrola syntaxe
  • 4. LINQ - co můžeme použít?  Základní operace   Třízení   GroupBy Agregační funkce   OrderBy, OrderByDescending Seskupování (Group)   Select, From, Where Count, Min, Max, Average, ... Spojování pomocí operátoru Join a další operace
  • 5. LINQ - co musíme znát?  Lambda výrazy  Expression trees (Stromy výrazů)  Extension Metody  Lokální dedukce typů  Anonymní typy  Inicializace dat  Lambda Výrazy – Kód jako data
  • 6. LINQ - lambda výrazy  Předání metody jako parametr jiné metodě   Doplnění nějakého obecného chování (např. jak se mají porovnávat dva řetězce) Zápis lambda výrazů v C# 3.0   Generické delegáty jsou součástí .NET knihoven Func<…>, liší se počtem typových parametrů Možnost zápisu anonymních delegátů (arg1, arg2) => výraz delegate TResult Func<TResult>(); delegate TResult Func<T1, TResult>(T1 a1); delegate TResult Func<T1, T2, TResult>(T1 a1, T2 a2); Func<string, string, string> add = (a, b) => a + " " + b; string res = add("Hello", "world"); Console.WriteLine(res);
  • 7. LINQ - lambda výrazy • Dva typy podle výrazu za šipkou – Výraz – tedy to co lze napsat v přiřazení ( a = <výraz>; ) – Blok – umožňuje zapsat více-řádkový kód Func<int, int, int> add = (a, b) => a + b; Func<int, int, int> add = (a, b) => { Console.WriteLine("scitam!"); return a + b; }; • Typy parametrů C# pozná (většinou) automaticky – Ale lze je případně doplnit Func<int, int, int> add = (int a, int b) => a + b;
  • 8. LINQ - expression trees  „kód jako data“   Lambda výrazy typu Expression<Func<…>> Neformálně: Vrátí objekty, které popisují to, co jsme do výrazu napsali - něco jako vrátit zdrojový kód výrazu Expression<Func<int, int, int>> exprAdd = (a, b) => a + b;  O tom později   .. je to pro LINQ klíčové! Stojí na tom implementace „LINQ to SQL“
  • 9. LINQ – extension metody  Zápis static class StringUtils { static public string Reverse(this string s) { StringBuilder sb = new StringBuilder(); for (int i = s.Length - 1; i >= 0; i--) sb.Append(s[i]); return sb.ToString(); } }  Užití string s = "dlrow olleH"; string r = s.Reverse();
  • 10. LINQ - extension metody  Jak to přesně funguje?      Musí být statická, ve statické třídě První parametr označen klíčovým slovem this Musí být v jmenném prostoru otevřeném pomocí using Existuje-li normální metoda se stejným jménem, volá se ta. Je to bezpečné?   Samozřejmě, extension metoda má práva přistupovat jen k public členům původní třídy Funguje to pouze jako „syntactic sugar“ Enumerable.Select( Enumerable.Where(t, n => n % 2 == 0), n => n * n); vs. t.Where(n => n % 2 == 0).Select(n => n * n);
  • 11. LINQ - dedukce typu  Klíčové slůvko var pro deklaraci proměnné     Typ proměnné se odvodí z výrazu na pravé straně Je to typově bezpečné a typ je vždy známý Výraz na pravé straně nesmí být null Lze použít i ve for a foreach cyklech // C# 3.0 var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] { 1, 2, 3 }; var orders = new Dictionary<int, Order>(); ' Visual Basic 9 Dim e = 10
  • 12. LINQ - dedukce typu  Pro Visual Studio se jedná o obyčejné proměnné   … a zobrazuje jejich typ C# kompilátor zná typ
  • 13. LINQ - dedukce typu • Vše je stále typově bezpečné – Nejedná se tedy o typ object! ' Visual Basic 9 Dim e = 10 Chyba: Neznámý typ! // C# var e var n var d 3.0 Chyba: n je typu ‘int’ Vhodné použití  = null; = 10; n.SomeFunction(); = new Dictionary<string, ICollection<string>>(); • Používejte ji rozumně! – Inicializace „ošklivých“ typů – Klíčové pro anonymní typy (uvidíme později)
  • 14. LINQ - anonymní typy var person = new { Name = „Adam", City = "Praha"}; Console.WriteLine(person.City); Dim person = New With {.Name = „Adam", .City = "Praha"} person.City = "Brno" Console.WriteLine(person.City)   Vytváří typ, který má uvedené vlastnosti Jméno typu nelze zapsat…    Lze je používat pouze lokálně díky dedukcí typů Nelze vrátit z metody (pouze jako object) (pozn.: v C# mají pouze get, ve VB i set)
  • 15. LINQ - inicializace dat  Inicializace objektů  Podobná syntaxe jako při zápisu anonymních typů // Anonymní typ var p = new { Name = „Adam", City = "Praha" }; // Pojmenovaný typ var p = new Person { Name = „Adam", City = "Praha" };  Lze použít na libovolné .NET typy s vlastnostmi var f = new Form { BackColor = Color.Green, Text = "Hello!" };  Je možné volat vlastní konstruktor při vytváření var c = new Customer("John", "Doe") { City = "New York" };
  • 16. LINQ - inicializace objektů  Jak to funguje?  Úsporný zápis pro obyčejné nastavování vlastností class Customer { public string Name { get; set; } public string Country { get; set; } public string City { get; set; } } var c = new Customer { Name = "Jose", Country = "Spain", City="Barcelona" } Dim c = New Customer With { .Name = "Jose", .Country = "Spain", .City="Barcelona" } Customer _temp = new Customer(); _temp.Name = "Jose"; _temp.Country = "Spain"; _temp.City = "Barcelona"; Customer c = _temp;
  • 17. LINQ - inicializace kolekcí  Podobné, jako inicializace pole, ale pro ostatní kolekce var numsA = new int[] { 1, 2, 3, 4, 5, 6 }; var numsL = new List<int> { 1, 2, 3, 4, 5, 6 };  Samozřejmě, možno kombinovat s inicializací objektů var custs = new List<Customer> { new Customer { Name = "John", Surname = "Doe", City = "New York" }, new Customer { Name = "Tomas", Surname="Petricek", City = "Prague" } };  Lze inicializovat i komplikovanější kolekce var spellings = new Dictionary<int, string> { { 0, "Zero" }, { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
  • 18. LINQ - kód jako data  Druhá možnost, jak deklarovat lambda výrazy   Místo Func<…> použijeme Expression<Func<…>> To je reprezentace zdrojového kódu výrazu using System.Linq.Expressions; // Visual Basic Dim square As Expression(Of Func(Of Integer, String)) = _ Function(i) "Number: " + (i.ToString()) // C# Expression<Func<int, string>> square = (i) => "Number: " + (i.ToString());  Použijeme-li WriteLine/ToString, získáme pseudokód: Console.WriteLine(square); // Vypíše: i => ("Number: " + i.ToString())
  • 19. LINQ - kód jako data  Volba mezi stromem a delegátem na základě typu   Při deklaraci nelze používat var! Func<...> Kompiluje se jako delegát Expression<Func<...>> Uloží se jako data (strom) Kód funkce se uloží jako data (strom)     Kód (strom) se např. dá přeložit do jazyka SQL Stromy lze konstruovat z kódu Stromy lze upravovat (vytvořit změněnou kopii) Stromy lze kompilovat na spustitelný kód (za běhu)
  • 20. LINQ - kód jako data  PŘÍKLAD: Strom následujícího výrazu: Expression<Func<int, string>> square = (i) => "Number: " + (i.ToString()); ParameterExpression (Type=int, Name =“i”) LambdaExpression (params => body) ConstantExpression (“Number: “) BinaryExpression (Type=“Add”) MethodCallExpression (Object.ToString)  ParameterExpression (Type=int, Name=“i”) Výraz (strom) bude k dispozici za běhu programu  Může být různě zpracován (např. přeložen do SQL)
  • 21. LINQ - dotazy  Pomocí lambda výrazů a extension metod lze zapsat některé obvyklé operace s daty: var q = db.Products .Where(p => p.ProductName.StartsWith("C")) .Select(p => p.ProductName);  Ale je to dost dobré? … jak by vypadal Join nebo GroupBy? var q = from p in db.Products where p.ProductName.StartsWith("C“) select p.ProductName;   Pro tyto běžné příklady má C# (a VB) rozšíření, které přidává dotazy přímo do programovacího jazyka Vnitřně se stále vše překládá pomocí extension metod
  • 22. LINQ - základní dotazy  Standardní dotazy s Where a Select:  ID kategorií, které se jmenují Beverages var q = from c in db.Categories where c.CategoryName == "Beverages" select c.CategoryID;  Chceme pouze první takovou… var q = (from c in db.Categories where c.CategoryName == "Beverages" select c.CategoryID).First();  Vracíme anonymní typ – pouze jméno a cenu produktu: var q = from p in db.Products where p.CategoryID == 1 select new { p.ProductName, Price = p.UnitPrice };
  • 23. LINQ - dotazy  Řazení položek pomocí klauzule OrderBy  Řazení produktů podle jména: var q = from p in db.Products orderby p.ProductName select p;  Vzestupné (ascending) a sestupné (descending) pořadí: var q = from p in db.Products orderby p.ProductName ascending, p.UnitPrice descending select p;
  • 24. LINQ - dotaz Začíná klíčovým slovem from Následují klauzule from, join, let, where, orderby from id in source { from id in source | join id in source on expr equals expr [ into id ] | let id = expr | Na konec select where condition | nebo group by orderby ordering, ordering, … } select expr | group expr by key [ into id query ] Může následovat klauzule into
  • 25. LINQ - dotazy  Spojení více zdrojů dat pomocí operace Join  Použití více klauzulí from v dotazu: var q = from p in db.Products from c in db.Categories where p.CategoryID == c.CategoryID select new { Category = c.CategoryName, Product = p.ProductName };  Totéž pomocí operátoru Join (inner join) var q = from p in db.Products join c in db.Categories on p.CategoryID equals c.CategoryID select new { Category = c.CategoryName, Product = p.ProductName };  Pozn.: Outer join lze zapsat pomocí metody DefaultIfEmpty()
  • 26. LINQ - dotazy  Seskupování pomocí group … by  Narozdíl od SQL vrací objekty skutečně po skupinách (nikoliv jako seznam, kde jsou skupiny po sobě) var q = from p in db.Products group p by p.CategoryID; foreach (var group in q) { Console.WriteLine(group.Key); foreach (var prod in group) Console.WriteLine(prod.ProductName); }  Použití klauzule into pro další práci se skupinami: var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, Count = (from p in g where p.UnitPrice > 10.0M select g) .Count() };
  • 27. LINQ - dotazy  Zápis dotazů pomocí volání metod  Počet produktů v dané kategorii: var q = db.Products .Where(p => p.CategoryID == 2) .Count();  Seřadíme podle jména a vrátíme index produktu a jméno: var q = db.Products .OrderBy(p => p.ProductName) .Select((p, index) => new { Index = index, Name = p.ProductName });  „Stránkování“ pomocí operátorů Skip a Take: var q = db.Products .OrderBy(p => p.ProductName) .Skip(5*page).Take(page);
  • 28. Část druhá LINQ to * Jan Ambrož
  • 29. LINQ obecně  Proč potřebujeme nové technologie pro práci s daty? C# 3.0 VB 9.0 Others… LINQ: Langauge INtegrated Query LINQ to Objects LINQ to DataSets LINQ to SQL LINQ to Entities LINQ to XML <book> <title/> <author/> <year/> <price/> </book> Objekty Databáze XML
  • 30. LINQ – pod pokličkou  Většina použití LINQu se týká kolekcí dat    tedy rozhraní IEnumerable<T> pod .NET Základní implementace ve statické třídě Enumerable Pro použití nutné importovat namespace System.Linq     tím je umožněno využívat Extension Methods definované statickou třídou Enumerable tato třída definuje metody pro všechny potřebné operátory použití každého operátoru vrací mírně modifikované rozhraní IEnumerable<T> výsledkem je tedy opět rozhraní IEnumerable<T>, tedy opět kolekce dat
  • 31. LINQ – pod pokličkou  Ne vždy je však toto řešení ideální   např. LINQ to SQL – nechceme nahrávat obsah celé tabulky a pak filtrovat a řadit data Řešením je použití rozhraní IQueryable<T>     tvorba Expression Tree používáním LINQ operátorů využití Extension Methods definovaných třídou Queryable tzv. Provider následně transformuje Expression Tree do výsledné podoby vlastního dotazu funkčnost není omezena, rozhraní je potomkem IEnumerable<T>, stále tedy lze využít např. foreach cyklus k procházení výsledků
  • 32. LINQ – pod pokličkou  Použití IQueryable<T>    LINQ to SQL – nejprve je vytvořen dotaz, který je následně transformován do SQL jazyka LINQ to Anything – dotazy vůči webovým službám, webovým stránkám, ... Další možností je manuální implementace operátorů     definice vlastní třídy obsahující metody Where, Select, ... díky tomu, že kompilátor překládá jazykové konstrukce LINQu na volání metod, vše funguje automaticky výhodou je absolutní kontrola nad prováděním dotazu nevýhodou pak samozřejmě implementační náročnost
  • 33. LINQ – pod pokličkou IEnumerable<T> (data) where IQueryable<T> (data) (objekty) where orderby (objekty) orderby (dotaz) select (výsledek) select (výsledek)
  • 34. LINQ to Objects  Možnost použít LINQ na libovolné kolekce dat      v prostředí .NETu tedy každý objekt implementující IEnumerable<T> (všechny třídy jako List<T>, Queue<T>, Stack<T>, ale také (a hlavně) libovolné pole) ve většině případů je zápis pomocí LINQu úspornější a čitelnější některé operace, jako např. řazení dat nebo použití operátoru Join, jsou bez použití LINQ velmi krkolomné použití LINQu přibližuje klasické programování deklarativnímu mohou však nastat případy, kdy použití LINQ není optimální
  • 35. LINQ to Objects – absurdní příklad  Následující příklad ukazuje, že téměř vše lze zapsat jako LINQ dotaz int i = 5; var q = from t in new int[] { i } where t == 6 select t; if (q.Any()) Console.WriteLine(“i je 6”);  Totéž lze samozřejmě jednoduše vyjádřit jako int i = 5; if (i == 6) Console.WriteLine(“i je 6”);
  • 36. LINQ to Objects – příklady  Dotaz na znaky v řetězci string aString = "ABCDE99F-J74-12-89A"; // Select only those characters that are numbers IEnumerable<char> stringQuery = from ch in aString where Char.IsDigit(ch) select ch; // Execute the query foreach (char c in stringQuery) Console.Write(c + " "); // Call the Count method on the existing query. int count = stringQuery.Count(); Console.WriteLine("Count = {0}", count);
  • 37. LINQ to Objects – příklady  LINQ v kombinaci s Regular Expressions // Create the regular expression to find all things "Visual". Regex searchTerm = new Regex(@"Visual(Basic|C#|C++|J#|SourceSafe|Studio)"); // Search the contents of each .htm file. Remove the where clause // to find even more matches! This query produces a list of files // where a match was found, and a list of the matches in that file. // Note: Explicit typing of "Match" in select clause. This is // required because MatchCollection is not a generic IEnumerable // collection. var queryMatchingFiles = from file in fileList where file.Extension == ".htm” let fileText = File.ReadAllText(file.FullName) let matches = searchTerm.Matches(fileText) where searchTerm.Matches(fileText).Count > 0 select new { name = file.FullName, matches = from Match match in matches select match.Value };
  • 38. LINQ to Objects – příklady  Seskupení souborů podle velikosti // Get all file names at the selected path var fileNames = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories); // Transform file names into objects describing the file var fileList = from name in fileNames select new FileInfo(name); // Group the files according to their size, leaving out // files that are less than 200000 bytes. var querySizeGroups = from file in fileList let len = file.Length where len > 0 group file by (len / 100000) into fileGroup where fileGroup.Key >= 2 orderby fileGroup.Key descending select fileGroup;
  • 39. LINQ to Objects – příklady  Ukázka použití operátoru Join string[] names = File.ReadAllLines(@"../../../names.csv"); string[] scores = File.ReadAllLines(@"../../../scores.csv"); // Merge the data sources using a named type. // Note the dynamic creation of a list of ints for the // TestScores member. We skip 1 because the first string // in the array is the student ID, not an exam score. var queryNamesScores = from name in names let x = name.Split(',') from score in scores let s = score.Split(',') where x[2] == s[0] select new Student() { FirstName = x[0], LastName = x[1], ID = Convert.ToInt32(x[2]), ExamScores = (from scoreAsText in s.Skip(1) select Convert.ToInt32(scoreAsText.ToList()) };
  • 40. LINQ to XML  Změna přístupu ke XML dokumentům v .NETu   nové, vhodně navržené objekty zabudování podpory XML přímo do jazyku Visual Basic, podpora při kompilaci Dim doc As XElement = _ <rss><channel> <%= From post In MyData.Posts Take 5 Select _ <item><title><%= post.Title %></title></item> %> </channel></rss>  Technika dotazování shodná jako u LINQ to Objects  nové objekty jen lépe vyhovují modelu LINQ dotazů
  • 41. LINQ to XML  Přístup k elementům v XML dokumentu <authors> <author name="Ludwig" surname="Wittgenstein"> <book title="Tractatus Logico-Philosophicus" /> <book title="Philosophical Investigations" /> </author> <author name="Rene" surname="Descartes"> <book title="Discourse on Method and the Meditations" /> <book title="Meditations and Other Metaphysical Writings" /> </author> </authors>      – vrací všechny elementy doc.Descendants("book") – vrací všechny knihy doc.Element("authors") – vrací kořenový element doc.Element("authors").Elements() – všechny autory el.Attribute("title“) – vrací hodnotu atributu doc.Descendants()
  • 42. LINQ to XML – příklad dokumentu  Jednoduchý příklad XML dokumentu <Contacts> <Contact> <Name>Patrick Hines</Name> <Phone Type="Home">206-555-0144</Phone> <Phone Type="Work">425-555-0145</Phone> <Address> <Street>123 Main St</Street> <City>Mercer Island</City> <State>WA</State> <Postal>68042</Postal> </Address> </Contact> </Contacts>
  • 43. LINQ to XML – předchozí generace  Postup pro vytvoření XML dokumentu v .NET 2.0 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);
  • 44. LINQ to XML – nová generace  Postup pro vytvoření XML dokumentu pomocí LINQ to XML 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") ) ) );
  • 45. LINQ to XML – XPath (porovnání)  XPath dotaz a jeho ekvivalent          child (the default axis) – XContainer.Elements Parent (..) – XObject.Parent attribute axis (@) – XElement.Attribute nebo Xelement.Attributes ancestor axis – XNode.Ancestors ancestor-or-self axis – XElement.AncestorsAndSelf descendant axis (//) – XContainer.Descendants nebo XContainer.DescendantNodes descendant-or-self – XElement.DescendantsAndSelf nebo XElement. DescendantNodesAndSelf following-sibling – XNode.ElementsAfterSelf nebo XNode.NodesAfterSelf preceding-sibling – XNode.ElementsBeforeSelf nebo XNode.NodesBeforeSelf
  • 46. LINQ to SQL NorthwindDataContext db = new NorthwindDataContext(); var q = from c in db.Customers where c.Country == "UK" select new { Name = c.ContactName, c.City }  Používá dva klíčové principy LINQu    Dotazy jako konstrukce jazyka C# 3.0 Kód jako data (Lambda výrazy & Expression Trees) Jak to funguje?   Třídy popisují strukturu DB (NorthwindDataContext,…) Dotaz se překládá do jazyka SQL a pouští na SQL Serveru (využití rozhraní IQueryable<T>)
  • 47. LINQ to SQL – struktura databáze  Model databáze lze vytvořit pomocí nástroje Object Relational Designer (O/R Designer)   existují další nástroje, jako např. SQLMetal, nebo lze popsat strukturu vlastním kódem podle přesných pravidel Vše je zapouzdřeno v potomku třídy DataContext  jsou případy, kdy je na místě uvažovat o zapouzdření vygenerovaného kódu pro omezení dostupných funkcí pro uživatele kódu či omezení potenciálních chyb, lepší podpoře transakcí atd.
  • 48. LINQ to SQL – struktura databáze  Generuje se jedna třída pro každou tabulku   Atributy popisují mapování mezi objektem a databází „Column“ mapuje sloupeček v DB a vlastnost objektu [Table(Name="dbo.Customers")] public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged private string _CustomerID; { [Column(Storage = "_CustomerID", DbType = "NChar(5)")] public string CustomerID { get { /* ... */ } set { /* ... */ } } /* ... */ }
  • 49. LINQ to SQL – struktura databáze  Třída „DataContext“ reprezentuje celou databázi   Obsahuje reprezentace tabulek (Table<Category>) Je možné nahradit implementaci základních operací [DatabaseAttribute(Name = "Northwind")] public partial class NwindDataContext : DataContext { public Table<Category> Categories { get { /* ... */ } } partial void InsertCategory(Category instance); partial void UpdateCategory(Category instance); partial void DeleteCategory(Category instance); }  Případně obsahuje metody pro volání uložených procedur
  • 50. LINQ to SQL – parametrické dotazy 1) Skládání částí dotazu jako řetězců    Od Microsoftu (hledejte „Dynamic LINQ library“) Flexibilní, ale chybí kontrola v době kompilace Umožňuje následující zápis: var s = db.Products .Where("UnitPrice > 10.0 and CategoryID = 42"); var q = from p in s orderby p.ProductName select p;  Řetězce lze kombinovat se silně typovanými dotazy 2) Skládání lambda výrazů
  • 51. LINQ to SQL – parametrické dotazy 1) Skládání částí dotazu jako řetězců 2) Skládání lambda výrazů   Hledejte „LINQ Queries at Runtime“ Zachová typovou kontrolu, ale více „myšlenkově náročné“, protože využívá pokročilejší vlastnosti lambda funkcí ConditionCombinator combineOr = (f, g) => (c) => f.Call(c) || g.Call(c); Expression<Func<Product, bool>> cond1 = (p) => p.UnitPrice > 10.0M; Expression<Func<Product, bool>> cond2 = (p) => p.CategoryID == 42; var q = from p in db.Products where combineOr.Call(cond1, cond2) orderby p.ProductName select p;
  • 52. LINQ to SQL – kompilované dotazy  LINQ musí pokaždé přeložit „strom“ na SQL dotaz   Lze se tomu nějak vyhnout, optimalizovat to? Ano! LINQ umí tzv. „kompilované dotazy“ // Deklarace – lze např jako vlastnost ve třídě Func<NorthwindDataContext, string, IQueryable<Product>> ProductsByCategory; // Vytváření předkompilovaného dotazu ProductsByCategory = CompiledQuery.Compile( (NorthwindDataContext db, string catName) => from p in db.Products where p.Category.CategoryName == catName select p); // Volání předkompilovaného dotazu var q = ProductsByCategory(db, "Beverages");
  • 53. LINQ to SQL – implicitní řetězení dotazů  Jednoduché načítání souvisejících dat    potřebujeme např. ke každému zpracovávanému zákazníkovi vždy znát i seznam jeho objednávek implicitní využívání cizích klíčů je schopné data zajistit, ale budeme mít množství dotazů směřujících do databáze existuje řešení Northwnd db = new Northwnd(@"c:northwnd.mdf"); DataLoadOptions dlo = new DataLoadOptions(); dlo.LoadWith<Customer>(c => c.Orders); db.LoadOptions = dlo; var londonCustomers = from cust in db.Customers where cust.City == “London“ select cust; foreach (var custObj in londonCustomers) { if (custObj.Orders.Any()) { Console.WriteLine(custObj.CustomerID); } }
  • 54. LINQ to SQL – implicitní filtrování dat  Jednoduché filtrování načítaných dat    pokud nás např. zajímají vždy jen objednávky neodeslané dnes nechceme tedy načítat vždy všechny objednávky, ani na všech místech v kódu uvádět stejnou podmínku existuje řešení Northwnd db = new Northwnd(@"c:northwnd.mdf"); DataLoadOptions dlo = new DataLoadOptions(); dlo.AssociateWith<Customer>(c => c.Orders.Where(p => p.ShippedDate != DateTime.Today)); db.LoadOptions = dlo; var custOrderQuery = from cust in db.Customers where cust.City == “London“ select cust;
  • 55. LINQ to SQL – identita entit  Různé dotazy vrací „stejnou“ instanci   Stejný řádek podle primárního klíče v databázi „DataContext“ má cache a vrátí stejný .NET objekt! var db = new NwindDataContext(); var c1 = (from c in db.Categories where c.CategoryName = "Beverages" select c).First(); c1.CategoryName = "Drinks"; var c2 = (from c in db.Categories where c.CategoryID = 1 select c).First(); Console.WriteLine(c2.CategoryName);  Chování je možné pro zrychlení vypnout  …a pokud pouze načítáte data, pak je to výhodné: db.ObjectTrackingEnabled = false;
  • 56. LINQ to SQL – modifikace dat  Přímá podpora pouze pomocí načtených instancí NwindDataContext db = new NwindDataContext(); var cat = (from c in db.Categories where c.CategoryID == 42 select c).First(); db.Categories.DeleteOnSubmit(cat); db.SubmitChanges();   „SubmitChanges“ ukládá změny do DB (nenastala-li kolize) Možnosti pro usnadnění a optimalizaci   Pomocí uložených procedur (není třeba nejprve načítat) Jako metody v „partial“ třídě „DataContext“
  • 57. LINQ to SQL – modifikace dat  Příklad vkládání dat // Create a new Order object. Order ord = new Order { OrderID = 12000, ShipCity = "Seattle", OrderDate = DateTime.Now // … }; // Add the new object to the Orders collection. db.Orders.InsertOnSubmit(ord); // Submit the change to the database. db.SubmitChanges();
  • 58. LINQ to SQL – transakční zpracování  Standardně používá „optimistic concurrency“     Při načtení se zapamatuje původní stav entit Načtené entity se upravují pouze v paměti Zavoláním „SubmitChanges()“ se změny zapisují do DB Před zápisem kontroluje, zda došlo ke změně a případná kolize způsobí výjimku NwindDataContext db = new NwindDataContext(); var cat = db.Categories.First(c => c.CategoryID == 1); cat.CategoryName = "Drinks"; db.SubmitChanges(); // LINQ zapíše změny do DB  Pomocí transakcí lze „pesimistic concurrency“
  • 59. LINQ to SQL – transakční zpracování  Standardně používá „optimistic concurrency“  Pomocí transakcí lze „pesimistic concurrency“    Zamyká přistup k tabulce/řádku po dobu práce s ním Může nastat „deadlock“ (a jeden z čekajících je zrušen) např. pomocí: System.Transactions.TransactionScope NwindDataContext db = new NwindDataContext(); using (TransactionScope ts = new TransactionScope()) { var cat = db.Categories.First(c => c.CategoryID == 1); cat.CategoryName = "Drinks"; db.SubmitChanges(); // LINQ zapíše změny do DB ts.Complete(); // COMMIT transakce v DB }
  • 60. LINQ to SQL – n-vrstvá architektura  LINQ lze obecně použít kdekoli, ale ...    Nepovolit programátorům napsat hloupost (např. stahování obsahu celé tabulky) Ale chceme flexibilitu při psaní dotazů Zapouzdříme vygenerovaný DataContext   Zpřístupníme pouze rozumné chování! Např.: Produkty lze vyhledávat pouze podle kategorie NorthwindDAL nd = new NorthwindDAL(); var productNames = from p in nd.GetProductsByCategory(beveragesId) select p.ProductName;
  • 61. LINQ to SQL a ADO.NET  Využití jednoho připojení k databázi SqlConnection nwindConn = new SqlConnection(connString); nwindConn.Open(); Northwnd interop_db = new Northwnd(nwindConn); SqlTransaction nwindTxn = nwindConn.BeginTransaction(); // ... interop_db.Transaction = nwindTxn; // ... interop_db.SubmitChanges(); nwindTxn.Commit();
  • 62. LINQ to DataSets  Podpora LINQ dotazů při práci s DataSety   Pomocí extension metod „Field“ a „AsEnumerable“ Existuje i varianta pro typované DataSety (generované třídy již podporují všechna potřebná rozhraní) using System.Data; // Potřebné extension metody! var q = from r in ds.Tables["Customers"].AsEnumerable() where r.Field<string>("City") == "London" select new { Name = r.Field<string>("Name"), City = r.Field<string>("City") };   Lze jednoduše řešit i případy Nullable-sloupců Pozn.: Extension metody umožňují přidat podporu pro LINQ k libovolným existujícím třídám!
  • 63. LINQ to DataSets  Co když potřebujeme opakovat parametrické dotazy mnohokrát?   dotazy do číselníků v paměti atd. ADO.NET podporují DataView   LINQ dotaz nad DataSet není reprezentován jako DataView lze jednoduše řešit pomocí metody „AsDataView()“ var q = from row in data.AsEnumerable() orderby row.Field<DateTime>(“Date”) select row; DataView rowsByDate = q.AsDataView();  má své limitace (nullable-sloupce apod.)
  • 64. Zdroje   MSDN Tomáš Petříček – Microsoft MVP pro C# Programátorské večery  V příštím semestru sledujte nástěnku. Uskuteční se přednáška na téma LINQ, kde se dostaneme i k praktickým ukázkám či podrobnějšímu vysvětlení.