平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“C#批量导入Excel数据到SQLite数据库的流程步骤”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
理解这一步时,在winforms里实现批量导入Excel数据到SQLite数据库中,兼容xls和xlsx。
导入到MySQL逻辑也一致。

在这个场景下,在VS2022里,右键项目->管理NuGet程序包,搜索同时安装以下依赖包
需安装三个依赖包:
1、ExcelDataReader:读取 Excel 文件
2、ExcelDataReader.DataSet:把 Excel 读成 DataSet
3、System.Text.Encoding.CodePages:读取老式 .xls 文件

Excel第一行必须是标题,标题名要和代码里读取的完全一致

在窗体上放以下两个控件,导入成功后刷新重新加载数据到下面的表格。
控件:Button;名称:btnImport;说明:Text=导入Excel
控件:Lable;名称:importStatus;说明:用来显示导入状态

/// <summary>
/// 批量导入excel
/// </summary>
private void btnImport_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog()) {
ofd.Title = "选择要导入的Excel文件";
ofd.Filter = "Excel文件|*.xls;*.xlsx";
if (ofd.ShowDialog() != DialogResult.OK)
return;
string filePath = ofd.FileName;
try
{
// 1.读取Excel
importStatus.Text = "正在读取Excel...";
Application.DoEvents();
DataTable dt = ReadExcel(filePath);
// 判断读取的excel里是否有数据
if (dt == null || dt.Rows.Count == 0) {
importStatus.Text = "Excel里没有数据";
MessageBox.Show("Excel里没有数据", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// 2.批量导入
importStatus.Text = $"共读取{dt.Rows.Count}行,正在导入...";
Application.DoEvents();
int success = ImportToDatabase(dt);
// 3.显示结果
importStatus.Text = $"导入完成:成功{success}条,失败{dt.Rows.Count - success}条";
MessageBox.Show($"导入成功!n成功:{success}条n失败:{dt.Rows.Count - success}条", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
// 重新加载数据
LoadUserData();
}
catch (Exception ex)
{
importStatus.Text = "导入失败";
MessageBox.Show($"导入失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// 读取 Excel 文件到 DataTable
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private DataTable ReadExcel(string filePath) {
// .NET Framework 读取 .xls 需要注册编码提供程序
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
var result = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
{
UseHeaderRow = true // 第一行作为标题
}
});
if(result.Tables.Count > 0)
return result.Tables[0];
return null;
}
}
}
/// <summary>
/// 批量导入数据库(用事务提高性能)
/// </summary>
/// <param name="dt"></param>
private int ImportToDatabase(DataTable dt) {
int successCount = 0;
int totalCount = dt.Rows.Count;
using (var conn = SqliteHelper.GetConnection()) {
// 打开连接
conn.Open();
// 启动事务
using (var transaction = conn.BeginTransaction())
{
using (var cmd = new SQLiteCommand(conn)) {
cmd.Transaction = transaction;
cmd.CommandText = "INSERT INTO user (username, password, status) VALUES (@username, @password, @status)";
var pUsername = new SQLiteParameter("@username", DbType.String);
var pPassword = new SQLiteParameter("@password", DbType.String);
var pStatus = new SQLiteParameter("@status", DbType.Int32);
cmd.Parameters.AddRange(new[] { pUsername, pPassword, pStatus });
foreach (DataRow row in dt.Rows)
{
try
{
string username = row["用户名"]?.ToString().Trim();
string password = row["密码"]?.ToString().Trim();
string statusStr = row["状态"]?.ToString().Trim();
int status = 0; //未知
if (statusStr == "开启") status = 1;
else if (statusStr == "关闭") status = 2;
else status = 0;
if (string.IsNullOrEmpty(username))
continue;
string passwordHash = BCrypt.Net.BCrypt.HashPassword(password ?? "123456");
pUsername.Value = username;
pPassword.Value = passwordHash;
pStatus.Value = status;
cmd.ExecuteNonQuery();
successCount++;
}
catch (Exception ex)
{
continue;
}
//每100条更新一次进度
if (successCount % 100 == 0)
{
importStatus.Text = $"正在导入:{successCount}/{totalCount}条...";
Application.DoEvents();
}
}
}
transaction.Commit();
}
}
return successCount;
}
加载数据到DataGridView方法
/// <summary>
/// 加载用户数据到 DataGridView
/// </summary>
private void LoadUserData() {
try
{
string sql = "SELECT id, username, CASE status WHEN 1 THEN '开启' WHEN 2 THEN '关闭' ELSE '未知' END AS status FROM user ORDER BY ID ASC";
DataTable dt = SqliteHelper.ExecuteDataTable(sql);
dgvUsers.DataSource = dt;
// 设置标题
if (dgvUsers.Columns.Contains("id"))
dgvUsers.Columns["id"].HeaderText = "编号";
if (dgvUsers.Columns.Contains("username"))
dgvUsers.Columns["username"].HeaderText = "用户名";
if (dgvUsers.Columns.Contains("status"))
dgvUsers.Columns["status"].HeaderText = "状态";
// 添加修改/删除列
// 先移除旧的按钮列,避免重复添加
if (dgvUsers.Columns.Contains("colEdit"))
dgvUsers.Columns.Remove("colEdit");
if (dgvUsers.Columns.Contains("colDelete"))
dgvUsers.Columns.Remove("colDelete");
// 修改按钮列
DataGridViewButtonColumn btnEdit = new DataGridViewButtonColumn();
btnEdit.Name = "colEdit";
btnEdit.HeaderText = "操作";
btnEdit.Text = "修改";
btnEdit.UseColumnTextForButtonValue = true;
btnEdit.Width = 60;
dgvUsers.Columns.Add(btnEdit);
// 删除按钮列
DataGridViewButtonColumn btnDelete = new DataGridViewButtonColumn();
btnDelete.Name = "colDelete";
btnDelete.HeaderText = "操作";
btnDelete.Text = "删除";
btnDelete.UseColumnTextForButtonValue = true;
btnDelete.Width = 60;
dgvUsers.Columns.Add(btnDelete);
}
catch (Exception ex)
{
MessageBox.Show("加载数据失败:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
实际处理时,总的来说,C#导入Excel数据到SQLite适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。