如何正确使用Entity Framework脚手架模型获取关联数据并避免循环引用
解决EF Core数据库优先模式下一对多关联数据加载与循环引用问题
首先,你当前的实现有两个核心问题:一是手动加载关联数据时用了.Result做同步阻塞(容易引发死锁),二是双向导航属性导致JSON序列化时出现循环引用。下面是完全基于脚手架生成的模型就能解决问题的正确方案:
第一步:用EF Core预加载(Include)正确加载关联数据
EF Core提供的Include方法可以实现预先加载,它会生成JOIN查询一次性获取主实体和关联数据,既高效又能避免手动加载的诸多问题。修改你的服务层代码如下:
public async Task<Champion> GetChampion(int id) { // 使用Include预先加载Champion对应的ChampionScreenshot集合 return await _context.Champion .Include(champion => champion.ChampionScreenshot) .FirstAsync(m => m.ChampionId == id); }
这样修改后,EF Core会自动填充Champion的ChampionScreenshot属性,同时避免了原代码中同步阻塞异步方法的不良实践。
第二步:解决JSON序列化循环引用问题
循环引用的根源是Champion包含ChampionScreenshot集合,而每个ChampionScreenshot又带有反向导航的Champion属性,序列化时会陷入Champion -> ChampionScreenshot -> Champion的无限递归。这里有两种无需自定义模型的解决方案:
方案1:给反向导航属性添加序列化忽略标记
因为是脚手架生成的partial类,不要直接修改自动生成的文件(否则下次脚手架更新会覆盖),可以创建一个同名的partial类来扩展:
// 新建ChampionScreenshot.partial.cs文件 public partial class ChampionScreenshot { [JsonIgnore] // 告诉序列化器忽略这个反向导航属性 public Champion Champion { get; set; } }
方案2:全局配置JSON序列化器处理循环引用
如果你不想修改模型,可以在项目配置中全局设置序列化器的循环引用处理规则:
若使用Newtonsoft.Json(ASP.NET Core 3.1及更早版本默认)
在Startup.cs的ConfigureServices方法中添加:
services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
若使用System.Text.Json(.NET 6+默认)
在Program.cs中添加:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; });
原代码问题复盘
.ToListAsync().Result是同步阻塞异步方法,违反了ASP.NET Core异步编程的最佳实践,极易引发死锁。- 手动加载
ChampionScreenshot后,EF Core的上下文跟踪机制会自动填充每个截图的Champion属性,最终导致序列化时出现循环引用。
通过上面的方法,你可以完全基于脚手架生成的模型完成需求,同时解决数据加载和序列化的问题。
内容的提问来源于stack exchange,提问作者José Caballero
相关产品推荐
相关产品推荐

