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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 01:01:18