Szukaj…


Uwagi

LINQ to zestaw funkcji wprowadzonych w .NET Framework w wersji 3.5, który wypełnia lukę między światem obiektów a światem danych.

Tradycyjnie zapytania dotyczące danych są wyrażane jako proste ciągi bez sprawdzania typu w czasie kompilacji lub obsługi IntelliSense. Ponadto musisz nauczyć się innego języka zapytań dla każdego typu źródła danych: baz danych SQL, dokumentów XML, różnych usług internetowych i tak dalej. LINQ sprawia, że zapytanie jest najwyższej klasy konstrukcją językową w języku C # i Visual Basic. Piszesz zapytania do silnie typowanych kolekcji obiektów, używając słów kluczowych w języku i znanych operatorów.

Ustawiać

LINQ wymaga .NET 3.5 lub nowszej wersji (lub .NET 2.0 przy użyciu LINQBridge ).

Dodaj odwołanie do System.Core , jeśli nie zostało jeszcze dodane.

Na górze pliku zaimportuj przestrzeń nazw:

  • DO#
  using System;
  using System.Linq;
  • VB.NET
  Imports System.Linq

Różne sprzężenia w LINQ

W poniższych przykładach użyjemy następujących próbek:

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,
  },
};

DOŁĄCZ DO WEWNĘTRZNEGO

Składnia zapytania

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();

Metoda Składnia

var joined = Products.Join(Orders, p => p.ProductId, 
                                   o => o.OrderId, 
                                     => new 
                                    { 
                                      OrderId   = o.OrderId, 
                                      ProductId = p.ProductId, 
                                      Name      = p.Name 
                                    })
                     .ToList();

Wynik:

{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" }

DOŁĄCZ DO LEWEGO ZEWNĘTRZNEGO

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();

Wynik:

{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" },
{ NULL, 3, "Book nr 3" }

KRZYŻ DOŁĄCZ

var joined = (from p in Products
              from o in Orders
              select new
              {
                o.OrderId,
                p.ProductId,
                p.Name
              }).ToList();

Wynik:

{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" },
{ NULL, 3, "Book nr 3" },
{ 4, NULL, NULL }

DOŁĄCZ DO GRUPY

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 Propertie zawierają teraz IEnumerable<Order> ze wszystkimi połączonymi zamówieniami.

Wynik:

{ 1, "Book nr 1", Orders = { 1, 2 } },
{ 2, "Book nr 2", Orders = { 3 } },
{ 3, "Book nr 3", Orders = { } },

Jak dołączyć na wielu warunkach

Dołączając do jednego warunku, możesz użyć:

join o in Orders 
  on p.ProductId equals o.ProductId

Łącząc się na wiele, użyj:

join o in Orders 
  on new { p.ProductId, p.CategoryId } equals new { o.ProductId, o.CategoryId }

Upewnij się, że oba obiekty anonimowe mają te same właściwości, aw VB.NET, muszą być oznaczone Key , chociaż VB.NET pozwala wielu Equals klauzule oddzielone And :

Join o In Orders 
  On p.ProductId Equals o.ProductId And p.CategoryId Equals o.CategoryId

Składnia zapytania i składnia metody

Składnia zapytań i składnia metod są semantycznie identyczne, ale wiele osób uważa, że składnia zapytań jest prostsza i łatwiejsza do odczytania. Powiedzmy, że musimy pobrać wszystkie parzyste przedmioty uporządkowane rosnąco ze zbioru liczb.

DO#:

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)

Pamiętaj, że niektóre zapytania muszą być wyrażone jako wywołania metod. Na przykład musisz użyć wywołania metody, aby wyrazić zapytanie, które pobiera liczbę elementów pasujących do określonego warunku. Należy również użyć wywołania metody dla zapytania, które pobiera element o maksymalnej wartości w sekwencji źródłowej. Może to być zaletą użycia składni metody w celu zwiększenia spójności kodu. Oczywiście można zawsze zastosować tę metodę po wywołaniu składni zapytania:

DO#:

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();

Metody LINQ i IEnumerable vs IQueryable

Metody rozszerzenia LINQ w IEnumerable<T> pobierają rzeczywiste metody 1 , czy metody anonimowe:

//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);

lub nazwane metody (metody jawnie zdefiniowane jako część klasy):

//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

Teoretycznie można przeanalizować IL metody , dowiedzieć się, co ta metoda próbuje zrobić, i zastosować logikę tej metody do dowolnego źródła danych, nie tylko obiektów w pamięci. Ale analizowanie IL nie jest dla osób o słabym sercu.


Na szczęście .NET zapewnia interfejs IQueryable<T> oraz metody rozszerzenia w System.Linq.Queryable dla tego scenariusza. Te metody rozszerzeń przyjmują drzewo wyrażeń - strukturę danych reprezentującą kod - zamiast faktycznej metody, którą dostawca LINQ może następnie przeanalizować 2 i przekonwertować na bardziej odpowiednią formę do wysyłania zapytań do źródła danych. Na przykład:

//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)

Jeśli (na przykład) to zapytanie dotyczy bazy danych SQL, dostawca może przekonwertować to wyrażenie na następującą instrukcję SQL:

SELECT *
FROM Persons
WHERE LastName LIKE N'A%'

i uruchom go w stosunku do źródła danych.

Z drugiej strony, jeśli zapytanie dotyczy interfejsu API REST, dostawca może przekonwertować to samo wyrażenie na wywołanie interfejsu API:

http://www.example.com/person?filtervalue=A&filtertype=startswith&fieldname=lastname

Istnieją dwie podstawowe zalety dostosowywania żądania danych na podstawie wyrażenia (w przeciwieństwie do ładowania całej kolekcji do pamięci i lokalnego wysyłania zapytań):

  • Podstawowe źródło danych często może wydajniej wysyłać zapytania. Na przykład bardzo dobrze może istnieć indeks LastName . Ładowanie obiektów do pamięci lokalnej i wyszukiwanie w pamięci traci tę wydajność.
  • Dane można kształtować i zmniejszać przed przesłaniem. W takim przypadku baza danych / usługa internetowa musi tylko zwrócić pasujące dane, w przeciwieństwie do całego zestawu Osób dostępnych ze źródła danych.

Notatki
1. Technicznie rzecz biorąc, nie przyjmują metod, ale raczej przekazują instancje wskazujące na metody . Jednak to rozróżnienie nie ma tu znaczenia.
2. To jest przyczyna błędów takich jakLINQ to Entities nie rozpoznaje metody„ System.String ToString () ”i tej metody nie można przetłumaczyć na wyrażenie sklepu. ”. Dostawca LINQ (w tym przypadku dostawca Entity Framework) nie wie, jak analizować i tłumaczyć wywołanie ToString na równoważny SQL.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow