一、前置说明
- LINQ to SQL:仅支持 .NET Framework、仅SQL Server、DBML设计器、
DataContext 上下文,已淘汰。
- EF Core(LINQ to Entities):跨平台 .NET Core/.NET 5+、多数据库、CodeFirst为主、
DbContext,现代标准。
- 两者查询 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 纯代码 |
六、总结
- LINQ 查询表达式(Where/Select/Join/OrderBy)两套技术完全通用,会LINQ to SQL几乎零成本看懂EF Core查询;
- 真正分水岭是上下文、增删改API、异步、批量操作、迁移、多库支持;
- 新项目直接 EF Core,老 .NET Framework WebForm 遗留系统才会见到 LINQ to SQL;
- EF Core 是 LINQ to SQL 的官方继任者,功能全面超越,是目前行业标准。