65.9K
CodeProject 正在变化。 阅读更多。
Home

Linq DLinq XLinq PLinq 一站式服务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (24投票s)

2010 年 8 月 27 日

CPOL

8分钟阅读

viewsIcon

44071

downloadIcon

1132

面向初学者的 LINQ 概述。

引言

是的,我知道网上有很多关于这个主题的文章,CodeProject 本身也有很多。然而,我发现缺少的是一个地方,新手可以在那里获得所有关于 LINQ 的相关信息。之后,他们可以深入研究自己感兴趣的主题。

通过本文,我将分享关于 .NET 这一突出功能的信息。我希望能够让新手能够以 LINQ 的方式编写代码,如果他们还没有这样做的话。同时,我也希望能够让他们开始朝同一个方向思考解决方案。当然,本文并不提供关于该主题的所有细节。本文为您提供了 LINQ 的入门和总体理解。

什么是LINQ

LINQ 是 Language Integrated Query 的缩写。它依赖于语言改进(C# 3.0 和 VB 9.0 及更高版本)。

这是微软对 LINQ 的说法:

“LINQ 是一组对 .NET Framework 的扩展,包括语言集成查询、集合和转换操作。它通过对查询的原生语言语法进行扩展,从而扩展了 C# 和 Visual Basic,并提供类库来利用这些功能。” 简单来说,LINQ 是一种你可以用来编写符合你需求并对各种数据存储(包括远程数据存储,如 SQL 等)进行操作的查询。

为什么使用 LINQ?

在一般的编程场景中,我们查询各种类型的数据存储,例如,对象集合、数据库和 XML 等。为了查询这些数据存储,我们必须使用最适合它们的不同机制。例如,我们使用 foreach 循环来查询集合,为数据库编写 SQL 查询,并为查询 XML 数据编写 XPath 查询。

所以,这里的关键是我们必须了解用于处理不同数据存储的相似任务的个体术语。为了更清楚地说明我的观点,请考虑一个场景,我们需要从特定区域(例如印度)查找客户。

使用对象进行查询

foreach(Customer c in customers) 
if (c.Region == “India") … 

从数据库表进行查询

SELECT * FROM Customers WHERE Region='India'

使用 xPath 查询 XML

//Customers/Customer[@Region=‘India'] 

LINQ 在这里试图做的是,它为你提供了 **统一的方法** 来查询和操作不同的数据源。因此,为了查询数据,你不需要学习 XPath、SQL 和对象迭代。你只需要知道如何使用 LINQ 进行查询。一旦你开始在日常应用程序中使用它,你就会体会到它的真正威力。

此外,LINQ 还带来了一些好处,如下所示:

  • 弥合了面向对象和关系数据之间的差距
  • 直接集成到语言中
  • 类型检查和编译时安全
  • 智能感知。
  • 调试器支持

示例 LINQ 查询及其剖析

一个简单的 Linq 查询看起来如下所示:

var contacts = from c in customers
where c.Region == "India"
select new {c.Name, c.Phone}

这个小的代码片段包含 3.0 框架及更高版本支持的几项语言功能,这些功能在下面的插图中进行了识别。这两个查询是相同的,只是表示法不同。

sampleq.PNG

以下是简单 LINQ 查询中出现的术语:

1) 标准查询表达式

这些是语言语法,用于帮助你编写 .NET Framework 可以理解的 LINQ 查询。

qexp.png

2) 本地变量类型推断

这允许你使用 var 而不是显式指定类型。Linq 查询通常返回 IEnumerable<T>。在某些情况下,使用匿名类型很方便。需要注意的是,变量从初始化器推断出变量类型。var 的作用域始终是本地的。

int i = 666;
string s = "Goodbye";
double d = 3.14;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();

可以被以下代码替换:

var i = 666;
var s = "Goodbye";
var d = 3.14;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();

3) 对象初始化器

这允许你通过提供属性值来在一行中创建对象的实例。

public class Point
{
    private int x, y;
    public int X { get { return x; } set { x = value; } }
    public int Y { get { return y; } set { y = value; } }
}

下面的行使用提供的参数创建了 Point 对象的实例。

Point a = new Point { X = 0, Y = 1 };

4) 匿名类型

通常我们使用名称创建类型。此功能允许你创建没有名称的类型。

 var i1 = new {CustomerId = 123, Name = “santosh” };
 var i2 = new {123, “santosh”};

5) Lambda 表达式

定义
Lambda 表达式是一个匿名函数,可以包含表达式和语句。

让我们看看 Lambda 表达式是如何演变的。假设我们想获取 City 为“London”的客户。为此,我们可以使用我们一个老朋友——委托。

  1. 使用 **委托** 获取客户
    List<Customer> customers = GetCustomers();
    var local = customers.FindAll(CityEqualsLondon);
    private bool CityEqualsLondon(Customer c){
                return c.City == "London";
    }
  2. 假设你不想创建一个函数,例如 CityEqualsLondon() 来进行过滤,你可以使用 **匿名方法** 来代替。它只是内联的,你就能得到所需的结果。
    List<Customer> customers = GetCustomers();
    var local = customers.FindAll(delegate(Customer c) { return c.City == "London"; });
  3. 为什么不使用一些东西来完成所需的工作,而无需编写编译器可以从代码中推断出的通用语法呢?解决方案是 **Lambda 表达式**。
    List<Customer> customers = GetCustomers();
    var local = customers.FindAll(c => c.City == "London");

因此,c => c.city == "London" 是一个 Lambda 表达式,它包含将所有所需信息传递给编译器,以便编译器知道该怎么做。“=>”被读作“goes to”。

在使用 LINQ 或 Lambda 表达式时,最好了解以下两个术语:

谓词

(p) => p.Gender == “F” 
//All persons, p, such that person’s Gender is “F" 

Projection

(p) => p.Gender ? “F” : “Female” 
//“Each person p becomes string “Female” if Gender is “F 

6) 扩展方法

此功能允许你在不创建派生类型的情况下将方法添加到现有类型。
以下是解释扩展方法的条目:

  • 扩展现有类型
  • 无需派生即可添加方法
  • 访问扩展类型的公共成员
  • 必须是
    • publicstatic
    • 驻留在 static 类中
  • 在扩展类型的参数前使用此关键字

以下代码用于创建一个名为 ToMsgbox 的示例扩展方法,它扩展了 strings 数组,使得 strings 与换行符连接并在消息框中显示。这不是一个很好的例子,但足以解释其基本思想。

 public static class MyExtns
    {
        public static void ToMsgbox(this string[] strings)
        {
            MessageBox.Show(string.Join(Environment.NewLine, strings));
        }
    }

LINQ 架构

下图解释了 LINQ 的基本架构。你用你选择的语言编写 LINQ 查询,这些查询在启用了 LINQ 的数据源上运行。LINQ 主要有三个逻辑分支:LINQ to Object、LINQ to Database and Entities(称为 DLINQ)和 LINQ to XML(称为 XLINQ)。

arch.PNG

LINQ to Objects

LINQ to Objects 示例

string[] testStrings = { "Santosh", "Vikram", "Mohit", "Chirag", 
"Amit", "Vikas", "Vipul", "Andy" };
            
            var temp = from str in testStrings
                       let words = str.ToLower().ToCharArray()
                       where str.Length > 4 && words[0] == 'v'
                       orderby str.Length descending
                       select str;
            temp.ToArray().ToMsg();

LINQ to SQL/Entities (DLINQ)

当你使用 LINQ 查询数据库和实体时,这被称为 DLINQ。Visual Studio 在集成设计器中提供了进行 ORM 映射的选项。你可以从 ScottGu 的博客 此处 查看更多详细信息。

关于 DLINQ 的几点说明:

  • ORM 设计器将关系结构映射到类
  • 将查询处理从内存推送到远程数据存储
  • 自动生成参数化 SQL
  • 完全支持存储过程
  • 表具有 CRUD 定义(默认 = 使用运行时)
    • 可以改为指向存储过程
  • 基于延迟加载的概念构建

下图解释了 LINQ to SQL 的工作原理

linq2sql.PNG

以下是延迟执行的代码片段示例。你看到,实际查询直到执行 DataBind() 时才触发到数据库。因此,实际查询的执行会一直延迟,直到没有其他选择。大多数查询运算符在声明时不会执行。

//Define query 
var query = from c in ctx.Customers 
where c.Region == “India“ 
select c; 

//Execute query 
gridViewCustomers.DataSource = query; 
gridViewCustomers.DataBind(); //Execute here 

除此之外,

  • 查询在枚举时执行
  • 绑定到 DataBound 控件
  • ForEach 循环迭代中执行
  • 转换为集合 ToList()ToArray()

延迟执行

  • 优点
    • 仅在需要时获取数据,而不是一次性获取所有数据
    • 最小化网络流量、内存消耗和数据库负载
    • 非常适合在用户按需获取对象时使用
  • 缺点
    • 多话的界面:每次请求子记录都会调用显式数据库查询

即时执行

  • 优点
    • 单个查询返回所有数据
    • 如果你知道你需要所有返回的数据,那么这非常有用
  • 缺点
    • 返回大量数据,无论是否使用

ORM 设计器看起来是这样的

nwind.PNG

这里有一些 DLINQ 查询示例

选择产品

var products = from p in _NWindDataContext.Products
where p.CategoryID == 1
select new { p.ProductName, p.CategoryID };
dataGridView1.DataSource = products; 

插入产品

Product product = new Product();
product.CategoryID = 1;
product.ProductName = "testProduct";
_NWindDataContext.Products.InsertOnSubmit(product);
_NWindDataContext.SubmitChanges();
RefreshData();

更新产品

var product = (from p in _NWindDataContext.Products
where p.CategoryID == 1 && p.ProductName.Contains("testProduct")
select p).First();
product.ProductName = "santosh";
_NWindDataContext.SubmitChanges();
RefreshData();

删除产品

var product = (from p in _NWindDataContext.Products
     where p.CategoryID == 1 && p.ProductName.Contains("santosh")
     select p).First();
_NWindDataContext.Products.DeleteOnSubmit(product);
_NWindDataContext.SubmitChanges();
RefreshData();

LINQ to XML (XLINQ)

简单来说,当你编写对 XML 进行操作的 LINQ 查询时,它们被称为 XLINQ 查询。

 XDocument xdoc = XDocument.Load("auths.xml");
 var auths = from a in xdoc.Root.Descendants("author")
                        where a.Element("au_fname").Value.Contains("a")
                        orderby a.Element("au_fname").Value.Length descending
                        select a.Element("au_fname").Value;

表达式树

表达式树以树状数据结构表示代码,其中每个节点都是一个表达式。你可以创建 Lambda 表达式的表达式树,这在使用 LINQ 进行查询时非常有用,并且有许多条件过滤器(where 子句)你想应用于查询。

public delegate bool Predicate<T>(T item);

Expression<Predicate<Customer>> test = c => c.Region== "India";

ParameterExpression c = Expression.Parameter(typeof(Customer), "c");
Expression expr = Expression.Equal(
        Expression.Property(c, typeof(Customer).GetProperty("Region")),
        Expression.Constant("India")
    );

Expression<Predicate<Customer>> test =
    Expression.Lambda<Predicate<Customer>>(expr, c);

并行 LINQ - (PLINQ)

Framework 4.0 引入了并行处理。LINQ 也因此受益,因为它允许 LINQ 查询并行运行。查询如何并行执行是一个复杂的过程。我不想让你负担更多关于这方面的内容。如果你想了解更多详细信息,请查看本文末尾的链接。

plinq.PNG

从编程的角度来看,如果你想知道如何编写利用此功能的 LINQ 查询,请参考以下代码片段并注意 AsParallel()

//LINQ to Object Query
int[] output = arr
    .Select(x => Test(x))
    .ToArray();

//PLinq Query
int[] output = arr.AsParallel()
    .Select(x => Test(x))
    .ToArray(); 

参考文献

© . All rights reserved.