You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core保存含重复关联实体的Schedule时的跟踪错误优化方案

更优解决方案

1. 仅传递FilmId(推荐)

既然Film已存在于数据库,无需传递完整实体,只保留FilmId即可。EF Core会通过外键自动关联现有Film,不会创建重复跟踪实例。

修改后的输入JSON:

{
    "Id": 0,
    "Date": "10.06.2023",
    "Sessions": [
        {
            "Id": 0,
            "Time": "10:00",
            "ScheduleId": 0,
            "FilmId": 11
        },
        {
            "Id": 0,
            "Time": "12:00",
            "ScheduleId": 0,
            "FilmId": 11
        }
    ]
}

接口代码无需修改,直接调用_context.Add(schedule)即可完成关联。

2. 合并重复的Film实例(适配前端必须传完整实体的场景)

如果前端必须传递完整Film对象,可在添加到上下文前,将所有Session的Film引用指向同一个实例,确保EF仅跟踪一个对象。

修改接口代码:

[HttpPost]
[Consumes("application/json")]
public async Task<IActionResult> Add([FromBody] Schedule schedule)
{
    if (!ModelState.IsValid)
    {
        return Json(new { success = false });
    }

    // 按Film Id分组,合并相同Id的Film引用
    var filmGroups = schedule.Sessions
        .Where(s => s.Film != null && s.Film.Id > 0)
        .GroupBy(s => s.Film.Id);
    
    foreach (var group in filmGroups)
    {
        var sharedFilm = group.First().Film;
        foreach (var session in group.Skip(1))
        {
            session.Film = sharedFilm;
        }
    }

    _context.Add(schedule);
    await _context.SaveChangesAsync();
    return Json(new { success = true, model = schedule });
}

3. 手动附加现有Film实例(确保数据一致性)

如果需要确保使用数据库中最新的Film数据,可先从上下文获取或附加Film,再让Session引用该实例。

修改接口代码:

[HttpPost]
[Consumes("application/json")]
public async Task<IActionResult> Add([FromBody] Schedule schedule)
{
    if (!ModelState.IsValid)
    {
        return Json(new { success = false });
    }

    // 获取所有唯一的Film Id
    var uniqueFilmIds = schedule.Sessions.Select(s => s.FilmId).Distinct();
    foreach (var filmId in uniqueFilmIds)
    {
        // 检查上下文是否已跟踪该Film,未跟踪则从数据库获取
        var trackedFilm = _context.Films.Local.FirstOrDefault(f => f.Id == filmId);
        var targetFilm = trackedFilm ?? await _context.Films.FindAsync(filmId);

        if (targetFilm == null)
        {
            return Json(new { success = false, message = $"Film {filmId}不存在" });
        }

        // 让所有对应Session引用这个已跟踪的实例
        foreach (var session in schedule.Sessions.Where(s => s.FilmId == filmId))
        {
            session.Film = targetFilm;
        }
    }

    _context.Add(schedule);
    await _context.SaveChangesAsync();
    return Json(new { success = true, model = schedule });
}

临时方案的弊端
  • Detach方案:可能破坏上下文对Film的跟踪状态,导致后续操作出现数据不一致问题。
  • 置空Film方案:后续访问Session.Film时会触发NullReferenceException,影响代码健壮性。

内容的提问来源于stack exchange,提问作者Alfredo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:06:22