使用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
相关产品推荐
相关产品推荐

