EF Core 本身不内置审计日志功能,但可通过重写 SaveChanges/SaveChangesAsync,利用 ChangeTracker 捕获新增、修改、删除操作及字段级变化,写入自定义 AuditLog 实体实现轻量审计;需定义日志实体、注入用户上下文、处理字段过滤与脱敏。

EF Core 本身不内置审计日志功能,但可以通过拦截 SaveChanges 过程,在数据写入数据库前自动捕获实体的变更状态,从而实现轻量、统一的审计日志记录。核心思路是:在 DbContext 中重写 SaveChanges / SaveChangesAsync,遍历所有被跟踪的实体,提取新增、修改、删除操作及字段级变化,并写入自定义审计日志表。
1. 定义审计日志实体
先创建一个标准的日志实体,用于存储操作人、时间、实体类型、操作类型(Add/Update/Delete)、关键字段变更等信息:
- AuditLogId:主键(Guid 或 long)
- UserId:当前操作用户 ID(需从上下文或 ClaimsPrincipal 获取)
- EntityType:被操作的实体类名(如 "Order")
- EntityId:被操作实体的主键值(支持 string/int/Guid)
- Action:操作类型("Created", "Updated", "Deleted")
- ChangedFields:JSON 字符串,记录字段名、旧值、新值(仅 Update 时有差异)
- Timestamp:UTC 时间戳
2. 在 DbContext 中注入审计逻辑
重写 SaveChanges 方法,利用 EF Core 的 ChangeTracker 获取变更状态:
- 调用
ChangeTracker.Entries()遍历所有状态为Added、Modified、Deleted的实体 - 对每个条目,提取主键值(
entry.Metadata.FindPrimaryKey().Properties)和变更详情 - 对于
Modified条目,用entry.OriginalValues和entry.CurrentValues对比字段差异 - 构造
AuditLog实体并添加到当前上下文(注意:避免递归触发审计) - 建议将审计日志实体加入
DbContext时使用Entry(log).State = EntityState.Added而非Add(),防止与主 SaveChanges 冲突
3. 处理用户上下文与线程安全
获取当前用户 ID 是常见难点,推荐以下方式:
- 通过构造函数注入
IHttpContextAccessor(需在Program.cs注册),从中读取User.Identity.Name或自定义 Claim - 若非 Web 环境(如后台服务),可依赖外部传入的
CurrentUserId(例如通过作用域服务或显式参数) - 避免在审计逻辑中直接 await 异步操作(如查用户信息),保持同步处理;如必须异步,请改用
SaveChangesAsync并确保日志写入也异步完成
4. 可选增强:字段级过滤与忽略
不是所有字段都需要审计(如 UpdatedAt、RowVersion)。可通过以下方式控制:
- 给实体属性加自定义特性(如
[AuditIgnore]),在审计逻辑中跳过标记字段 - 约定忽略以
Is、Has、Last开头的布尔/时间戳字段 - 为敏感字段(如密码)强制脱敏:写入日志前替换为
"[REDACTED]"
基本上就这些。不需要第三方库也能跑起来,关键是把变更提取逻辑写稳、字段对比做准、用户上下文拿得对。小项目够用,大系统可在此基础上接入消息队列或 Elasticsearch 做异步归档。










