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);
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í.