linq チュートリアル
linqを使い始める
サーチ…
備考
LINQは、オブジェクトの世界とデータの世界とのギャップを埋める.NET Frameworkバージョン3.5で導入された一連の機能です。
従来、データに対するクエリは、コンパイル時またはIntelliSenseサポートでタイプチェックを行わない単純な文字列として表現されていました。さらに、SQLデータベース、XMLドキュメント、さまざまなWebサービスなど、データソースの種類ごとに異なるクエリ言語を学習する必要があります。 LINQは、クエリをC#およびVisual Basicでファーストクラスの言語構成にします。言語キーワードと慣れ親しんだ演算子を使用して、強く型付けされたオブジェクトのコレクションに対してクエリを作成します。
セットアップ
LINQには.NET 3.5以上(またはLINQBridgeを使用した.NET 2.0)が必要です。
まだ追加されていない場合は、 System.Coreへの参照を追加します。
ファイルの先頭に、名前空間をインポートします。
- C#
using System;
using System.Linq;
- VB.NET
Imports System.Linq
LINQの異なる結合
次の例では、次のサンプルを使用します。
List<Product> Products = new List<Product>()
{
new Product()
{
ProductId = 1,
Name = "Book nr 1",
Price = 25
},
new Product()
{
ProductId = 2,
Name = "Book nr 2",
Price = 15
},
new Product()
{
ProductId = 3,
Name = "Book nr 3",
Price = 20
},
};
List<Order> Orders = new List<Order>()
{
new Order()
{
OrderId = 1,
ProductId = 1,
},
new Order()
{
OrderId = 2,
ProductId = 1,
},
new Order()
{
OrderId = 3,
ProductId = 2,
},
new Order()
{
OrderId = 4,
ProductId = NULL,
},
};
INNER JOIN
クエリの構文
var joined = (from p in Products
join o in Orders on p.ProductId equals o.ProductId
select new
{
o.OrderId,
p.ProductId,
p.Name
}).ToList();
メソッドの構文
var joined = Products.Join(Orders, p => p.ProductId,
o => o.OrderId,
=> new
{
OrderId = o.OrderId,
ProductId = p.ProductId,
Name = p.Name
})
.ToList();
結果:
{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" }
LEFT OUTER JOIN
var joined = (from p in Products
join o in Orders on p.ProductId equals o.ProductId into g
from lj in g.DefaultIfEmpty()
select new
{
//For the empty records in lj, OrderId would be NULL
OrderId = (int?)lj.OrderId,
p.ProductId,
p.Name
}).ToList();
結果:
{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" },
{ NULL, 3, "Book nr 3" }
クロスジョイン
var joined = (from p in Products
from o in Orders
select new
{
o.OrderId,
p.ProductId,
p.Name
}).ToList();
結果:
{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" },
{ NULL, 3, "Book nr 3" },
{ 4, NULL, NULL }
グループ・ジョイン
var joined = (from p in Products
join o in Orders on p.ProductId equals o.ProductId
into t
select new
{
p.ProductId,
p.Name,
Orders = t
}).ToList();
プロパティーOrders
は、リンクされたすべてのIEnumerable<Order>
含むIEnumerable<Order>
というIEnumerable<Order>
が含まれています。
結果:
{ 1, "Book nr 1", Orders = { 1, 2 } },
{ 2, "Book nr 2", Orders = { 3 } },
{ 3, "Book nr 3", Orders = { } },
複数の条件に参加する方法
1つの条件で結合する場合は、次のものを使用できます。
join o in Orders
on p.ProductId equals o.ProductId
複数に参加するときは、
join o in Orders
on new { p.ProductId, p.CategoryId } equals new { o.ProductId, o.CategoryId }
両方の匿名オブジェクトが同じプロパティを持っていることを確認し、VB.NETではKey
とマークする必要がありますが、VB.NETではAnd
区切られた複数のEquals
句を使用できます。
Join o In Orders
On p.ProductId Equals o.ProductId And p.CategoryId Equals o.CategoryId
クエリ構文とメソッド構文
クエリー構文とメソッド構文は意味的には同じですが、多くの人がクエリー構文をより簡単で簡単に見つけることができます。たとえば、数字の集合から昇順に並べられた偶数項目をすべて取得する必要があるとします。
C#:
int[] numbers = { 0, 1, 2, 3, 4, 5, 6 };
// Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
// Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
VB.NET:
Dim numbers() As Integer = { 0, 1, 2, 3, 4, 5, 6 }
' Query syntax: '
Dim numQuery1 = From num In numbers
Where num Mod 2 = 0
Select num
Order By num
' Method syntax: '
Dim numQuery2 = numbers.where(Function(num) num Mod 2 = 0).OrderBy(Function(num) num)
クエリの中には、メソッド呼び出しとして表現する必要があることを忘れないでください。たとえば、指定された条件に一致する要素の数を取得するクエリを表すには、メソッド呼び出しを使用する必要があります。また、ソースシーケンス内の最大値を持つ要素を取得するクエリに対してメソッド呼び出しを使用する必要があります。したがって、メソッド構文を使用してコードをより一貫性のあるものにする利点があります。ただし、もちろん、クエリ構文呼び出しの後にメソッドを適用することもできます。
C#:
int maxNum =
(from num in numbers
where num % 2 == 0
select num).Max();
VB.NET:
Dim maxNum =
(From num In numbers
Where num Mod 2 = 0
Select num).Max();
LINQメソッド、およびIEnumerable 対IQueryable
IEnumerable<T>
LINQ拡張メソッドは、匿名メソッドかどうかにかかわらず、実際のメソッドを1つ取る:
//C#
Func<int,bool> fn = x => x > 3;
var list = new List<int>() {1,2,3,4,5,6};
var query = list.Where(fn);
'VB.NET
Dim fn = Function(x As Integer) x > 3
Dim list = New List From {1,2,3,4,5,6};
Dim query = list.Where(fn);
または名前付きメソッド(クラスの一部として明示的に定義されたメソッド)
//C#
class Program {
bool LessThan4(int x) {
return x < 4;
}
void Main() {
var list = new List<int>() {1,2,3,4,5,6};
var query = list.Where(LessThan4);
}
}
'VB.NET
Class Program
Function LessThan4(x As Integer) As Boolean
Return x < 4
End Function
Sub Main
Dim list = New List From {1,2,3,4,5,6};
Dim query = list.Where(AddressOf LessThan4)
End Sub
End Class
理論的には、メソッドのILを解析し、そのメソッドが何をしようとしているのかを把握し、メモリ内のオブジェクトだけでなく、そのメソッドのロジックを基になるデータソースに適用することが可能です。しかし、ILをパースすることは、かすかな心のためではありません。
幸いにも、.NETはこのシナリオではIQueryable<T>
インターフェイスとSystem.Linq.Queryable
の拡張メソッドを提供します。これらの拡張メソッドは、実際のメソッドではなく、式ツリー(コードを表すデータ構造)を使用して、LINQプロバイダが2を解析し、基になるデータソースを照会するためのより適切な形式に変換します。例えば:
//C#
IQueryable<Person> qry = PersonsSet();
// Since we're using a variable of type Expression<Func<Person,bool>>, the compiler
// generates an expression tree representing this code
Expression<Func<Person,bool>> expr = x => x.LastName.StartsWith("A");
// The same thing happens when we write the lambda expression directly in the call to
// Queryable.Where
qry = qry.Where(expr);
'VB.NET
Dim qry As IQueryable(Of Person) = PersonSet()
' Since we're using a variable of type Expression(Of Func(Of Person,Boolean)), the compiler
' generates an expression tree representing this code
Dim expr As Expression(Of Func(Of Person, Boolean)) = Function(x) x.LastName.StartsWith("A")
' The same thing happens when we write the lambda expression directly in the call to
' Queryable.Where
qry = qry.Where(expr)
たとえば、このクエリがSQLデータベースに対して行われている場合、プロバイダはこの式を次のSQL文に変換できます。
SELECT *
FROM Persons
WHERE LastName LIKE N'A%'
それをデータソースに対して実行します。
一方、問合せがREST APIに対して実行されている場合、プロバイダは同じ式をAPIコールに変換できます。
http://www.example.com/person?filtervalue=A&filtertype=startswith&fieldname=lastname
式に基づいてデータ要求を調整することには、(コレクション全体をメモリにロードしてローカルにクエリするのではなく)2つの主な利点があります。
- 基礎となるデータソースは、しばしばより効率的にクエリできます。たとえば、
LastName
インデックスがあります。オブジェクトをローカルメモリにロードし、メモリ内でクエリするとその効率が失われます。 - データを転送する前に、データを整形して縮小することができます。この場合、データベース/ Webサービスは、データソースから利用可能なPersonのセット全体とは対照的に、一致するデータを返すだけでよい。
ノート
技術的には、実際にはメソッドを取るのではなく、メソッドを指すインスタンスを委譲します 。しかし、この区別はここでは無関係です。
2.これは、 " LINQ to Entitiesがメソッド 'System.String ToString()'メソッドを認識せず、このメソッドをストア式に変換できない ような エラーが発生する理由です。 " LINQプロバイダ(この場合はEntity Frameworkプロバイダ)は、 ToString
への呼び出しを解析して同等のSQLに変換する方法を知らない。