Ricerca…


Osservazioni

LINQ è un insieme di funzionalità introdotte in .NET Framework versione 3.5 che colma il divario tra il mondo degli oggetti e il mondo dei dati.

Tradizionalmente, le query sui dati sono espresse come semplici stringhe senza controllo del tipo in fase di compilazione o supporto IntelliSense. Inoltre, è necessario apprendere un linguaggio di query diverso per ciascun tipo di origine dati: database SQL, documenti XML, vari servizi Web e così via. LINQ crea una query in un costrutto linguistico di prima classe in C # e Visual Basic. Si scrivono query contro raccolte di oggetti fortemente tipizzate utilizzando parole chiave della lingua e operatori familiari.

Impostare

LINQ richiede .NET 3.5 o versione successiva (oppure .NET 2.0 con LINQBridge ).

Aggiungi un riferimento a System.Core , se non è stato ancora aggiunto.

Nella parte superiore del file, importa lo spazio dei nomi:

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

I diversi join in LINQ

Nei seguenti esempi, utilizzeremo i seguenti esempi:

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

Sintassi delle query

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

Sintassi del metodo

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

Risultato:

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

SINISTRA ESTERNO

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

Risultato:

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

CROSS JOIN

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

Risultato:

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

GROUP JOIN

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

Gli Orders Propertie ora contengono un oggetto IEnumerable<Order> con tutti gli ordini collegati.

Risultato:

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

Come aderire a più condizioni

Quando ti unisci a una singola condizione, puoi usare:

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

Quando ti unisci a più persone, usa:

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

Assicurati che entrambi gli oggetti anonimi abbiano le stesse proprietà, e in VB.NET, devono essere contrassegnati con Key , anche se VB.NET consente più clausole Equals separate da And :

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

Sintassi delle query e sintassi del metodo

La sintassi delle query e la sintassi del metodo sono semanticamente identiche, ma molte persone trovano la sintassi delle query più semplice e più facile da leggere. Diciamo che dobbiamo recuperare tutti gli oggetti pari ordinati in ordine crescente da una serie di numeri.

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)

Ricorda che alcune query devono essere espresse come chiamate di metodo. Ad esempio, è necessario utilizzare una chiamata al metodo per esprimere una query che recupera il numero di elementi che corrispondono a una condizione specificata. È inoltre necessario utilizzare una chiamata al metodo per una query che recupera l'elemento che ha il valore massimo in una sequenza di origine. Quindi questo potrebbe essere un vantaggio dell'uso della sintassi del metodo per rendere il codice più coerente. Tuttavia, ovviamente è sempre possibile applicare il metodo dopo una chiamata di sintassi di query:

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

Metodi LINQ e IEnumerable vs IQueryable

I metodi di estensione LINQ su IEnumerable<T> prendono i metodi attuali 1 , sia che si tratti di metodi anonimi:

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

o metodi con nome (metodi definiti esplicitamente come parte di una classe):

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

In teoria, è possibile analizzare l'IL del metodo , capire che cosa il metodo sta tentando di fare e applicare la logica di quel metodo a qualsiasi origine dati sottostante, non solo oggetti in memoria. Ma analizzare IL non è per i deboli di cuore.


Fortunatamente, .NET fornisce l' IQueryable<T> e i metodi di estensione a System.Linq.Queryable , per questo scenario. Questi metodi di estensione prendono un albero di espressioni - una struttura dati che rappresenta il codice - invece di un metodo effettivo, che il provider LINQ può quindi analizzare 2 e convertire in un modulo più appropriato per interrogare l'origine dati sottostante. Per esempio:

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

Se (ad esempio) questa query è su un database SQL, il provider potrebbe convertire questa espressione nella seguente istruzione SQL:

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

ed eseguirlo contro la fonte dei dati.

D'altra parte, se la query è contro un'API REST, il provider può convertire la stessa espressione in una chiamata API:

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

Ci sono due principali vantaggi nel personalizzare una richiesta di dati basata su un'espressione (anziché caricare l'intera raccolta in memoria e interrogare localmente):

  • L'origine dati sottostante può spesso eseguire query in modo più efficiente. Ad esempio, potrebbe esserci un indice su LastName . Caricare gli oggetti nella memoria locale e interrogare in memoria perde quell'efficienza.
  • I dati possono essere modellati e ridotti prima di essere trasferiti. In questo caso, il database / servizio web deve solo restituire i dati corrispondenti, in contrapposizione all'intero insieme di Persone disponibili dall'origine dati.

Gli appunti
1. Tecnicamente, in realtà non prendono metodi, ma delegano le istanze che puntano a metodi . Tuttavia, questa distinzione è irrilevante qui.
2. Questo è il motivo per cui errori come " LINQ to Entities" non riconosce il metodo "System.String ToString ()" e questo metodo non può essere tradotto in un'espressione di archivio. ". Il provider LINQ (in questo caso il provider Entity Framework) non sa come analizzare e tradurre una chiamata a ToString in SQL equivalente.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow