如何在ASP.NET MVC启动时自动导入本地Excel数据至数据库
ASP.NET MVC 启动自动导入Excel至数据库实现方案
1. 选择Excel读取库
优先选用EPPlus(支持.xlsx,NuGet直接安装EPPlus)或NPOI(兼容.xls/.xlsx),无需前端上传,直接读取本地文件。
2. 编写Excel读取工具类
实现多文件、多工作表遍历逻辑,将Excel数据转为通用字典格式便于后续处理:
public class ExcelImporter { private readonly string _excelDir; public ExcelImporter(string excelDir) { _excelDir = excelDir; Directory.CreateDirectory(_excelDir); // 确保目录存在 } // 获取目录下所有Excel文件 public IEnumerable<FileInfo> GetAllExcelFiles() { var dirInfo = new DirectoryInfo(_excelDir); return dirInfo.GetFiles("*.xlsx").Concat(dirInfo.GetFiles("*.xls")); } // 读取单个文件的所有工作表数据 public IEnumerable<(string SheetName, List<Dictionary<string, object>> Data)> ReadExcel(FileInfo file) { ExcelPackage.LicenseContext = LicenseContext.NonCommercial; // EPPlus 5+ 需设置许可证 using var package = new ExcelPackage(file); foreach (var sheet in package.Workbook.Worksheets) { if (sheet.Dimension is null) continue; // 跳过空工作表 // 读取表头(第一行) var headers = Enumerable.Range(1, sheet.Dimension.End.Column) .Select(col => sheet.Cells[1, col].Text.Trim()) .ToList(); // 读取数据行(从第二行开始) var rows = new List<Dictionary<string, object>>(); for (int row = 2; row <= sheet.Dimension.End.Row; row++) { var rowData = new Dictionary<string, object>(); for (int col = 1; col <= sheet.Dimension.End.Column; col++) { rowData[headers[col - 1]] = sheet.Cells[row, col].Value; } rows.Add(rowData); } yield return (sheet.Name, rows); } } }
3. Entity Framework 自动建表与数据插入
场景1:已知Excel结构(预定义实体)
如果Excel结构固定,先定义实体类与DbContext:
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public class AppDbContext : DbContext { public DbSet<Product> Products { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlServer("你的数据库连接字符串"); } }
导入时直接映射实体并插入,EF会自动创建表(需确保Database.EnsureCreated()执行):
private void ImportFixedData(List<Dictionary<string, object>> excelData) { using var db = new AppDbContext(); db.Database.EnsureCreated(); // 自动创建数据库与表 var products = excelData.Select(row => new Product { Id = Convert.ToInt32(row["Id"]), Name = row["Name"].ToString(), Price = Convert.ToDecimal(row["Price"]) }); db.Products.AddRange(products); db.SaveChanges(); }
场景2:动态根据Excel表头建表
若Excel结构不固定,通过动态SQL创建表并插入数据(注意SQL注入风险,本地文件场景可控):
private void CreateDynamicTable(string tableName, List<string> headers) { using var db = new AppDbContext(); // 生成建表SQL,默认字段为NVARCHAR(MAX),可根据Excel数据类型调整 var createSql = $"CREATE TABLE [{tableName}] (" + string.Join(", ", headers.Select(h => $"[{h}] NVARCHAR(MAX)")) + ")"; db.Database.ExecuteSqlRaw(createSql); } private void InsertDynamicData(string tableName, List<Dictionary<string, object>> data) { if (!data.Any()) return; using var db = new AppDbContext(); var headers = data.First().Keys.ToList(); var paramNames = headers.Select((_, idx) => $"@p{idx}").ToList(); var insertSql = $"INSERT INTO [{tableName}] ({string.Join(", ", headers.Select(h => $"[{h}]"))}) VALUES ({string.Join(", ", paramNames)})"; // 参数化插入避免SQL注入 foreach (var row in data) { var parameters = headers.Select((h, idx) => new SqlParameter($"@p{idx}", row[h] ?? DBNull.Value)).ToArray(); db.Database.ExecuteSqlRaw(insertSql, parameters); } }
4. 应用启动时触发导入
在Global.asax.cs的Application_Start方法中执行导入逻辑:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // 执行Excel导入 RunExcelImport(); } private void RunExcelImport() { try { var excelDir = Server.MapPath("~/App_Data/Excels"); // 存放Excel的目录 var importer = new ExcelImporter(excelDir); foreach (var file in importer.GetAllExcelFiles()) { var sheetDataList = importer.ReadExcel(file).ToList(); foreach (var (sheetName, data) in sheetDataList) { if (!data.Any()) continue; // 生成唯一表名,避免冲突 var tableName = $"Import_{Path.GetFileNameWithoutExtension(file.Name)}_{sheetName}".Replace(" ", "_"); using var db = new AppDbContext(); // 检查表是否已存在 var tableExists = db.Database.ExecuteSqlRaw( $"SELECT CASE WHEN EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{tableName}') THEN 1 ELSE 0 END") == 1; if (!tableExists) { var headers = data.First().Keys.ToList(); CreateDynamicTable(tableName, headers); } InsertDynamicData(tableName, data); } } } catch (Exception ex) { // 记录日志,避免导入失败导致应用启动异常 System.Diagnostics.Trace.WriteLine($"Excel导入失败:{ex.Message}"); } }
5. 关键注意事项
- 权限配置:确保应用程序池身份对Excel目录有读取权限,对数据库有建表、插入权限。
- 重复导入:可新增
ImportLog表记录已导入的文件/工作表,启动时跳过已导入内容。 - 性能优化:大数据量时需分批插入,避免一次性提交过多数据。
- 异常处理:必须添加try-catch,防止导入失败导致应用无法启动。
内容的提问来源于stack exchange,提问作者joeOla
相关产品推荐
相关产品推荐

