linqAan de slag met linq


Opmerkingen

LINQ is een set functies geïntroduceerd in .NET Framework versie 3.5 die de kloof overbrugt tussen de wereld van objecten en de wereld van gegevens.

Traditioneel worden query's op gegevens uitgedrukt als eenvoudige tekenreeksen zonder typecontrole tijdens het compileren of ondersteuning voor IntelliSense. Bovendien moet u voor elk type gegevensbron een andere querytaal leren: SQL-databases, XML-documenten, verschillende webservices, enzovoort. LINQ maakt van een query een eersteklas taalconstructie in C # en Visual Basic. U schrijft query's op sterk getypeerde verzamelingen objecten met behulp van taalzoekwoorden en vertrouwde operatoren.

LINQ-methoden en ontelbaar versus IQueryable

LINQ-uitbreidingsmethoden op IEnumerable<T> nemen feitelijke methoden 1 , ongeacht of deze anonieme methoden zijn:

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

of benoemde methoden (methoden die expliciet als onderdeel van een klasse zijn gedefinieerd):

//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 theorie is het mogelijk om de IL van de methode te ontleden , erachter te komen wat de methode probeert te doen en de logica van die methode toe te passen op elke onderliggende gegevensbron, niet alleen objecten in het geheugen. Maar het ontleden van IL is niet voor bangeriken.


Gelukkig biedt .NET de IQueryable<T> -interface en de uitbreidingsmethoden op System.Linq.Queryable voor dit scenario. Deze uitbreidingsmethoden hebben een expressieboom - een gegevensstructuur die code vertegenwoordigt - in plaats van een daadwerkelijke methode, die de LINQ-provider vervolgens kan ontleden 2 en converteren naar een geschiktere vorm voor het doorzoeken van de onderliggende gegevensbron. Bijvoorbeeld:

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

Als (bijvoorbeeld) deze query betrekking heeft op een SQL-database, kan de provider deze expressie converteren naar de volgende SQL-instructie:

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

en voer het uit tegen de gegevensbron.

Aan de andere kant, als de query tegen een REST API is, kan de provider dezelfde expressie converteren naar een API-aanroep:

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

Het aanpassen van een gegevensverzoek op basis van een expressie heeft twee primaire voordelen (in tegenstelling tot het laden van de hele verzameling in het geheugen en lokaal opvragen):

  • De onderliggende gegevensbron kan vaak efficiënter zoeken. Er kan bijvoorbeeld een index op LastName . Als u de objecten in het lokale geheugen laadt en in het geheugen opvraagt, verliest u die efficiëntie.
  • De gegevens kunnen worden gevormd en verkleind voordat ze worden overgedragen. In dit geval hoeft de database / webservice alleen de overeenkomende gegevens te retourneren, in tegenstelling tot de volledige set personen die beschikbaar is in de gegevensbron.

Notes
1. Technisch gezien nemen ze niet echt methoden, maar delegeren ze eerder instanties die naar methoden wijzen . Dit onderscheid is hier echter niet relevant.
2. Dit is de reden voor fouten zoals " LINQ to Entities herkent de methode 'System.String ToString ()' niet en deze methode kan niet worden vertaald in een winkelexpressie. ". De LINQ-provider (in dit geval de Entity Framework-provider) weet niet hoe hij een aanroep naar ToString moet parseren en vertalen naar equivalente SQL.

Querysyntaxis en methodesyntaxis

Querysyntaxis en methodesyntaxis zijn semantisch identiek, maar veel mensen vinden querysyntaxis eenvoudiger en gemakkelijker te lezen. Laten we zeggen dat we alle even items die in oplopende volgorde zijn besteld moeten halen uit een verzameling getallen.

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)
 

Vergeet niet dat sommige vragen moeten worden uitgedrukt als methodeaanroepen. U moet bijvoorbeeld een methode-aanroep gebruiken om een query uit te drukken die het aantal elementen ophaalt dat overeenkomt met een opgegeven voorwaarde. U moet ook een methode-aanroep gebruiken voor een query die het element ophaalt met de maximale waarde in een bronreeks. Dus dat kan een voordeel zijn van het gebruik van methodesyntaxis om de code consistenter te maken. Natuurlijk kunt u de methode echter altijd toepassen na een syntaxis van de zoekopdracht:

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

Opstelling

LINQ vereist .NET 3.5 of hoger (of .NET 2.0 met LINQBridge ).

Voeg een referentie toe aan System.Core , als deze nog niet is toegevoegd.

Importeer boven aan het bestand de naamruimte:

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

De verschillende joins in LINQ

In de volgende voorbeelden gebruiken we de volgende voorbeelden:

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

BINNENKANTE

Zoekopdrachtsyntaxis

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

Methode Syntaxis

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

Resultaat:

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

LINKER BUITENKANTEL

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

Resultaat:

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

Resultaat:

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

GROEP VERBINDEN

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

De Propertie- Orders nu een IEnumerable<Order> met alle gekoppelde bestellingen.

Resultaat:

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

Hoe deel te nemen aan meerdere voorwaarden

Wanneer u aan één voorwaarde deelneemt, kunt u het volgende gebruiken:

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

Gebruik bij het deelnemen aan meerdere:

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

Zorg ervoor dat beide anonieme objecten dezelfde eigenschappen hebben en in VB.NET moeten ze als Key worden gemarkeerd, hoewel VB.NET meerdere Equals clausules toestaat, gescheiden door And :

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