.NET

advertisement
Microsoft
.NET
Лекция 4:
LINQ
Меженин Михаил, кафедра "Системное программирование", ВМИ, ЮУрГУ, 2014
Microsoft
.NET
LINQ
LINQ – Language Integrated Query
Набор возможностей языка и фреймворка для выполнения
запросов над локальными наборами данных программы.
2
Microsoft
.NET
LINQ
Последовательность – любой объект, реализующий
интерфейс IEnumerable<T>:
string[] names = { "Tom", "Dick", "Harry" };
List<float> numbers = new List<string>();
string chars = "abcdefg";
3
Microsoft
.NET
LINQ
Оператор запроса – метод, изменяющий входную
последовательность.
Класс System.Linq.Enumerable реализует около 40
стандартных операторов.
System.Linq.Enumerable.Where(…);
4
Microsoft
.NET
LINQ
Запрос – выражение, при прохождении которого входная
последовательность модифицируется операторами
запроса.
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames =
System.Linq.Enumerable.Where(names,n => n.Length >= 4);
foreach (string n in filteredNames)
Console.WriteLine (n);
// Dick, Harry
5
Microsoft
.NET
LINQ
Операторы запроса реализованы как методы расширения:
using System.Linq;
string[] names = { "Tom", "Dick", "Harry" };
var filteredNames =
System.Linq.Enumerable.Where(names,n => n.Length >= 4);
var filteredNames2 = names.Where (n => n.Length >= 4);
foreach (string n in filteredNames)
Console.WriteLine (n);
// Dick, Harry
6
Microsoft
.NET
LINQ
Операторы запроса принимают в качестве аргумента
лямбда-выражение:
Для каждого элемента n вернуть значение выражения n.Length >= 4
n => n.Length >= 4
Для каждого элемента n вернуть n+2
n => n + 2
7
Microsoft
.NET
LINQ
Стандартный синтаксис (fluent syntax):
var filteredNames = names.Where (n => n.Length >= 4);
Альтернативный синтаксис (query expression syntax):
var filteredNames = from n in names
where n.Length >= 4
select n;
8
Microsoft
.NET
Цепочки операторов
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());
JAY
MARY
HARRY
9
Microsoft
.NET
Цепочки операторов
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());
Query Syntax:
var query2 =
from n in names
where n.Contains ("a")
orderby n.Length
select n.ToUpper();
10
Microsoft
.NET
Цепочки операторов
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());
~SQL:
SELECT
FROM
WHERE
ORDER BY
UPPER(names.n)
names
names.n.Contains("a")
LEN(names.n.Length)
11
Microsoft
.NET
Цепочки операторов
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());
Без методов расширения:
var query = Enumerable.Select (
Enumerable.OrderBy (
Enumerable.Where (
names, n => n.Contains ("a")
), n => n.Length
), n => n.ToUpper()
);
12
Microsoft
.NET
Цепочки операторов
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());
var filtered = names .Where (n => n.Contains ("a"));
var sorted = filtered.OrderBy (n => n.Length);
мфк finalQuery = sorted .Select (n => n.ToUpper());
13
Microsoft
.NET
Цепочки операторов
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Take
(3);
// Jay, Mary, Harry
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = names
.Take
(3)
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length);
// Harry
14
Microsoft
.NET
Операторы
Некоторые операторы возвращают элемент, а не последовательность:
int[] numbers = { 10, 9, 8, 7, 6 };
int firstNumber = numbers.First();
int lastNumber = numbers.Last();
int secondNumber = numbers.ElementAt(1);
bool hasTheNumberNine = numbers.Contains(9);
//
//
//
//
10
6
9
true
15
Microsoft
.NET
Операторы
Некоторые операторы принимают две входных последовательности:
int[] seq1
int[] seq2
var concat
var union
=
=
=
=
{ 1, 2, 3 };
{ 3, 4, 5 };
seq1.Concat(seq2);
seq1.Union (seq2);
// { 1, 2, 3, 3, 4, 5 }
// { 1, 2, 3, 4, 5 }
16
Microsoft
.NET
Отложенное выполнение
Выражения выполняются в процессе прохождения
последовательности:
var numbers = new List<int>();
numbers.Add (1);
var query = numbers.Select (n => n * 10);
numbers.Add (2);
foreach (int n in query)
Console.WriteLine(n + " ");
// 10 20
17
Microsoft
.NET
Отложенное выполнение
Выражения выполняются в процессе прохождения
последовательности…
…кроме операторов, возвращающих элемент (First, Count, etc.) и
операторов преобразования (ToArray, ToList, ToDictionary, ToLookup).
var numbers = new List<int>();
numbers.Add (1);
var query = numbers.Count();
numbers.Add (2);
Console.WriteLine(query);
// 1; query – int!
18
Microsoft
.NET
Отложенное выполнение
Выражения выполняются заново в случае нового прохождения
последовательности:
var numbers = new List<int>() { 1, 2 };
var query = numbers.Select (n => n * 10);
foreach (int n in query)
Console.Write (n + " ");
numbers.Clear();
foreach (int n in query)
Console.Write (n + "|");
// 10 20
// 19
Microsoft
.NET
Отложенное выполнение
Выражения выполняются заново в случае нового прохождения
последовательности:
var numbers = new List<int>() { 1, 2 };
var query = numbers.Select (n => n * 10).ToList();
foreach (int n in query)
Console.Write (n + " ");
numbers.Clear();
foreach (int n in query)
Console.Write (n + "|");
// 10 20
// 10 20; query – List!
20
Microsoft
.NET
Отложенное выполнение
Выражения выполняются заново в случае нового прохождения
последовательности:
var numbers = new List<int>() { 1, 2 };
var query = numbers.Select (n => n * 10);
foreach (int n in query.ToList())
Console.Write (n + " ");
numbers.Clear();
foreach (int n in query.ToList())
Console.Write (n + "|");
// 10 20
// ?
21
Microsoft
.NET
Отложенное выполнение
При использовании в лямбда-выражении внешних переменных
принимается в расчет их значение на момент исполнения запроса.
var query = "abcdefghijklmnopqrstuvwxyz";
string vowels = "aeiou";
for (int i = 0; i < vowels.Length; i++)
query = query.Where (c => c != vowels[i]);
foreach (char c in query)
Console.Write (c);
// ?
22
Microsoft
.NET
Отложенное выполнение
При использовании в лямбда-выражении внешних переменных
принимается в расчет их значение на момент исполнения запроса.
var query = "abcdefghijklmnopqrstuvwxyz";
string vowels = "aeiou";
for (int i = 0; i < vowels.Length; i++)
query = query.Where (c => c != vowels[i]);
foreach (char c in query)
Console.Write (c);
// IndexOutOfRangeException
// i == 5
23
Microsoft
.NET
Отложенное выполнение
Объявление переменной внутри цикла создает 5 переменных,
значения которых будут захватываться при выполнении запроса.
var query = "abcdefghijklmnopqrstuvwxyz";
string vowels = "aeiou";
for (int i = 0; i < vowels.Length; i++) {
char vowel = vowels[i];
query = query.Where (c => c != vowel);
}
foreach (char c in query)
Console.Write (c);
// bcdfghklmnpqrstvwxyz
24
Microsoft
.NET
Декораторы
var lessThanTen = new int[] { 5, 12, 3 }.Where(n => n < 10);
25
Microsoft
.NET
Декораторы
var query = new int[] { 5, 12, 3 }.Where (n => n < 10)
.OrderBy (n => n)
.Select (n => n * 10);
26
Microsoft
.NET
Декораторы
{ 5, 12, 3 }
n < 10
n
n * 10
27
Microsoft
.NET
Подзапросы
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var outerQuery = names
.Where (n => n.Length == names.Min(n2 => n2.Length));
// Tom, Jay
28
Microsoft
.NET
Подзапросы
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var outerQuery = names
.Where (n => n.Length == names.Min(n2 => n2.Length));
var query1 = names.Min(n => n.Length);
var query2 = names.Where(n => n.Length = query1);
29
Microsoft
.NET
IQueryable
LINQ обладает двумя различными архитектурами:
IEnumerable – для локальных последовательностей на основе
декораторов, выражения компилируются в IL.
IQueryable – для внешних источников данных на основе деревьев
разбора, интерпретируемых во время исполнения запроса.
30
Microsoft
.NET
IQueryable
Провайдеры, реализующие IQueryable:
LINQ to Entities (Entity Framework)
LINQ to SQL
LINQ to DataSets
LINQ to XML
…
LINQ to Twitter
31
Microsoft
.NET
LINQ to SQL
SQL Server:
create table Customer
(
ID int not null primary key,
Name varchar(30)
)
insert Customer values (1, 'Tom')
insert Customer values (2, 'Dick')
insert Customer values (3, 'Harry')
insert Customer values (4, 'Mary')
insert Customer values (5, 'Jay')
32
Microsoft
.NET
LINQ to SQL
using
using
using
using
System;
System.Linq;
System.Data.Linq;
System.Data.Linq.Mapping;
[Table] public class Customer
{
[Column(IsPrimaryKey=true)] public int ID;
[Column] public string Name;
}
33
Microsoft
.NET
LINQ to SQL
var dataContext = new DataContext ("connection string");
var customers = dataContext.GetTable <Customer>();
IQueryable<string> query = customers
.Where (n => n.Name.Contains ("a"))
.OrderBy (n => n.Name.Length)
.Select (n => n.Name.ToUpper());
foreach (string name in query)
Console.WriteLine (name);
SELECT UPPER([t0].[Name]) AS [value]
FROM [Customer] AS [t0]
WHERE [t0].[Name] LIKE @p0
ORDER BY LEN([t0].[Name])
34
Microsoft
.NET
IQueryable
35
Microsoft
.NET
IQueryable
IEnumerable<string> q = customers
.Select (c => c.Name.ToUpper())
.OrderBy (n => n)
.Pair()
.Select ((n, i) => "Pair " + i.ToString() + " = " + n);
SELECT UPPER (Name)
FROM Customer
ORDER BY UPPER (Name)
36
Microsoft
.NET
AsEnumerable()
IEnumerable<string> q = customers
.Select (c => c.Name.ToUpper())
.AsEnumerable()
.OrderBy (n => n)
.Pair()
.Select ((n, i) => "Pair " + i.ToString() + " = " + n);
SELECT UPPER (Name)
FROM Customer
37
Microsoft
.NET
LINQ to SQL
[Table (Name="Customers")] public class Customer
{
[Column(IsPrimaryKey=true)] public int ID;
[Column] public string Name;
}
38
Microsoft
.NET
Entity Framework
[EdmEntityType
(NamespaceName = "NutshellModel", Name = "Customer")]
public partial class Customer
{
[EdmScalarPropertyAttribute
(EntityKeyProperty = true, IsNullable = false)]
public int ID { get; set; }
[EdmScalarProperty
(EntityKeyProperty = false, IsNullable = false)]
public string Name { get; set; }
}
39
Microsoft
.NET
Conceptual Model
<edmx:ConceptualModels>
<Schema …>
<EntityContainer Name="Model1Container"
annotation:LazyLoadingEnabled="true">
<EntitySet Name="Customers" EntityType="Model1.Customer" />
</EntityContainer>
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Int32" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Name="Name" Type="String" Nullable="false" />
</EntityType>
</Schema>
</edmx:ConceptualModels>
40
Microsoft
.NET
Storage Model
<edmx:StorageModels>
<Schema …>
<EntityContainer Name="Model1StoreContainer">
<EntitySet Name="Customers"
EntityType="Model1.Store.Customers" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="Customers">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>
</Schema>
</edmx:StorageModels>
41
Microsoft
.NET
Mapping
<EntityContainerMapping StorageEntityContainer="Model1StoreContainer"
CdmEntityContainer="Model1Container">
<EntitySetMapping Name="Customers">
<EntityTypeMapping TypeName="IsTypeOf(Model1.Customer)">
<MappingFragment StoreEntitySet="Customers">
<ScalarProperty Name="Id" ColumnName="Id" />
<ScalarProperty Name="Name" ColumnName="Name" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
42
Microsoft
.NET
Model1.edmx
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx">
<!-- EF Runtime content -->
<edmx:Runtime>
<!-- SSDL content -->
<edmx:StorageModels>
<Schema Namespace="Model1.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2012"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl">
<EntityContainer Name="Model1StoreContainer">
<EntitySet Name="Customers" EntityType="Model1.Store.Customers" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="Customers">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>
</Schema></edmx:StorageModels>
<!-- CSDL content -->
<edmx:ConceptualModels>
<Schema xmlns="http://schemas.microsoft.com/ado/2009/11/edm"
xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" Namespace="Model1"
Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
annotation:UseStrongSpatialTypes="false">
<EntityContainer Name="Model1Container" annotation:LazyLoadingEnabled="true">
<EntitySet Name="Customers" EntityType="Model1.Customer" />
</EntityContainer>
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Name="Name" Type="String" Nullable="false" />
</EntityType>
</Schema>
</edmx:ConceptualModels>
<!-- C-S mapping content -->
<edmx:Mappings>
<Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2009/11/mapping/cs">
<EntityContainerMapping StorageEntityContainer="Model1StoreContainer" CdmEntityContainer="Model1Container">
<EntitySetMapping Name="Customers">
<EntityTypeMapping TypeName="IsTypeOf(Model1.Customer)">
<MappingFragment StoreEntitySet="Customers">
<ScalarProperty Name="Id" ColumnName="Id" />
<ScalarProperty Name="Name" ColumnName="Name" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping></edmx:Mappings>
</edmx:Runtime>
<!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) -->
<edmx:Designer xmlns="http://schemas.microsoft.com/ado/2009/11/edmx">
<edmx:Connection>
<DesignerInfoPropertySet>
<DesignerProperty Name="MetadataArtifactProcessing" Value="EmbedInOutputAssembly" />
</DesignerInfoPropertySet>
</edmx:Connection>
<edmx:Options>
<DesignerInfoPropertySet>
<DesignerProperty Name="ValidateOnBuild" Value="true" />
<DesignerProperty Name="EnablePluralization" Value="True" />
<DesignerProperty Name="CodeGenerationStrategy" Value="None" />
<DesignerProperty Name="UseLegacyProvider" Value="False" />
</DesignerInfoPropertySet>
</edmx:Options>
<!-- Diagram content (shape and connector positions) -->
<edmx:Diagrams>
</edmx:Diagrams>
</edmx:Designer>
</edmx:Edmx>
43
Microsoft
.NET
LINQ to SQL vs. Entity Framework
var l2sContext = new DataContext ("database connection string");
Table<Customer> customers = context.GetTable <Customer>();
var efContext = new ObjectContext ("entity connection string");
ObjectSet<Customer> customers = context.CreateObjectSet<Customer>();
44
Microsoft
.NET
LINQ to SQL vs. Entity Framework
Customer cust = customers.OrderBy (c => c.Name).First();
cust.Name = "Updated Name";
context.SubmitChanges();
Customer cust = customers.OrderBy (c => c.Name).First();
cust.Name = "Updated Name";
context.SaveChanges();
45
Microsoft
.NET
LINQ to SQL vs. Entity Framework
class MyContext : DataContext // For LINQ to SQL
{
public Table<Customer> Customers
{
get { return GetTable<Customer>(); }
}
}
var l2sContext = new MyContext ("database connection string");
Console.WriteLine (context.Customers.Count());
46
Microsoft
.NET
LINQ to SQL vs. Entity Framework
/*
0, Ada
1, Zed
2, Jake
*/
var context = new MyContext ("connection string");
Customer a = context.Customers.OrderBy (c => c.Name).First();
Customer b = context.Customers.OrderBy (c => c.ID).First();
Console.WriteLine(a == b);
// true
47
Microsoft
.NET
LINQ to SQL vs. Entity Framework
create table Purchase
(
ID int not null primary key,
CustomerID int references Customer (ID),
Price decimal not null
)
[Association (Storage="_Customer", ThisKey="CustomerID",
IsForeignKey=true)]
public Customer Customer { get {...} set {...} }
[EdmRelationshipNavigationProperty ("MyModel", "FK..., "Customer")]
public Customer Customer { get {...} set {...} }
48
Download