Entity Framework查询同ID实体无法获取原始值对比变更如何解决
问题根源
你遇到的是Entity Framework的**跟踪缓存(变更跟踪器)**默认行为导致的问题:
同一个DbContext实例生命周期内,当你第一次查询指定Id的Ticket实体后,EF会将该实体对象放入跟踪缓存中;后续你修改了该实体的内存属性,再次以相同Id查询该实体时,EF默认优先返回缓存中已存在的实体对象引用,不会真正请求数据库拉取最新的持久化数据,所以你拿到的originalTicket和修改后的ticket本质是同一个内存对象,自然对比不出任何变更。
可行解决方案
方案1:查询原始实体时使用非跟踪查询(最常用)
在查询原始版本的实体时添加AsNoTracking()方法,强制EF绕过跟踪缓存,直接访问数据库拉取最新的持久化数据。
你可以修改GetByIdAsync方法,添加是否跟踪的参数:
public async Task<Ticket> GetByIdAsync(int id, bool asNoTracking = false) { var query = _db.Tickets .Include(p => p.Project) .Include(t => t.Comments) .Include(p=>p.AssignedUser); if(asNoTracking) query = query.AsNoTracking(); return await query.FirstOrDefaultAsync(p => p.Id == id); }
调用获取原始实体的时候传入asNoTracking:true即可:
var ticketChanges = GetTicketChanges(await GetByIdAsync(ticket.Id, asNoTracking: true), ticket, user);
方案2:从EF跟踪器中直接获取原始值(性能最优,无需额外查询数据库)
你不需要额外查询数据库,直接通过EF的变更跟踪器就能拿到该实体所有属性的原始持久化值,还能直接获取发生变更的属性列表,性能更高:
public List<History> GetTicketChanges(Ticket ticket, string changesAuthor) { List<History> ticketChanges = new(); if (ticket == null) return ticketChanges; var entry = _db.Entry(ticket); // 直接遍历所有变更的属性 foreach (var property in entry.Properties.Where(p => p.IsModified)) { var originalValue = property.OriginalValue; var currentValue = property.CurrentValue; // 在这里生成对应的变更记录即可 } // 其余历史记录生成逻辑 }
方案3:手动将修改后的实体从跟踪缓存中分离
在查询原始实体前,先把当前修改后的实体从EF跟踪器中移除,后续查询就会去数据库拉取新数据:
// 在调用GetByIdAsync查询原始数据前执行 _db.Entry(ticket).State = EntityState.Detached; // 后续再查询就会从数据库拉取原始数据 var originalTicket = await GetByIdAsync(ticket.Id);
内容的提问来源于stack exchange,提问作者upizs
相关产品推荐
相关产品推荐

