You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ASP.NET Core中同时更新含多层子实体的父实体问题

解决ConsignmentLine关联ConsignmentDoc的同步更新问题

搞定Consignment和ConsignmentLine的同步后,要处理ConsignmentLine关联的ConsignmentDocument其实很简单——只需要在处理每个ConsignmentLine的逻辑里,嵌套处理它的文档集合就行。我帮你梳理一下修改步骤和完整代码:

关键修改点

1. 深度加载ConsignmentLine的关联文档

首先要修改查询现有Consignment的代码,确保加载ConsignmentLine下的ConsignmentDocument集合,否则后续无法处理子级文档的更新:

var existingcon = _context.Consignment
    .Include(cl => cl.ConsignmentLine)
        .ThenInclude(cld => cld.ConsignmentDocument) // 新增这行,深度加载子级文档
    .Include(l => l.Legging)
    .Include(cd=> cd.ConsignmentDocument)
    .SingleOrDefault(c => c.Id == consignment.Id);

2. 嵌套处理ConsignmentLine的文档同步

在处理ConsignmentLine的循环中,加入对ConsignmentDocument的新增、更新、删除逻辑:

  • 对于已存在的ConsignmentLine:先更新行本身,再同步它的文档集合
  • 对于新增的ConsignmentLine:确保文档的外键正确关联到当前Consignment和ConsignmentLine

3. 删除ConsignmentLine时同步删除关联文档

如果数据库未配置外键级联删除,需要手动删除该ConsignmentLine下的所有文档,再删除行本身。

修改后的完整PutConsignment方法

public async Task<IActionResult> PutConsignment(Consignment consignment) {
    var existingcon = _context.Consignment
        .Include(cl => cl.ConsignmentLine)
            .ThenInclude(cld => cld.ConsignmentDocument) // 深度加载ConsignmentLine的文档
        .Include(l => l.Legging)
        .Include(cd=> cd.ConsignmentDocument)
        .SingleOrDefault(c => c.Id == consignment.Id);

    if (existingcon == null || existingcon.Id != consignment.Id) {
        return BadRequest();
    }

    // 更新Consignment主实体
    _context.Entry(existingcon).CurrentValues.SetValues(consignment);

    // 更新或新增ConsignmentLine,同时处理关联的ConsignmentDocument
    foreach (var conline in consignment.ConsignmentLine) {
        var existingline = existingcon.ConsignmentLine
            .Where(x => x.Id == conline.Id)
            .SingleOrDefault();

        if (existingline != null) {
            // 更新现有ConsignmentLine
            _context.Entry(existingline).CurrentValues.SetValues(conline);

            // --- 处理该ConsignmentLine关联的ConsignmentDocument ---
            // 1. 更新或新增文档
            foreach (var newDoc in conline.ConsignmentDocument) {
                var existingDoc = existingline.ConsignmentDocument
                    .Where(d => d.DocumentId == newDoc.DocumentId)
                    .SingleOrDefault();
                
                if (existingDoc != null) {
                    _context.Entry(existingDoc).CurrentValues.SetValues(newDoc);
                } else {
                    // 设置外键,确保关联正确
                    newDoc.ConsignmentLineId = existingline.Id;
                    newDoc.ConsignmentId = existingcon.Id;
                    existingline.ConsignmentDocument.Add(newDoc);
                }
            }

            // 2. 删除不在传入集合中的文档
            foreach (var existingDoc in existingline.ConsignmentDocument.ToList()) {
                if (!conline.ConsignmentDocument.Any(d => d.DocumentId == existingDoc.DocumentId)) {
                    _context.ConsignmentDocument.Remove(existingDoc);
                }
            }
            // --- 文档处理结束 ---
        } else {
            // 新增ConsignmentLine
            existingcon.ConsignmentLine.Add(conline);
            // 确保新增行的文档外键正确
            foreach (var doc in conline.ConsignmentDocument) {
                doc.ConsignmentId = existingcon.Id;
                doc.ConsignmentLineId = conline.Id;
            }
        }
    }

    // 删除ConsignmentLine,同时删除关联文档
    foreach (var line in existingcon.ConsignmentLine.ToList()) {
        if(!consignment.ConsignmentLine.Any(x=> x.Id == line.Id)) {
            // 先删除该Line下的所有文档
            foreach (var doc in line.ConsignmentDocument.ToList()) {
                _context.ConsignmentDocument.Remove(doc);
            }
            _context.ConsignmentLine.Remove(line);
        }
    }

    // 原有Legging处理逻辑(保持不变)
    foreach (var newleg in consignment.Legging) {
        var leg = existingcon.Legging
            .Where(x => x.Id == newleg.Id)
            .SingleOrDefault();
        if (leg != null) {
            _context.Entry(leg).CurrentValues.SetValues(newleg);
        } else existingcon.Legging.Add(newleg);
    }

    foreach (var leg in existingcon.Legging.ToList()) {
        if(!consignment.Legging.Any(x=> x.Id == leg.Id)) {
            _context.Legging.Remove(leg);
        }
    }

    // 原有Consignment直接关联的文档处理逻辑(保持不变)
    foreach (var newdoc in consignment.ConsignmentDocument) {
        var doc = existingcon.ConsignmentDocument
            .Where(x => x.DocumentId == newdoc.DocumentId)
            .SingleOrDefault();
        if (doc != null) {
            _context.Entry(doc).CurrentValues.SetValues(newdoc);
        } else existingcon.ConsignmentDocument.Add(newdoc);
    }

    foreach (var doc in existingcon.ConsignmentDocument.ToList()) {
        if (!consignment.ConsignmentDocument.Any(x => x.DocumentId == doc.DocumentId)) {
            _context.ConsignmentDocument.Remove(doc);
        }
    }

    try {
        await _context.SaveChangesAsync();
    } catch (DbUpdateConcurrencyException) {
        if (!ConsignmentExists(consignment.Id)) {
            return NotFound();
        } else {
            throw;
        }
    }

    return Ok(consignment);
}

额外说明

  • 如果你的数据库已经为ConsignmentLineId外键配置了级联删除(ON DELETE CASCADE),那么删除ConsignmentLine时可以省略手动删除文档的步骤,数据库会自动处理。
  • 注意确保传入的ConsignmentLine对象包含完整的ConsignmentDocument集合,否则会误删未传入的文档。

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

火山引擎 最新活动