EF Core LINQ 基础示例,对比当年 LINQ to SQL 的写法差异

一、前置说明

  1. LINQ to SQL:仅支持 .NET Framework、仅SQL Server、DBML设计器、DataContext 上下文,已淘汰。
  2. EF Core(LINQ to Entities):跨平台 .NET Core/.NET 5+、多数据库、CodeFirst为主、DbContext,现代标准。
  3. 两者查询 LINQ 语法高度相似,但上下文、新增、修改、配置、生命周期、异步写法差异巨大。

环境准备

EF Core NuGet(SQL Server)

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />

二、统一测试实体(两张表:用户+订单)

1)EF Core 实体(无设计器,纯特性/fluent配置)

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

// 用户表
[Table("UserInfo")]
public class UserInfo
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }

    [MaxLength(50)]
    public string UserName { get; set; }
    public int Age { get; set; }
    public DateTime CreateTime { get; set; }

    // 导航属性:一对多
    public List<Order> Orders { get; set; } = new();
}

// 订单表
[Table("Order")]
public class Order
{
    [Key]
    public long Id { get; set; }
    public long UserId { get; set; }
    public string OrderNo { get; set; }
    public decimal Amount { get; set; }

    // 导航属性
    public UserInfo User { get; set; }
}

2)EF Core 上下文 DbContext(替代LINQ to SQL的DataContext)

public class AppDbContext : DbContext
{
    // DbSet 对应数据库表(替代LINQ to SQL Table<T>)
    public DbSet<UserInfo> UserInfos { get; set; }
    public DbSet<Order> Orders { get; set; }

    // 配置连接字符串
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string conn = "Server=.;Database=TestDB;Trusted_Connection=True;TrustServerCertificate=True";
        optionsBuilder.UseSqlServer(conn);
    }

    // FluentAPI 配置(可选,替代特性)
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserInfo>()
            .HasMany(u => u.Orders)
            .WithOne(o => o.User)
            .HasForeignKey(o => o.UserId);
    }
}

三、完整 EF Core LINQ CRUD 示例(同步+异步)

public static void EfCoreDemo()
{
    using var db = new AppDbContext();

    #region 1.新增
    // 单条新增
    var user = new UserInfo
    {
        UserName = "张三",
        Age = 22,
        CreateTime = DateTime.Now
    };
    db.UserInfos.Add(user);
    db.SaveChanges(); // 提交事务,返回受影响行数
    long newId = user.Id;

    // 批量新增
    var userList = new List<UserInfo>
    {
        new UserInfo{UserName="李四",Age=25,CreateTime=DateTime.Now},
        new UserInfo{UserName="王五",Age=28,CreateTime=DateTime.Now}
    };
    db.UserInfos.AddRange(userList);
    db.SaveChanges();
    #endregion

    #region 2.LINQ 查询(和LINQ to SQL语法几乎一样)
    // 查全部
    List<UserInfo> all = db.UserInfos.ToList();

    // 条件筛选 + 排序 LINQ表达式
    List<UserInfo> filterList = db.UserInfos
        .Where(u => u.Age > 20 && u.UserName.Contains("张"))
        .OrderByDescending(u => u.CreateTime)
        .ToList();

    // 单条 FirstOrDefault 防止报错
    UserInfo single = db.UserInfos.FirstOrDefault(u => u.Id == 1);

    // 投影只查部分字段
    var dto = db.UserInfos
        .Select(u => new { u.Id, u.UserName, u.Age })
        .ToList();

    // 判断存在
    bool hasData = db.UserInfos.Any(u => u.Id == 1);

    // 分页查询
    int page = 1;
    int size = 10;
    var pageData = db.UserInfos
        .Where(u => u.Age > 18)
        .Skip((page - 1) * size)
        .Take(size)
        .ToList();
    long total = db.UserInfos.Count(u => u.Age > 18);

    // 联表查询 Include 贪婪加载导航(EF特色)
    var userWithOrder = db.UserInfos
        .Include(u => u.Orders) // 关联订单
        .Where(u => u.Id == 1)
        .FirstOrDefault();

    // 多表Join写法(和LINQ to SQL一致)
    var joinData = (from u in db.UserInfos
                    join o in db.Orders on u.Id equals o.UserId
                    select new
                    {
                        u.UserName,
                        o.OrderNo,
                        o.Amount
                    }).ToList();
    #endregion

    #region 3.修改
    // 方式1:先查询再修改(变更追踪自动识别)
    var editUser = db.UserInfos.Find(1);
    if (editUser != null)
    {
        editUser.Age = 30;
        db.SaveChanges();
    }

    // 方式2:批量条件更新(EF Core 7+支持ExecuteUpdate,无需先查)
    db.UserInfos
        .Where(u => u.UserName == "李四")
        .ExecuteUpdate(setters => setters.SetProperty(u => u.Age, 35));
    #endregion

    #region 4.删除
    // 单条删除
    var delUser = db.UserInfos.Find(1);
    if (delUser != null)
        db.UserInfos.Remove(delUser);
    db.SaveChanges();

    // 批量条件删除(EF Core7+)
    db.UserInfos
        .Where(u => u.Age < 18)
        .ExecuteDelete();
    #endregion

    #region 5.事务
    using var tran = db.Database.BeginTransaction();
    try
    {
        db.UserInfos.Add(new UserInfo { UserName = "小明", Age = 20, CreateTime = DateTime.Now });
        db.SaveChanges();
        db.Orders.Add(new Order { UserId = user.Id, OrderNo = "ORD001", Amount = 99 });
        db.SaveChanges();
        tran.Commit();
    }
    catch
    {
        tran.Rollback();
    }
    #endregion
}

// 异步版本(EF Core主推,LINQ to SQL无原生异步)
public static async Task EfCoreAsyncDemo()
{
    using var db = new AppDbContext();
    var list = await db.UserInfos.Where(u => u.Age > 18).ToListAsync();
    bool exist = await db.UserInfos.AnyAsync(u => u.Id == 1);
    long count = await db.UserInfos.CountAsync();
}

四、当年 LINQ to SQL 对等代码(对比差异)

1. 上下文(DataContext,由DBML设计器自动生成)

// 自动生成,不能手写配置,依赖DBML可视化设计器
TestDBDataContext db = new TestDBDataContext();

// 表集合 Table<T>,而非 DbSet<T>
Table<UserInfo> UserInfos = db.UserInfos;
Table<Order> Orders = db.Orders;

2. LINQ 查询(查询语法几乎一模一样,只是上下文API不同)

// LINQ 查询逻辑完全通用,这是唯一相似点
var list = db.UserInfos
    .Where(u => u.Age > 20)
    .OrderByDescending(u => u.CreateTime)
    .ToList();

// 联表写法一致
var join = from u in db.UserInfos
           join o in db.Orders on u.Id equals o.UserId
           select new { u.UserName, o.OrderNo };

3. 增删改巨大差异

TestDBDataContext db = new TestDBDataContext();

// 新增
UserInfo user = new UserInfo { UserName = "张三", Age = 22 };
db.UserInfos.InsertOnSubmit(user);
db.SubmitChanges(); // 提交方法名不同

// 修改:只能查出来改,无批量更新API
var edit = db.UserInfos.First(u => u.Id == 1);
edit.Age = 30;
db.SubmitChanges();

// 删除
var del = db.UserInfos.First(u => u.Id == 2);
db.UserInfos.DeleteOnSubmit(del);
db.SubmitChanges();

// 无原生异步、无ExecuteUpdate/ExecuteDelete、无Include导航加载
// 事务写法完全不同
using (var trans = new TransactionScope())
{
    db.SubmitChanges();
    trans.Complete();
}

五、核心差异对照表

对比项 LINQ to SQL EF Core
上下文 DataContext(DBML设计器生成) DbContext 手写CodeFirst
表集合 Table<T> DbSet<T>
提交保存 SubmitChanges() SaveChanges() / SaveChangesAsync()
新增API InsertOnSubmit() Add() / AddRange()
删除API DeleteOnSubmit() Remove() / RemoveRange()
批量操作 无原生批量更新/删除,必须全查出来 ExecuteUpdate / ExecuteDelete
异步支持 完全不支持 全套 ToListAsync/AnyAsync/CountAsync
多数据库 仅SQL Server SQLServer/MySQL/SQLite/PG/Oracle
跨平台 仅限 .NET Framework Windows .NET全平台、Linux、Mac、MAUI
表关联加载 弱关联,无Include .Include().ThenInclude() 导航贪婪加载
建表迁移 无迁移工具,依赖DBML Add-Migration / Update-Database 迁移
生命周期 DataContext轻量化但不推荐复用 推荐 using 局部实例/DI注入
配置方式 DBML可视化设计 特性 + FluentAPI 纯代码

六、总结

  1. LINQ 查询表达式(Where/Select/Join/OrderBy)两套技术完全通用,会LINQ to SQL几乎零成本看懂EF Core查询;
  2. 真正分水岭是上下文、增删改API、异步、批量操作、迁移、多库支持
  3. 新项目直接 EF Core,老 .NET Framework WebForm 遗留系统才会见到 LINQ to SQL;
  4. EF Core 是 LINQ to SQL 的官方继任者,功能全面超越,是目前行业标准。
posted @ 2026-06-25 09:31  人生就是修炼  阅读(4)  评论(0)    收藏  举报