当核心诉求是极致查询性能、已存在复杂sql或需精细控制sql执行时,应选dapper;需快速构建完整crud、关系建模和长期可维护业务系统时,应选ef core;二者可分层共存,ef处理主业务增删改,dapper专注高性能只读场景。

什么时候该用 Dapper 而不是 EF Core
当你的核心诉求是「极致查询性能」「已存在复杂 SQL」「或需要精细控制每一条 SQL 的执行」时,Dapper 是更直接的选择。它不生成 SQL,不维护变更跟踪,也不抽象数据库行为——你写什么,它就执行什么。
常见场景包括:报表导出、实时看板聚合查询、遗留系统数据迁移、高频低延迟读服务(如行情快照)。
-
Dapper在单表简单查询上比EF Core快 2–3 倍;多表 JOIN + 大结果集时差距可能拉到 5 倍以上 - 无法用
Include做导航属性加载,必须手写 JOIN 或分步查询 - 没有迁移(
Migrations)、没有实体验证、没有并发令牌自动处理 - 参数绑定只支持命名参数(
@id),不支持匿名对象以外的复杂对象映射(需手动DynamicParameters)
什么时候必须选 EF Core
当你需要快速构建具备完整 CRUD、关系建模、事务一致性、跨数据库兼容性(如从 SQL Server 切到 PostgreSQL)和长期可维护性的业务系统时,EF Core 提供的是生产力护城河,不是性能妥协。
典型适用场景:内部管理系统、SaaS 多租户后台、需要频繁迭代模型的中台服务。
- 支持 LINQ 查询翻译,开发时用 C# 写逻辑,运行时转成目标数据库 SQL
- 内置变更跟踪,
SaveChanges()自动识别新增/修改/删除并生成对应语句 -
HasIndex、IsRequired、OwnsOne等配置能驱动数据库约束和迁移脚本生成 - 异步 API 全覆盖(
ToListAsync、ExecuteSqlInterpolatedAsync),且线程安全
Dapper 和 EF Core 能不能一起用
完全可以,而且在真实项目中很常见——关键在于分层隔离,避免混用导致心智负担。
推荐做法是:用 EF Core 管理主业务流程(增删改、关联保存、领域事件触发),用 Dapper 单独封装高性能只读模块(如统计汇总、导出查询、搜索建议)。
- 共享同一
DbContext的连接字符串和事务(通过context.Database.GetDbConnection()获取底层连接) - 不要在同一个事务里混用
EF Core的SaveChanges()和Dapper的写操作,容易因连接状态不同步引发异常 - 避免对同一张表同时用 EF 的追踪查询 + Dapper 的更新——EF 缓存可能过期,导致后续读取脏数据
using (var connection = context.Database.GetDbConnection())
{
await connection.OpenAsync();
using (var tx = await connection.BeginTransactionAsync())
{
// Dapper 查询
var stats = await connection.QueryAsync<ReportRow>(
"SELECT COUNT(*) as Total FROM Orders WHERE CreatedAt > @since",
new { since = DateTime.UtcNow.AddDays(-7) },
tx);
<pre class='brush:php;toolbar:false;'> // EF Core 修改
var order = await context.Orders.FindAsync(123);
order.Status = "Shipped";
await context.SaveChangesAsync();
await tx.CommitAsync();
}}
新手最容易踩的 EF Core 性能坑
不是 EF 慢,而是默认行为在高负载下容易暴露问题。几个立刻见效的调整点:
- 禁用自动检测变更:
context.ChangeTracker.AutoDetectChangesEnabled = false(仅适用于只读或批量插入后统一 Save) - 查询不用
AsNoTracking()时,EF 会为每个实体创建追踪快照——列表页、搜索页务必加上 -
FindAsync()只走主键索引,但FirstOrDefaultAsync(x => x.Email == "...")会触发全表扫描,记得加数据库索引 - 避免 N+1:用
Include+Select投影到 DTO,而不是先查主表再循环查子表
真正复杂的查询,别硬套 LINQ——EF Core 7+ 支持 ExecuteSqlInterpolated,可以直接内联参数化 SQL,又保留上下文事务能力。










