EF Core数据库优先模式一对多导航属性查询为空问题
问题根因
- 核心原因是Entity Framework Core默认不会自动加载关联导航属性,你当前代码既没有使用预加载、也没有配置懒加载、也没有手动显式加载,EF根本不会发起关联表的查询,导航属性自然只会保留构造函数初始化的空集合,和EF版本、外键数值类型无关。你之前功能正常是因为所有操作都是单表直接查询,从未使用过导航属性做关联查询,第一次使用导航属性才触发这个默认行为问题。
- 次要隐患是你创建外键时使用了
WITH NOCHECK参数,且创建后未对存量数据做校验、未启用约束信任,会导致数据库层面无法保证参照完整性,同时脚手架自动生成的删除行为配置(ClientSetNull)和数据库默认的NO ACTION不匹配,后续删除数据时可能抛出异常,但该问题不会影响查询时导航属性的加载逻辑。
验证方法
直接在查询时添加Include预加载关联数据,如果能正常返回结果即可确认根因:
// 测试MainServerData关联Currency database.MainServerData .Include(s => s.Currencies) .FirstOrDefault(s => s.ServerId == decimalId)?.Currencies?.FirstOrDefault(); // 测试Currency关联Wallet database.Currencies .Include(c => c.Wallets) .FirstOrDefault(c => c.Id == pickedCurrencyId)?.Wallets.Count();
修复方案
根据你的开发习惯二选一即可:
方案1:使用预加载(推荐,性能可控)
所有需要用到导航属性的查询,都通过Include(多级关联用ThenInclude)手动指定需要加载的关联数据,优点是可以明确控制每个查询的join逻辑,不会出现意外的N+1性能问题,缺点是漏加Include就会出现空集合问题。如果经常需要多级关联,可以封装公共的查询扩展方法统一配置Include逻辑,减少重复代码。
方案2:开启懒加载(编码便捷)
- 安装和当前EF Core版本一致的
Microsoft.EntityFrameworkCore.ProxiesNuGet包(你当前使用5.0.17版本,就安装对应5.0.17版本的包) - 在
DatabaseContextBase的OnConfiguring方法中添加懒加载代理配置:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseLazyLoadingProxies() // 开启懒加载代理 .UseSqlServer("你的数据库连接字符串"); // 保留原有数据库配置 }
配置完成后,所有标记为virtual的导航属性会在第一次被访问时自动发起查询加载关联数据,不需要手动写Include,缺点是不注意的话容易出现循环遍历导航属性导致的N+1查询性能问题。
外键隐患修复
执行以下SQL语句,校验存量数据合法性并启用外键约束的信任状态,保证数据库层面参照完整性:
ALTER TABLE [dbo].[Currencies] WITH CHECK CHECK CONSTRAINT [FK_Currencies_MainServerData] ALTER TABLE [dbo].[Wallets] WITH CHECK CHECK CONSTRAINT [FK_Wallets_Currencies]
后续执行脚手架命令时,添加-OnDeleteBehavior NoAction参数,让生成的模型删除行为和数据库默认配置一致,避免配置不匹配导致的删除异常:
Scaffold-DbContext "<my connection string>" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Database/GeneratedNew -Context DatabaseContextBase -DataAnnotations -OnDeleteBehavior NoAction
内容的提问来源于stack exchange,提问作者Sadem
相关产品推荐
相关产品推荐

