CLINQ数据查询技能Skill csharp-linq

LINQ(语言集成查询)是C#编程语言中的核心技术,用于统一查询和操作各种数据源,如内存集合、数据库、XML等。它支持查询语法和方法语法,提供延迟执行、表达式树和性能优化功能,适用于后端开发、数据分析和应用开发。关键词:C# LINQ, 数据查询, 集合操作, 延迟执行, 表达式树, 性能优化。

后端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称:csharp-linq 用户可调用:false 描述:在使用LINQ(语言集成查询)时,包括查询和方法语法、延迟执行、表达式树和性能优化。 允许工具:

  • 读取
  • 写入
  • 编辑
  • 搜索
  • 全局
  • Bash

C# LINQ

LINQ(语言集成查询)提供了跨不同数据源(包括集合、数据库、XML等)的一致查询体验。它结合了类似SQL查询的强大功能与C#类型安全和智能感知支持,实现了表达性强且可维护的数据操作代码。

查询语法

查询语法提供类似SQL的语法用于查询数据源,在编译时转换为方法调用。

using System;
using System.Collections.Generic;
using System.Linq;

public class QuerySyntaxExamples
{
    public record Person(string Name, int Age, string City);

    // 基本查询
    public IEnumerable<Person> BasicQuery(List<Person> people)
    {
        var query = from p in people
                    where p.Age >= 18
                    select p;

        return query;
    }

    // 带多个条件的查询
    public IEnumerable<Person> MultipleConditions(List<Person> people)
    {
        var query = from p in people
                    where p.Age >= 18 && p.City == "Seattle"
                    orderby p.Name
                    select p;

        return query;
    }

    // 使用select进行投影
    public IEnumerable<string> ProjectNames(List<Person> people)
    {
        var query = from p in people
                    where p.Age >= 21
                    select p.Name;

        return query;
    }

    // 匿名类型
    public IEnumerable<object> AnonymousProjection(List<Person> people)
    {
        var query = from p in people
                    select new
                    {
                        p.Name,
                        p.Age,
                        IsAdult = p.Age >= 18
                    };

        return query;
    }

    // 分组
    public IEnumerable<IGrouping<string, Person>> GroupByCity(
        List<Person> people)
    {
        var query = from p in people
                    group p by p.City;

        return query;
    }

    // 带投影的分组
    public IEnumerable<object> GroupWithProjection(List<Person> people)
    {
        var query = from p in people
                    group p by p.City into cityGroup
                    select new
                    {
                        City = cityGroup.Key,
                        Count = cityGroup.Count(),
                        AverageAge = cityGroup.Average(p => p.Age)
                    };

        return query;
    }

    // 连接
    public record Order(int Id, string PersonName, decimal Amount);

    public IEnumerable<object> JoinExample(
        List<Person> people,
        List<Order> orders)
    {
        var query = from p in people
                    join o in orders on p.Name equals o.PersonName
                    select new
                    {
                        p.Name,
                        p.Age,
                        OrderAmount = o.Amount
                    };

        return query;
    }

    // 左连接
    public IEnumerable<object> LeftJoin(
        List<Person> people,
        List<Order> orders)
    {
        var query = from p in people
                    join o in orders on p.Name equals o.PersonName
                    into personOrders
                    from po in personOrders.DefaultIfEmpty()
                    select new
                    {
                        p.Name,
                        OrderAmount = po?.Amount ?? 0
                    };

        return query;
    }
}

方法语法

方法语法使用扩展方法进行查询,提供更多灵活性和访问所有LINQ操作符。

using System;
using System.Collections.Generic;
using System.Linq;

public class MethodSyntaxExamples
{
    public record Product(string Name, decimal Price, string Category);

    // 过滤
    public IEnumerable<Product> FilterProducts(List<Product> products)
    {
        return products
            .Where(p => p.Price > 100)
            .Where(p => p.Category == "Electronics");
    }

    // 排序
    public IEnumerable<Product> OrderProducts(List<Product> products)
    {
        return products
            .OrderBy(p => p.Category)
            .ThenByDescending(p => p.Price);
    }

    // 投影
    public IEnumerable<string> ProjectNames(List<Product> products)
    {
        return products
            .Select(p => p.Name.ToUpper());
    }

    // SelectMany(扁平化)
    public IEnumerable<int> FlattenLists()
    {
        var lists = new List<List<int>>
        {
            new List<int> { 1, 2, 3 },
            new List<int> { 4, 5 },
            new List<int> { 6, 7, 8, 9 }
        };

        return lists.SelectMany(list => list);
    }

    // 分组
    public IEnumerable<IGrouping<string, Product>> GroupByCategory(
        List<Product> products)
    {
        return products.GroupBy(p => p.Category);
    }

    // 聚合
    public void AggregationExamples(List<Product> products)
    {
        decimal total = products.Sum(p => p.Price);
        decimal average = products.Average(p => p.Price);
        decimal max = products.Max(p => p.Price);
        decimal min = products.Min(p => p.Price);
        int count = products.Count();
        int expensiveCount = products.Count(p => p.Price > 500);
    }

    // Any和All
    public void ExistenceChecks(List<Product> products)
    {
        bool hasExpensive = products.Any(p => p.Price > 1000);
        bool allAffordable = products.All(p => p.Price < 100);
        bool hasElectronics = products.Any(p =>
            p.Category == "Electronics");
    }

    // Take和Skip
    public IEnumerable<Product> Pagination(
        List<Product> products,
        int page,
        int pageSize)
    {
        return products
            .OrderBy(p => p.Name)
            .Skip((page - 1) * pageSize)
            .Take(pageSize);
    }

    // Distinct
    public IEnumerable<string> UniqueCategories(List<Product> products)
    {
        return products
            .Select(p => p.Category)
            .Distinct();
    }

    // 集合操作
    public void SetOperations(
        List<Product> products1,
        List<Product> products2)
    {
        var union = products1.Union(products2);
        var intersect = products1.Intersect(products2);
        var except = products1.Except(products2);
    }
}

延迟执行

LINQ查询使用延迟执行,意味着查询在枚举时执行,而不是在定义时。

using System;
using System.Collections.Generic;
using System.Linq;

public class DeferredExecutionExamples
{
    // 延迟执行演示
    public void DeferredExecutionDemo()
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };

        // 查询已定义但未执行
        var query = numbers.Where(n => n > 2);

        Console.WriteLine("修改前:");
        foreach (var n in query)  // 在此处执行
        {
            Console.WriteLine(n);  // 3, 4, 5
        }

        // 修改源数据
        numbers.Add(6);
        numbers.Add(7);

        Console.WriteLine("修改后:");
        foreach (var n in query)  // 使用新数据再次执行
        {
            Console.WriteLine(n);  // 3, 4, 5, 6, 7
        }
    }

    // 使用ToList立即执行
    public void ImmediateExecution()
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };

        // 立即执行并缓存结果
        var list = numbers.Where(n => n > 2).ToList();

        numbers.Add(6);
        numbers.Add(7);

        // list仍然只包含3, 4, 5
        foreach (var n in list)
        {
            Console.WriteLine(n);
        }
    }

    // 强制立即执行的操作符
    public void ImmediateExecutionOperators()
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };

        // 这些立即执行
        var array = numbers.Where(n => n > 2).ToArray();
        var dict = numbers.ToDictionary(n => n, n => n * 2);
        var hashSet = numbers.ToHashSet();
        var lookup = numbers.ToLookup(n => n % 2);

        // 聚合操作立即执行
        int count = numbers.Count(n => n > 2);
        int sum = numbers.Sum();
        int max = numbers.Max();
        bool any = numbers.Any(n => n > 10);
    }

    // 多次枚举问题
    public void MultipleEnumeration()
    {
        var numbers = GetNumbers();  // IEnumerable

        // 差:枚举两次
        int count = numbers.Count();
        int sum = numbers.Sum();

        // 好:枚举一次,缓存
        var list = numbers.ToList();
        count = list.Count;
        sum = list.Sum();
    }

    private IEnumerable<int> GetNumbers()
    {
        Console.WriteLine("生成数字...");
        for (int i = 1; i <= 5; i++)
        {
            yield return i;
        }
    }
}

复杂查询

组合多个LINQ操作进行复杂数据转换。

using System;
using System.Collections.Generic;
using System.Linq;

public class ComplexQueries
{
    public record Student(string Name, int Grade, string Subject,
                          int Score);
    public record Course(string Subject, string Teacher, int Credits);

    // 复杂过滤和投影
    public IEnumerable<object> StudentReport(
        List<Student> students,
        List<Course> courses)
    {
        return students
            .Where(s => s.Grade >= 10)
            .GroupBy(s => new { s.Name, s.Grade })
            .Select(g => new
            {
                g.Key.Name,
                g.Key.Grade,
                Subjects = g.Select(s => s.Subject).Distinct(),
                AverageScore = g.Average(s => s.Score),
                TotalCredits = g.Join(
                    courses,
                    s => s.Subject,
                    c => c.Subject,
                    (s, c) => c.Credits
                ).Sum()
            })
            .OrderByDescending(s => s.AverageScore);
    }

    // 嵌套查询
    public IEnumerable<object> TopStudentsBySubject(
        List<Student> students)
    {
        return students
            .GroupBy(s => s.Subject)
            .Select(g => new
            {
                Subject = g.Key,
                TopStudent = g
                    .OrderByDescending(s => s.Score)
                    .Select(s => new { s.Name, s.Score })
                    .FirstOrDefault(),
                ClassAverage = g.Average(s => s.Score)
            });
    }

    // 窗口函数
    public IEnumerable<object> RunningTotal(List<Student> students)
    {
        return students
            .OrderBy(s => s.Name)
            .ThenBy(s => s.Subject)
            .Select((s, index) => new
            {
                s.Name,
                s.Subject,
                s.Score,
                RunningTotal = students
                    .Take(index + 1)
                    .Sum(x => x.Score)
            });
    }

    // 层次数据
    public record Category(string Name, string Parent);

    public IEnumerable<object> BuildHierarchy(List<Category> categories)
    {
        return categories
            .Where(c => c.Parent == null)
            .Select(parent => new
            {
                parent.Name,
                Children = categories
                    .Where(c => c.Parent == parent.Name)
                    .Select(child => new
                    {
                        child.Name,
                        Grandchildren = categories
                            .Where(gc => gc.Parent == child.Name)
                    })
            });
    }
}

性能优化

理解LINQ性能特征和优化技术。

using System;
using System.Collections.Generic;
using System.Linq;

public class PerformanceOptimization
{
    // 使用Count属性代替Count()方法
    public void CountOptimization(List<int> numbers)
    {
        // 慢:迭代整个集合
        int count1 = numbers.Where(n => n > 0).Count();

        // 快:如果可用,使用Count属性
        int count2 = numbers.Count;

        // 使用Any()代替Count()进行存在检查
        bool hasItems = numbers.Any();  // 快
        bool hasItems2 = numbers.Count() > 0;  // 慢
    }

    // 避免多次枚举
    public void AvoidMultipleEnumerations()
    {
        var query = GetExpensiveQuery();

        // 差:多次枚举
        int count = query.Count();
        int sum = query.Sum();
        var first = query.First();

        // 好:枚举一次
        var list = query.ToList();
        count = list.Count;
        sum = list.Sum();
        first = list.First();
    }

    // 在Select之前使用Where
    public IEnumerable<string> FilterBeforeProject(List<int> numbers)
    {
        // 好:先过滤(减少投影项)
        return numbers
            .Where(n => n > 100)
            .Select(n => n.ToString());

        // 差:先投影所有,再过滤
        // return numbers
        //     .Select(n => n.ToString())
        //     .Where(s => int.Parse(s) > 100);
    }

    // 使用FirstOrDefault代替Where().First()
    public int? FindFirst(List<int> numbers)
    {
        // 好:在第一个匹配处停止
        return numbers.FirstOrDefault(n => n > 100);

        // 差:过滤所有,然后取第一个
        // return numbers.Where(n => n > 100).FirstOrDefault();
    }

    // 避免不必要的排序
    public IEnumerable<int> TakeWithoutSort(List<int> numbers)
    {
        // 如果只需要前N个,考虑部分排序
        return numbers
            .OrderByDescending(n => n)
            .Take(10);

        // 对于大型集合更好:使用PriorityQueue或类似
    }

    // 对CPU密集型操作使用AsParallel
    public IEnumerable<int> ParallelQuery(List<int> numbers)
    {
        return numbers
            .AsParallel()
            .Where(n => ExpensiveOperation(n))
            .Select(n => n * 2);
    }

    private IEnumerable<int> GetExpensiveQuery()
    {
        return Enumerable.Range(1, 1000)
            .Where(n => n % 2 == 0);
    }

    private bool ExpensiveOperation(int n)
    {
        System.Threading.Thread.Sleep(1);
        return n > 50;
    }
}

LINQ to Objects vs LINQ to SQL

理解内存查询和数据库查询之间的差异。

using System;
using System.Collections.Generic;
using System.Linq;

public class LinqProviders
{
    public record Customer(int Id, string Name, string City);

    // LINQ to Objects(内存中)
    public void LinqToObjects(List<Customer> customers)
    {
        // 在内存中执行
        var query = customers
            .Where(c => c.City == "Seattle")
            .OrderBy(c => c.Name)
            .Select(c => new { c.Name, c.City });

        // 可以使用任何C#方法
        var withMethods = customers
            .Where(c => IsValidCity(c.City))
            .ToList();
    }

    // LINQ to SQL(数据库)
    public void LinqToSQL()
    {
        // 在实际应用中,这将是DbContext
        // var query = dbContext.Customers
        //     .Where(c => c.City == "Seattle")  // 翻译为SQL
        //     .OrderBy(c => c.Name)
        //     .Select(c => new { c.Name, c.City });

        // 不能在SQL查询中使用任意C#方法
        // 这会抛出运行时错误:
        // .Where(c => IsValidCity(c.City))

        // 使用AsEnumerable()切换到LINQ to Objects
        // var mixed = dbContext.Customers
        //     .Where(c => c.City == "Seattle")  // SQL
        //     .AsEnumerable()
        //     .Where(c => IsValidCity(c.City)); // 内存中
    }

    private bool IsValidCity(string city)
    {
        return !string.IsNullOrEmpty(city) && city.Length > 2;
    }
}

表达式树

理解用于高级LINQ场景的表达式树。

using System;
using System.Linq.Expressions;

public class ExpressionTreeExamples
{
    // 构建表达式树
    public void BuildExpressionTree()
    {
        // 手动表达式树
        ParameterExpression param = Expression.Parameter(typeof(int), "x");
        BinaryExpression body = Expression.Add(
            param,
            Expression.Constant(5)
        );
        Expression<Func<int, int>> expr =
            Expression.Lambda<Func<int, int>>(body, param);

        // 编译并执行
        Func<int, int> func = expr.Compile();
        int result = func(10);  // 15
    }

    // 从lambda到表达式
    public void LambdaToExpression()
    {
        // 从lambda表达式树
        Expression<Func<int, bool>> expr = x => x > 5;

        // 编译为委托
        Func<int, bool> func = expr.Compile();
        bool result = func(10);  // true
    }

    // 分析表达式
    public void AnalyzeExpression()
    {
        Expression<Func<int, bool>> expr = x => x > 5;

        // 获取部分
        var lambda = (LambdaExpression)expr;
        var body = (BinaryExpression)lambda.Body;
        var left = (ParameterExpression)body.Left;
        var right = (ConstantExpression)body.Right;

        Console.WriteLine($"参数:{left.Name}");
        Console.WriteLine($"操作符:{body.NodeType}");
        Console.WriteLine($"常量:{right.Value}");
    }

    // 动态查询构建
    public Expression<Func<T, bool>> BuildPredicate<T>(
        string propertyName,
        object value)
    {
        ParameterExpression param = Expression.Parameter(typeof(T), "x");
        MemberExpression property = Expression.Property(param,
                                                        propertyName);
        ConstantExpression constant = Expression.Constant(value);
        BinaryExpression equal = Expression.Equal(property, constant);

        return Expression.Lambda<Func<T, bool>>(equal, param);
    }
}

最佳实践

  1. 对具有多个操作的复杂查询使用方法语法
  2. 对看起来更像SQL的查询使用查询语法
  3. 当需要多次枚举时,调用ToList()ToArray()
  4. 使用Any()代替Count() > 0进行存在检查
  5. 在使用Select()投影之前先用Where()过滤
  6. 查找单个项目时,使用FirstOrDefault()代替Where().First()
  7. 当不需要转换数据时,避免使用Select()
  8. 仅对大型数据集的CPU密集型操作使用AsParallel()
  9. 注意延迟执行以及查询实际执行的时间
  10. 考虑频繁使用查询的表达式树编译成本

常见陷阱

  1. 多次枚举IEnumerable导致性能问题
  2. 在不理解SQL翻译的情况下对数据库查询使用LINQ
  3. 过早调用ToList(),失去延迟执行优势
  4. 在集合上使用Count()方法而不是Count属性
  5. 不释放数据库查询的IEnumerable,导致连接泄漏
  6. 在foreach更清晰的地方使用LINQ处理简单循环
  7. 过度使用AsParallel()导致开销而非加速
  8. 在lambda表达式中捕获变量导致意外闭包
  9. 在需要扁平化时使用Select()而不是SelectMany()
  10. 不理解复杂查询表达式中的操作符优先级

何时使用LINQ

在以下情况下使用LINQ:

  • 统一查询集合、数据库、XML或其他数据源
  • 表达性强且可维护的数据转换和过滤操作
  • 类型安全查询,具有编译时检查和智能感知
  • 用于数据操作的功能式编程模式
  • 复杂的分组、连接和聚合操作
  • 声明式代码,清晰表达意图
  • 与Entity Framework或其他ORM集成
  • 从可重用组件组合查询
  • 使用PLINQ对大型数据集进行并行处理
  • 跨不同数据源的一致API

资源