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

使用EPPlus导入Excel更新数据库已有行时新增重复行如何解决

问题根因

导入时出现重复新增、不执行更新,是代码存在以下逻辑错误:

  • Excel字段映射写错:重复给gender字段赋值两次(分别取第5、第6列),实体的year字段完全没有从Excel读取,所有导入对象的year都是int默认值0,直接导致后续年份比对逻辑失效
  • EF更新用法错误:传给UpdateRange的是手动new的游离对象,没有数据库主键id,EF无法关联到已存在的数据库行,执行时会默认当作新数据插入
  • 查询逻辑冗余低效:两次执行Count()往返数据库做判断,性能差还容易出现数据不一致
  • 无效校验:ModelState.IsValid只校验请求表单绑定的参数,手动从Excel解析的实体不会走这个校验,这个判断完全不生效
  • 缺少空值防护:Excel单元格为空时直接调用ToString()会触发空引用异常
修复后代码
public class Insured
{
    [Key]
    public int id { get; set; }

    [Required]
    public string identifier { get; set; }

    public string policyno { get; set; }

    public string firstname { get; set; }
    public string lastname { get; set; }
    public string gender { get; set; }
    public int year { get; set; }
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ImportExcelFile(IFormFile ExcelFile)
{
    ViewBag.Message = "";
    if (ExcelFile == null || ExcelFile.Length == 0)
    {
        ViewBag.Message = "请上传有效的Excel文件";
        return View();
    }

    var importList = new List<Insured>();
    using (var stream = new MemoryStream())
    {
        await ExcelFile.CopyToAsync(stream);
        using (var package = new ExcelPackage(stream))
        {
            // 判空工作表,避免找不到Sheet1抛错
            var worksheet = package.Workbook.Worksheets["Sheet1"];
            if (worksheet?.Dimension == null)
            {
                ViewBag.Message = "Excel文件内容为空或格式不符合要求";
                return View();
            }

            var rowCount = worksheet.Dimension.Rows;
            for (int row = 2; row <= rowCount; row++)
            {
                // 空值防护,处理单元格为空的场景
                var identifier = worksheet.Cells[row, 1].Value?.ToString().Trim().ToLower();
                var policyno = worksheet.Cells[row, 2].Value?.ToString().Trim();
                // 跳过必填项为空的无效行
                if (string.IsNullOrWhiteSpace(identifier) || string.IsNullOrWhiteSpace(policyno))
                    continue;

                var insured = new Insured
                {
                    identifier = identifier,
                    policyno = policyno,
                    firstname = worksheet.Cells[row, 3].Value?.ToString().Trim() ?? string.Empty,
                    lastname = worksheet.Cells[row, 4].Value?.ToString().Trim() ?? string.Empty,
                    gender = worksheet.Cells[row, 5].Value?.ToString().Trim() ?? string.Empty,
                    // 修正原代码错误:第6列对应year字段,增加类型转换容错
                    year = int.TryParse(worksheet.Cells[row, 6].Value?.ToString().Trim(), out var y) ? y : 0
                };
                importList.Add(insured);
            }
        }
    }

    // 批量处理导入数据
    foreach (var item in importList)
    {
        // 单次查询取出匹配的已存在记录,替代两次Count冗余查询
        var existRecord = _db.dbLifeData.FirstOrDefault(o => 
            o.identifier == item.identifier 
            && o.policyno == item.policyno);

        if (existRecord != null)
        {
            // 匹配到已有记录,按业务规则判断年份满足才更新
            if (existRecord.year <= item.year)
            {
                // 直接修改EF跟踪的已有实体属性,不要传new的游离对象给Update
                existRecord.firstname = item.firstname;
                existRecord.lastname = item.lastname;
                existRecord.gender = item.gender;
                existRecord.year = item.year;
                // 实体已被上下文跟踪,不需要显式调用Update,改值后SaveChanges自动生成Update语句
            }
        }
        else
        {
            // 不存在匹配记录才新增
            _db.dbLifeData.Add(item);
        }
    }
    // 循环外统一提交,减少数据库连接开销,EF默认包裹事务,保证数据一致性
    await _db.SaveChangesAsync();
    ViewBag.Message = $"导入完成,共处理{importList.Count}条数据";
    return View();
}
关键调整说明
  • 修正Excel列和实体字段的映射错误,补全year字段的读取逻辑,增加空值和类型转换容错处理
  • 废弃冗余的两次Count查询,单次数据库查询直接取出匹配的已存在实体,性能更高
  • 更新逻辑直接操作EF上下文跟踪的数据库已存在实体,EF可以正确识别变更,生成UPDATE语句而不是INSERT语句,从根源避免重复插入
  • 移除无效的ModelState.IsValid判断,增加必填字段非空校验,自动跳过无效行
  • 将SaveChanges移到循环外批量提交,减少数据库IO开销
  • 补充文件、工作表为空的边界场景处理,避免运行时异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:24:20