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

使用NPOI读取Excel存入MySQL时数据丢失且报空引用,求排查

问题排查与解决方案

核心问题分析

你的代码存在三个关键问题,直接导致空引用异常和数据丢失:

1. 未处理空行/空单元格引发空引用

  • Excel里的空白行,sheet.GetRow(i)会返回null,直接调用curRow.GetCell()必然触发空引用。
  • 即使行存在,部分单元格可能未被初始化(比如手动删除内容但未清除单元格),curRow.GetCell(index)返回null,直接访问StringCellValue或NumericCellValue也会报错。
  • 循环范围错误:sheet.LastRowNum是最后一行的索引(从0开始),你加1后用i <= rowCount循环,会超出有效行范围,访问不存在的行导致curRow为null。

2. 异步方法未等待导致数据丢失

_context.SaveChangesAsync()是异步方法,直接调用不使用await,会让数据库操作还没完成就进入下一次循环,引发并发冲突或操作中断,最终部分数据没插入成功。

3. 单元格类型未校验存在潜在错误

直接强制转换单元格值(比如(float)curRow.GetCell(6).NumericCellValue),如果单元格实际是字符串格式的数字,会抛出类型转换异常,中断后续数据插入。


修复后的代码

using ExcelRead.Data;
using ExcelRead.Models;
using Microsoft.AspNetCore.Mvc;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System.Collections.Generic;
using System.IO;

namespace ExcelRead.Controllers
{
    public class HomeController : Controller
    {
        private readonly ApplicationDbContext _context;

        public HomeController(ApplicationDbContext context)
        {
            _context = context;
        }

        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Excl()
        {
            string filePath = @"C:\Users\zameer.ahammad\Documents\TimeCards\FieldGlass\timeSheet.supplier.list.xlsx";
            try
            {
                using FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                IWorkbook workbook = new XSSFWorkbook(fs);
                ISheet sheet = workbook.GetSheet("timeSheet.supplier.list");
                
                if (sheet == null)
                {
                    Console.WriteLine("指定工作表不存在");
                    return View("Index");
                }

                int lastRowIndex = sheet.LastRowNum;
                var dataList = new List<fieldglassTimesheetDataModel>();

                // 循环范围修正:从第3行(索引2)到最后一行(LastRowNum)
                for (int i = 2; i <= lastRowIndex; i++)
                {
                    IRow curRow = sheet.GetRow(i);
                    // 跳过空行
                    if (curRow == null) continue;

                    var excelInsert = new fieldglassTimesheetDataModel();

                    // 处理ID单元格:确保非空并转换为字符串
                    var idCell = curRow.GetCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    excelInsert.ID = idCell.ToString()?.Trim() ?? string.Empty;

                    // 处理Worker单元格
                    var workerCell = curRow.GetCell(3, MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    excelInsert.Worker = workerCell.ToString()?.Trim() ?? string.Empty;

                    // 安全获取数值类型单元格的值
                    excelInsert.ST = GetCellNumericValue(curRow.GetCell(6, MissingCellPolicy.CREATE_NULL_AS_BLANK));
                    excelInsert.OT = GetCellNumericValue(curRow.GetCell(7, MissingCellPolicy.CREATE_NULL_AS_BLANK));
                    excelInsert.DT = GetCellNumericValue(curRow.GetCell(8, MissingCellPolicy.CREATE_NULL_AS_BLANK));

                    dataList.Add(excelInsert);
                }

                // 批量插入,减少数据库交互次数
                if (dataList.Count > 0)
                {
                    await _context.AddRangeAsync(dataList);
                    await _context.SaveChangesAsync();
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine($"错误详情:{exception.Message}\n堆栈跟踪:{exception.StackTrace}");
            }
            return View("Index");
        }

        // 辅助方法:兼容不同类型单元格的数值转换
        private float GetCellNumericValue(ICell cell)
        {
            if (cell == null) return 0f;
            
            return cell.CellType switch
            {
                CellType.Numeric => (float)cell.NumericCellValue,
                CellType.String => float.TryParse(cell.StringCellValue, out float val) ? val : 0f,
                _ => 0f
            };
        }
    }
}

关键修复点说明

  • 空值防护:用MissingCellPolicy.CREATE_NULL_AS_BLANK确保单元格不会返回null,同时添加空值判断避免报错。
  • 异步等待:将方法改为async Task<IActionResult>,对所有异步数据库操作加await,确保操作完成后再执行下一步。
  • 批量保存:先把数据存入集合,最后一次性插入,既提升效率又避免部分数据插入失败的问题。
  • 类型兼容:新增辅助方法处理不同类型的单元格,避免强制转换异常。
  • 循环范围修正:直接用LastRowNum作为循环上限,避免访问不存在的行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 00:48:20