01-第一个Linq例子和理解结构化查询

LINQ(语言集成查询)

官方文档:Language Integrated Query (LINQ) – C# | Microsoft Learn LINQ全称Language‑Integrated Query,中文:语言集成查询。 .NET版本:C#3.0 / .NET 3.5 开始内置,无需额外安装。需要命名空间 using System.Linq;,新版.NET支持隐式全局using,老版本需要手动添加。

传统方式查询数据需要写字符串形式SQL,编译不做类型检查、没有智能提示;不同数据源(数据库、XML、数组)要学不同查询语法。LINQ把查询变成C#语言一等语法构造,数组、List、XML、数据库可以使用一套查询写法

LINQ主要分支

  1. LINQ to Objects:操作内存集合(数组、List、IEnumerable)
  2. LINQ to XML:操作XML文档(需要引用System.Xml.Linq
  3. LINQ to DataSet:操作DataSet数据集
  4. LINQ to SQL:操作SQL Server数据库(老技术)
  5. LINQ to Entity(EF / EF Core):ORM框架,把LINQ翻译为SQL访问数据库

LINQ中把可迭代的对象集合叫做序列(sequence),绝大多数序列实现IEnumerable<T>接口;针对数据库远程查询则是IQueryable<T>,会生成表达式树,延迟翻译成SQL执行。

延迟执行(重要):定义LINQ查询表达式的时候不会执行查询;只有遍历(foreach)、调用FirstOrDefault()Count()这类方法时才真正执行查询。


示例1:数组查询(LINQ to Objects)

需求:字符串数组,筛选以.cn结尾的域名

using System;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        string[] siteNames = { "www.foxdevelop.cn", "www.foxdevelop.com", "www.foxdevelop.net", "www.baidu.cn" };

        // LINQ查询语法:定义查询,此时不执行
        var realSite =
            from s in siteNames        // from:遍历数据源,每个元素放入变量s
            where s.EndsWith(".cn")    // where:过滤条件
            select s;                  // select:输出结果

        // FirstOrDefault() 获取第一条,没有返回null;First()没有数据直接抛异常
        Console.WriteLine(realSite.FirstOrDefault());
        // LastOrDefault() 获取最后一条匹配
        Console.WriteLine(realSite.LastOrDefault());

        // foreach触发查询执行,遍历全部结果
        foreach (var item in realSite)
        {
            Console.WriteLine(item);
        }
        Console.ReadKey();
    }
}
Code language: JavaScript (javascript)

输出:

www.foxdevelop.com
www.baidu.cn
www.foxdevelop.com
www.baidu.cn
Code language: CSS (css)

语法理解(类比SQL)

from s in siteNames   // FROM siteNames 把每一个元素给到s变量
where s.EndsWith(".cn") // WHERE 条件过滤
select s;               // SELECT 返回结果
Code language: JavaScript (javascript)

提示:

  • First():取第一条,无数据抛异常
  • FirstOrDefault():取第一条,无数据返回默认值(引用类型返回null)
  • Last() / LastOrDefault()同理取最后一条

LINQ有两套写法:查询语法(from‑where‑select)方法语法(链式调用Where()/Select()),编译器会把查询语法编译成标准查询方法调用,两者语义、性能完全等价。

等价方法语法写法:

var realSite = siteNames.Where(s => s.EndsWith(".cn"));
Code language: JavaScript (javascript)

示例2:LINQ to XML 查询XML文档

需要引入程序集:System.Xml.Linq,命名空间:using System.Xml.Linq;

using System;
using System.Linq;
using System.Xml.Linq;

namespace ConsoleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 构造XML字符串解析为XElement对象
            XElement books = XElement.Parse(@"
<books>
    <book>
        <title>C#入门</title>
        <price>12.34</price>
    </book>
    <book>
        <title>Python教程</title>
        <price>3.5</price>
    </book>
</books>");

            // 查询:取出价格小于10元的书籍标题
            var titles =
                from book in books.Elements("book")
                where (double)book.Element("price") < 10.0
                select book.Element("title");

            foreach (var item in titles)
            {
                Console.WriteLine(item.Value);
            }
            Console.ReadKey();
        }
    }
}
Code language: HTML, XML (xml)

输出:

Python教程

关键API

  • XElement.Parse():将XML文本解析成内存XML对象
  • .Elements("book"):获取子节点
  • .Element("price"):获取第一个匹配子节点
  • .Value:读取节点文本值

本地查询 vs 远程数据库查询(IEnumerable 与 IQueryable)

  1. IEnumerable<T>(LINQ to Objects):内存集合,查询编译为委托,所有过滤、逻辑全部在本地内存执行。数组、List返回的序列属于这个。
  2. IQueryable<T>(EF Core、LINQ To SQL):针对远程数据源,编译器编译为表达式树;EF Core解析表达式树翻译成SQL语句交给数据库执行,减少内存开销。

简单IQueryable provider只支持少量查询;EF Core属于复杂Provider,可以把完整LINQ翻译成SQL,同时做类型映射。

LINQ可以做的事

  1. 筛选 where
  2. 排序 orderby
  3. 投影 select(转换输出字段、类型)
  4. 分组 group by
  5. 聚合:Count()Max()Sum()(这类没有查询子句,只能调用方法语法)
  6. 集合转换、类型转换

LINQ不只是查询,也可以做数据转换,例如字符串数组转数字集合,排序输出。

总结

  1. 查询变量只是保存查询逻辑,延迟执行,多次foreach会重复执行查询。
  2. First() / Last() 在无数据时报错;优先用xxxOrDefault()更安全。
  3. LINQ to XML需要手动引用System.Xml.Linq
  4. 查询语法和链式方法语法可以混合使用,没有性能差异。
  5. 操作数据库尽量使用IQueryable<T>,让过滤逻辑交给数据库执行,不要把全部数据拉到内存再过滤。

Leave a Reply

Your email address will not be published. Required fields are marked *