linqlinq入门


备注

LINQ是.NET Framework 3.5版中引入的一组功能,它弥合了对象世界与数据世界之间的差距。

传统上,对数据的查询表示为简单字符串,无需在编译时进行类型检查或IntelliSense支持。此外,您必须为每种类型的数据源学习不同的查询语言:SQL数据库,XML文档,各种Web服务等。 LINQ使查询成为C#和Visual Basic中的第一类语言构造。您可以使用语言关键字和熟悉的运算符来编写针对强类型对象集合的查询。

LINQ方法和IEnumerable vs 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
 

基于表达式定制数据请求有两个主要好处(而不是将整个集合加载到内存中并在本地查询):

  • 底层数据源通常可以更有效地查询。例如, LastName 上可能有一个索引。将对象加载到本地内存并查询内存会失去效率。
  • 数据可以在传输之前进行整形和缩小。在这种情况下,数据库/ Web服务只需要返回匹配的数据,而不是从数据源中获得的整个人员集。

笔记
1.从技术上讲,它们实际上并不采用方法,而是委托指向方法的实例 。但是,这种区别在这里无关紧要。
2.这就是“ LINQ to Entities无法识别方法'System.String ToString()'方法 错误的原因,并且此方法无法转换为商店表达式。 ” LINQ提供程序(在本例中为Entity Framework提供程序)不知道如何解析并将对ToString 的调用转换为等效的SQL。

查询语法和方法语法

查询语法和方法语法在语义上是相同的,但许多人发现查询语法更简单,更易于阅读。假设我们需要从一组数字中检索按升序排序的所有偶数项。

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需要.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,
  },
};
 

内部联接

查询语法

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 }
 

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

属性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