サーチ…


備考

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に変換する方法を知らない。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow