使用MVC5+EF6+SqlBulkCopy导入Excel至SQL遇三类错误求助
解决Excel批量导入SQL时的三类错误(MVC5 + EF6 + EPPlus + SqlBulkCopy)
我来帮你逐个排查并解决这三个问题,让你的批量导入功能正常跑起来:
1. 无法将DbConnection转换为SqlConnection的问题
原因
SqlBulkCopy只接受**SqlConnection类型的连接实例,但EF的Database.Connection返回的是通用的DbConnection抽象类型,直接传递会触发类型转换错误。另外,SqlBulkCopy要求连接处于打开状态**才能工作。
解决方法
- 把
DbConnection强制转换为SqlConnection(你的EF模型是SQL Server的,所以转换是安全的) - 保存连接的原始状态,使用后恢复(避免破坏EF自身的连接管理逻辑)
2. SqlBulkCopy被当作命名空间使用的问题
原因
这个错误通常是因为:
- 代码文件顶部没引用
System.Data.SqlClient命名空间 - 自定义的
BulkWriter类里错误地用命名空间代替了SqlBulkCopy实例 - 存在命名冲突(比如自定义了同名的类)
解决方法
- 确保添加
using System.Data.SqlClient; - 在
BulkWriter中正确实例化SqlBulkCopy对象,而不是直接调用命名空间
3. ObjectReader相关问题
原因
SqlBulkCopy需要IDataReader作为数据源,我们一般用FastMember库的ObjectReader把List<T>转成IDataReader。出错大概率是:
- 没安装FastMember NuGet包
- 没引用
FastMember命名空间 ObjectReader的使用语法错误
解决方法
- 通过NuGet安装FastMember:
Install-Package FastMember - 添加
using FastMember;命名空间 - 用
ObjectReader.Create(你的列表)生成数据读取器
修正后的完整代码
下面是整合所有修复点的实现,包括补全你用到的BulkWriter类:
首先添加必要的命名空间:
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Threading; using System.Threading.Tasks; using System.Web.Mvc; using EPPlus; using FastMember;
然后是修复后的BulkWriter类:
public class BulkWriter { public async Task InsertAsync<T>(List<T> data, string tableName, DbConnection connection, CancellationToken cancellationToken) { // 转换为SqlConnection if (connection is not SqlConnection sqlConnection) { throw new ArgumentException("仅支持SQL Server连接"); } // 保存连接原始状态,使用后恢复 var wasOpen = sqlConnection.State == ConnectionState.Open; try { if (!wasOpen) { await sqlConnection.OpenAsync(cancellationToken); } // 用FastMember生成IDataReader using var reader = ObjectReader.Create(data); using var bulkCopy = new SqlBulkCopy(sqlConnection) { DestinationTableName = tableName, BatchSize = 1000 // 批量大小可根据数据量调整 }; await bulkCopy.WriteToServerAsync(reader, cancellationToken); } finally { // 恢复连接初始状态 if (!wasOpen && sqlConnection.State == ConnectionState.Open) { await sqlConnection.CloseAsync(); } } } }
最后是你的Controller方法修正:
public async Task<ActionResult> ApplicationAsync(FormCollection formCollection) { var usersList = new List<bomApplicationImportTgt>(); if (Request != null) { HttpPostedFileBase file = Request.Files["UploadedFile"]; if ((file != null) && (file.ContentLength > 0) && !string.IsNullOrEmpty(file.FileName)) { string fileName = file.FileName; using (var package = new ExcelPackage(file.InputStream)) { var workSheet = package.Workbook.Worksheets.First(); var noOfRow = workSheet.Dimension.End.Row; for (int rowIterator = 2; rowIterator <= noOfRow; rowIterator++) { // 这里可以添加空值容错,比如用int.TryParse代替Convert.ToInt32 var user = new bomApplicationImportTgt { date = Convert.ToDateTime(workSheet.Cells[rowIterator, 1].Value), Description = workSheet.Cells[rowIterator, 2].Value?.ToString(), SequenceNumber = Convert.ToInt32(workSheet.Cells[rowIterator, 3].Value), PartNumber = workSheet.Cells[rowIterator, 4].Value?.ToString(), PartsName = workSheet.Cells[rowIterator, 5].Value?.ToString(), SP = workSheet.Cells[rowIterator, 6].Value?.ToString(), INT = workSheet.Cells[rowIterator, 7].Value?.ToString(), SN = workSheet.Cells[rowIterator, 8].Value?.ToString(), SZ = workSheet.Cells[rowIterator, 9].Value?.ToString(), C = workSheet.Cells[rowIterator, 10].Value?.ToString(), E_F = workSheet.Cells[rowIterator, 11].Value?.ToString(), Block = workSheet.Cells[rowIterator, 12].Value?.ToString(), SEC = workSheet.Cells[rowIterator, 13].Value?.ToString(), Item = workSheet.Cells[rowIterator, 14].Value?.ToString(), SUF = workSheet.Cells[rowIterator, 15].Value?.ToString(), Model = workSheet.Cells[rowIterator, 16].Value?.ToString(), M_E_F = workSheet.Cells[rowIterator, 17].Value?.ToString(), OP = workSheet.Cells[rowIterator, 18].Value?.ToString(), Type = workSheet.Cells[rowIterator, 19].Value?.ToString(), Quantity = workSheet.Cells[rowIterator, 20].Value?.ToString(), PLGRPCD = workSheet.Cells[rowIterator, 21].Value?.ToString(), PL1 = workSheet.Cells[rowIterator, 22].Value?.ToString(), ATC1 = workSheet.Cells[rowIterator, 23].Value?.ToString(), PL2 = workSheet.Cells[rowIterator, 24].Value?.ToString(), ATC2 = workSheet.Cells[rowIterator, 25].Value?.ToString(), PL3 = workSheet.Cells[rowIterator, 26].Value?.ToString(), ATC3 = workSheet.Cells[rowIterator, 27].Value?.ToString(), Plant = workSheet.Cells[rowIterator, 28].Value?.ToString(), SHR = workSheet.Cells[rowIterator, 29].Value?.ToString(), DC_Number = workSheet.Cells[rowIterator, 30].Value?.ToString(), FileName = fileName }; usersList.Add(user); } } } } // 只有当列表有数据时才执行批量导入 if (usersList.Any()) { using (Dev_Purchasing_New_ModelEntities excelImportDBEntities = new Dev_Purchasing_New_ModelEntities()) { await new BulkWriter().InsertAsync(usersList, "bomApplicationImportTgt", excelImportDBEntities.Database.Connection, CancellationToken.None); } } return View("Application"); }
额外提示
- 确保
bomApplicationImportTgt实体的属性名和数据库表的列名完全一致(大小写不敏感,但匹配度越高越不容易出问题),如果不一致需要手动添加bulkCopy.ColumnMappings.Add("属性名", "列名") - Excel单元格空值处理可以优化,比如用
int.TryParse(workSheet.Cells[rowIterator,3].Value?.ToString(), out var seqNum)然后赋值user.SequenceNumber = seqNum,避免转换异常 BatchSize可以根据你的数据量调整,1000-5000是比较常用的取值,平衡内存占用和导入速度
内容的提问来源于stack exchange,提问作者Minhal
相关产品推荐
相关产品推荐

