ASP.NET Core 8 MVC:如何从SQL查询导出带类型数据至Excel
解决ASP.NET Core MVC中带类型导出Excel的问题
方案一:基于IDataReader处理类型并赋值
直接转字符串会丢失所有类型信息,而直接赋值dr.GetValue(j)的问题主要是未处理DBNull.Value,同时需要根据字段类型明确转换值,确保Excel识别原生类型。以下是修正后的代码:
public async Task<IActionResult> RawData() { using var workbook = new XLWorkbook(); var worksheet = workbook.Worksheets.Add("Test"); using IDataReader dr = ExecuteReader("select * from foo", out IDbConnection _); // 写入表头(提升Excel可读性) for (int j = 0; j < dr.FieldCount; j++) { worksheet.Cell(1, j + 1).Value = dr.GetName(j); worksheet.Cell(1, j + 1).Style.Font.Bold = true; } int currentRow = 2; while (dr.Read()) { for (var j = 0; j < dr.FieldCount; j++) { if (dr.GetFieldType(j) == typeof(byte[])) continue; // 跳过二进制数据 object value = dr.IsDBNull(j) ? null : dr.GetValue(j); var cell = worksheet.Cell(currentRow, j + 1); // 根据字段类型赋值,确保Excel识别正确类型 switch (Type.GetTypeCode(dr.GetFieldType(j))) { case TypeCode.Boolean: cell.Value = (bool?)value; break; case TypeCode.Byte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: cell.Value = Convert.ToInt64(value); break; case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: cell.Value = Convert.ToDecimal(value); break; case TypeCode.DateTime: cell.Value = (DateTime?)value; break; default: // 字符串、GUID等其他类型 cell.Value = value?.ToString(); break; } } currentRow++; } using var stream = new MemoryStream(); workbook.SaveAs(stream); stream.Position = 0; // 重置流位置,避免文件损坏 return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "test.xlsx"); }
关键优化点
- 新增表头写入,提升导出文件的可读性
- 用
dr.IsDBNull(j)判断空值,避免DBNull.Value直接赋值导致的异常 - 通过
TypeCode区分数据类型,将值转换为对应类型后赋值,保证Excel识别数字、日期、布尔等原生类型 - 重置MemoryStream位置,防止导出文件损坏
方案二:使用EF Core FromSQLRawAsync结合动态类型
如果应用已使用Entity Framework Core,可以利用FromSQLRawAsync查询动态数据,自动保留原始类型,无需手动判断字段类型:
using Microsoft.EntityFrameworkCore; public async Task<IActionResult> RawDataWithEF() { using var workbook = new XLWorkbook(); var worksheet = workbook.Worksheets.Add("Test"); // 执行原生SQL查询,返回动态对象列表 var data = await _dbContext.Database.FromSqlRaw("select * from foo") .AsEnumerable() .ToListAsync(); if (!data.Any()) return File(new byte[0], "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "empty.xlsx"); // 获取属性名并写入表头 var properties = data.First().GetType().GetProperties(); for (int i = 0; i < properties.Length; i++) { worksheet.Cell(1, i + 1).Value = properties[i].Name; worksheet.Cell(1, i + 1).Style.Font.Bold = true; } // 写入数据行 int currentRow = 2; foreach (var item in data) { for (int i = 0; i < properties.Length; i++) { var value = properties[i].GetValue(item); var cell = worksheet.Cell(currentRow, i + 1); // 处理DBNull,直接赋值保留原始类型 cell.Value = value == DBNull.Value ? null : value; } currentRow++; } using var stream = new MemoryStream(); workbook.SaveAs(stream); stream.Position = 0; return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "test_ef.xlsx"); }
关键说明
- 需要注入你的DbContext实例(
_dbContext) FromSQLRawAsync返回的动态对象会自动保留数据库原始类型,无需手动判断转换- 同样处理
DBNull转null的逻辑,避免Excel识别异常
内容的提问来源于stack exchange,提问作者Andrus
相关产品推荐
相关产品推荐

