Поиск…


замечания

LINQ - это набор функций, внедренных в .NET Framework версии 3.5, которая устраняет разрыв между миром объектов и миром данных.

Традиционно запросы к данным выражаются как простые строки без проверки типов во время компиляции или поддержки IntelliSense. Кроме того, вам необходимо изучить другой язык запросов для каждого типа источников данных: базы данных SQL, документы XML, различные веб-службы и т. Д. LINQ делает запрос конструкцией языка первого класса в C # и Visual Basic. Вы пишете запросы против строго типизированных коллекций объектов, используя ключевые слова языка и знакомые операторы.

Настроить

Для LINQ требуется .NET 3.5 или выше (или .NET 2.0 с использованием LINQBridge ).

Добавьте ссылку на 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,
  },
};

ВНУТРЕННЕЕ СОЕДИНЕНИЕ

Синтаксис запроса

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" }

ЛЕВЫЙ ВНЕШНИЙ ВСТУПИТЕЛЬ

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> со всеми связанными Орденами.

Результат:

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

Как присоединиться к нескольким условиям

При присоединении к одному условию вы можете использовать:

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 позволяет использовать несколько Equals пункты , разделенные And :

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

Методы расширения LINQ в IEnumerable<T> принимают фактические методы 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-метод метода , выяснить, что этот метод пытается сделать, и применить логику этого метода к любому базовому источнику данных, а не только к объектам в памяти. Но синтаксический анализ ИЛ не для слабонервных.


К счастью, .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

Существует два основных преимущества при настройке запроса данных на основе выражения (в отличие от загрузки всей коллекции в память и запросов локально):

  • Основной источник данных может часто запрашивать более эффективно. Например, вполне может быть указатель на LastName . Загрузка объектов в локальную память и запрос в памяти теряют эту эффективность.
  • Данные могут быть сформированы и уменьшены до их передачи. В этом случае базе данных / веб-службе необходимо вернуть только сопоставимые данные, а не весь набор лиц, доступных из источника данных.

Заметки
1. Технически они фактически не применяют методы, а скорее делегируют экземпляры, указывающие на методы . Однако это различие здесь не имеет значения.
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