MassTransit 4.0 EF Core Saga状态无法自动加载子表的问题
我完全理解你的困扰——在EF6搭配MassTransit Saga时不用手动处理关联表加载,但切换到EF Core后,每次状态转换都得在queryCustomization里重复写Include,确实很繁琐。下面给你几个可行的解决方案,帮你摆脱重复配置的麻烦:
方案1:开启EF Core延迟加载(最省心)
EF6默认启用了延迟加载(只要导航属性是virtual),但EF Core为了性能考量默认关闭了这个特性。你可以通过以下步骤开启,让EF Core自动加载关联子表:
- 修改DbContext配置:在你的
SagaWithDependencyContextFactory对应的DbContext中,开启延迟加载代理:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseLazyLoadingProxies() // 开启延迟加载代理 .UseSqlServer("你的数据库连接字符串"); // 替换为实际数据库配置 }
- 标记Saga实体的导航属性为virtual:确保
InstanceState里的关联属性加上virtual修饰符,这样EF Core才能生成代理类实现延迟加载:
public class InstanceState : SagaStateMachineInstance { public Guid CorrelationId { get; set; } public virtual ICollection<Arg> Args { get; set; } // 标记为virtual public virtual ICollection<MoreArg> MoreArgs { get; set; } // 标记为virtual public byte[] RowVersion { get; set; } // 其他状态属性... }
配置完成后,当你在Saga状态转换中访问Args或MoreArgs时,EF Core会自动从数据库加载这些子表数据,不用再手动写Include。需要注意的是:要确保DbContext在Saga事件处理的整个生命周期内都处于活跃状态,避免出现延迟加载时DbContext已被释放的错误。
方案2:自定义Saga Repository统一配置Include
如果你不想使用延迟加载(比如担心N+1查询性能问题),可以自定义一个Saga Repository基类,把所有需要的Include逻辑集中配置,避免重复代码:
public class CustomInstanceStateRepository : EntityFrameworkSagaRepository<InstanceState> { public CustomInstanceStateRepository(Func<DbContext> dbContextFactory, bool optimistic = false) : base(dbContextFactory, ConfigureQuery, optimistic) { } private static IQueryable<InstanceState> ConfigureQuery(IQueryable<InstanceState> query) { // 在这里统一配置所有需要预加载的关联表 return query .Include(it => it.Args) .ThenInclude(it => it.MoreArgs); } }
然后在注册Saga Repository时,使用这个自定义类代替默认的EntityFrameworkSagaRepository:
var efSagaRepository = new CustomInstanceStateRepository(sagaDbContextFactory, optimistic: true);
这样所有使用该Repository的地方都会自动应用预加载配置,不用每次重复写queryCustomization。
为什么EF6不需要这么做?
EF6默认开启了延迟加载(只要导航属性是virtual),MassTransit的EF6集成会自动利用这一特性,在访问关联属性时自动加载子表数据。而EF Core为了性能考虑默认关闭了延迟加载,所以需要我们手动开启或者显式配置预加载。
内容的提问来源于stack exchange,提问作者Carlo V. Dango

