EF Core使用HasQueryFilter引入引用私有字段的Lambda表达式是否会引发内存泄漏?
结论
你当前的写法既存在内存泄漏风险,还会导致租户隔离功能失效,问题根源在于EF Core的模型是全局单例的特性,和lambda闭包捕获外部变量的行为共同导致的。
问题原因
- EF Core的
IModel(由OnModelCreating构建的模型对象)是应用级单例,整个应用生命周期只会初始化一次,会永久驻留在内存中。 - 你在首次初始化DbContext时调用
_tenantFilterProvider.GetExpression()返回的lambda表达式,闭包捕获了当前scoped的TenantFilterProvider实例中的_currentTenant字段。这个被捕获的TenantFilterProvider和对应CurrentTenant实例会被全局单例的模型引用,永远无法被GC回收,直接造成内存泄漏。 - 更严重的是后续所有请求的查询都会使用首次初始化时捕获的那个
CurrentTenant的TenantId,不会随当前请求的租户更新,完全达不到租户隔离的效果。
正确实现方式
官方推荐的写法是直接在DbContext中注入CurrentTenant,查询过滤器直接引用DbContext的实例成员,EF Core会自动处理表达式的解析,每次查询都使用当前DbContext实例中的租户信息,不会捕获初始化时的实例,也不会有内存泄漏问题:
public class AppDbContext : DbContext { private readonly CurrentTenant _currentTenant; public AppDbContext(DbContextOptions<AppDbContext> options, CurrentTenant currentTenant) : base(options) { _currentTenant = currentTenant; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<MyEntity>() .HasQueryFilter(entity => entity.TenantId == _currentTenant.TenantId); } }
如果你需要把过滤器逻辑抽离到单独的Provider类中,注意不要让返回的lambda捕获Provider自身的字段,需要将表达式构建为引用DbContext的实例成员,避免外部scoped实例被全局模型持有。
内容的提问来源于stack exchange,提问作者kewinbrand
相关产品推荐
相关产品推荐

