在 FindAsync 上应用全局查询过滤器

Apply Gobal Query Filter on FindAsync

我在 ASP.NET Core API:

中实施了 软删除 查询行为
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   //...
   modelBuilder.Entity<OneOfMyEntity>().HasQueryFilter(entity => !entity.IsDeleted);
   modelBuilder.Entity<AnotherEntity>().HasQueryFilter(entity => !entity.IsDeleted);
}

现在这在我的 Repository 中封装了我的 DbContext:

的两个常用方法中效果很好
public async Task<List<TEntity>> GetAllExcludeDeleted()
{
   return await _context.Set<TEntity>().ToListAsync();
}
public async Task<List<TEntity>> GetAll()
{
   return await _context.Set<TEntity>().IgnoreQueryFilters().ToListAsync();
}

但是,当我在我的存储库上编写集成测试时,我意识到它不是 DbContext.FindAsync(int) 方法的解决方案:

public async Task<TEntity> GetById(int id)
{
   return await _context.Set<TEntity>().FindAsync(id);
}

我的解决方案

我可以想象用 FirstOrDefault(e => !e.IsDeleted)

替换 FindAsync(id) 来处理这个问题

真题

你们怎么看,有什么比用 Linq query 替换 FindAsync 更好的解决方案?与 FindAsync 相比,FirstOrDefault 是否有一些 极端性能降级

Does FirstOrDefault has some extreme performance downgrade compared to FindAsync?

实际上 Find{Async} 在本地缓存中未跟踪(包含)具有指定键的实体时使用 FirstOrDefault{Async}

所以 Find{Async} 的唯一好处是在多次使用时目标实体在本地缓存,因为它避免了数据库访问。

另一方面,Find{Async}不支持预先加载(Include / ThenInclude),通常用于短暂的上下文中,一次性寻找特定的键, 所以通常使用它没有真正的好处。

另一个与性能无关的区别是 Find{Async} 允许您定位新的未提交实体(状态 Added),而所有其他方法查询数据库和 return 仅存在(或标记为 Deleted)的实体。但它不能也不应用全局查询过滤器。

所以这真的取决于具体的用法。我会说在大多数常见情况下,您只需使用 FirstOrDefault{Async},而在极少数情况下 - Find{Async}。我想大多数“存储库”示例都使用 Find{Async},因为它具有更紧凑的语法,而不是因为性能或其主要目的。