【.NET7 WinForm 实战】三层架构+EF Core+多数据库+完整功能(源码+教程+脚本)
今天给大家带来一篇全网最完整的 .NET7 WinForm 三层架构实战教程,涵盖「EF Core+SQL Server/MySQL+Autofac+全局异常+增删改查+搜索+分页+Excel导出」,所有代码可直接复制运行,配套源码、数据库脚本、界面布局图,小白也能轻松上手,可直接作为课设/毕设/实习项目。
一、项目核心亮点
✅ 标准三层架构 :UI层(窗体)+ BLL层(业务)+ DAL层(数据),解耦易维护
✅ 双数据库支持 :EF Core 适配 SQL Server/MySQL,一键切换
✅ 完整功能闭环 :新增/编辑/删除/查询/模糊搜索/分页/Excel导出
✅ 企业级特性 :Autofac依赖注入+全局异常处理+日志+界面自适应
✅ 零基础友好:逐行注释+图文教程+配套源码,复制即用
二、技术栈清单
| 技术/框架 | 版本 | 用途 |
|---|---|---|
| .NET | 7.0 | WinForm 运行框架 |
| EF Core | 7.0 | 数据库ORM框架 |
| SQL Server | 2019+ | 关系型数据库(主版本) |
| MySQL | 8.0+ | 关系型数据库(备选) |
| Autofac | 7.1.0 | 依赖注入容器 |
| NPOI | 2.6.0 | Excel导出 |
| Serilog | 3.1.1 | 日志记录 |
三、项目完整结构
WinFormThreeLayerDemo/
├─ Core/ // 核心层:实体/枚举/公共模型
│ ├─ Entities/ // 数据库实体(User)
│ └─ Models/ // 公共模型(分页、导出参数)
├─ BusinessLogic/ // 业务逻辑层(BLL)
│ ├─ Interfaces/ // 服务接口(IUserService)
│ └─ Services/ // 服务实现(UserService)
├─ DataAccess/ // 数据访问层(DAL)
│ ├─ Contexts/ // EF Core上下文(支持SQL Server/MySQL)
│ ├─ Interfaces/ // 仓储接口(IUserRepository)
│ └─ Repositories/ // 仓储实现(UserRepository)
├─ Infrastructure/ // 基础设施层
│ ├─ Dependency/ // Autofac配置
│ ├─ Exception/ // 全局异常处理
│ ├─ Extensions/ // 扩展方法(Excel导出)
│ └─ Logging/ // 日志配置
├─ Resources/ // 资源文件(可选)
└─ Forms/ // UI层(主窗体+分页控件)
└─ MainForm.cs // 核心窗体(所有功能入口)
四、环境准备
4.1 NuGet包安装(必装)
打开VS2022 → 右键项目 → 管理NuGet程序包 → 安装以下包:
bash
# EF Core 核心
Microsoft.EntityFrameworkCore 7.0.0
# SQL Server 驱动
Microsoft.EntityFrameworkCore.SqlServer 7.0.0
# MySQL 驱动(Pomelo)
Pomelo.EntityFrameworkCore.MySql 7.0.0
# Autofac
Autofac 7.1.0
# Excel导出
NPOI 2.6.0
# 日志
Serilog 3.1.1
Serilog.Sinks.File 5.0.0
4.2 数据库准备
- SQL Server :新建空数据库(命名:
UserManagementDB) - MySQL :新建空数据库(命名:
user_management_db,字符集:utf8mb4)
五、完整代码实现(复制即用)
5.1 Core层:实体与公共模型
5.1.1 核心实体(User)
Core/Entities/User.cs
csharp
using System;
namespace WinFormThreeLayerDemo.Core.Entities
{
/// <summary>
/// 用户实体(对应数据库Users表)
/// </summary>
public class User
{
/// <summary>
/// 主键ID
/// </summary>
public Guid Id { get; set; } = Guid.NewGuid();
/// <summary>
/// 用户名
/// </summary>
public string Username { get; set; } = string.Empty;
/// <summary>
/// 邮箱
/// </summary>
public string Email { get; set; } = string.Empty;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; } = DateTime.Now;
}
}
5.1.2 分页模型
Core/Models/PaginationModel.cs
csharp
using System;
using System.Collections.Generic;
namespace WinFormThreeLayerDemo.Core.Models
{
/// <summary>
/// 分页查询参数
/// </summary>
public class PaginationQuery
{
/// <summary>
/// 当前页码(默认第1页)
/// </summary>
public int PageIndex { get; set; } = 1;
/// <summary>
/// 每页条数(默认10条)
/// </summary>
public int PageSize { get; set; } = 10;
/// <summary>
/// 搜索关键词(可选)
/// </summary>
public string Keyword { get; set; } = string.Empty;
}
/// <summary>
/// 分页返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
public class PaginationResult<T>
{
/// <summary>
/// 总条数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int TotalPages { get; set; }
/// <summary>
/// 当前页数据
/// </summary>
public List<T> Data { get; set; } = new List<T>();
}
}
5.2 DataAccess层:数据访问(EF Core+双数据库)
5.2.1 EF Core上下文(支持SQL Server/MySQL)
DataAccess/Contexts/UserDbContext.cs
csharp
using Microsoft.EntityFrameworkCore;
using WinFormThreeLayerDemo.Core.Entities;
namespace WinFormThreeLayerDemo.DataAccess.Contexts
{
/// <summary>
/// EF Core数据库上下文(支持SQL Server/MySQL切换)
/// </summary>
public class UserDbContext : DbContext
{
/// <summary>
/// 映射Users表
/// </summary>
public DbSet<User> Users => Set<User>();
/// <summary>
/// 配置数据库连接(切换注释即可换数据库)
/// </summary>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// ========== 方案1:SQL Server 连接 ==========
string sqlServerConn = "Data Source=.;Initial Catalog=UserManagementDB;User ID=sa;Password=你的SQL密码;TrustServerCertificate=True;";
optionsBuilder.UseSqlServer(sqlServerConn);
// ========== 方案2:MySQL 连接 ==========
// string mySqlConn = "server=localhost;port=3306;database=user_management_db;uid=root;pwd=你的MySQL密码;charset=utf8mb4;";
// optionsBuilder.UseMySql(mySqlConn, ServerVersion.AutoDetect(mySqlConn));
}
/// <summary>
/// 配置实体映射(可选,EF Core可自动识别)
/// </summary>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置User实体主键
modelBuilder.Entity<User>().HasKey(u => u.Id);
// 配置Username非空
modelBuilder.Entity<User>().Property(u => u.Username).IsRequired();
// 配置Email非空
modelBuilder.Entity<User>().Property(u => u.Email).IsRequired();
}
}
}
5.2.2 仓储接口(IUserRepository)
DataAccess/Interfaces/IUserRepository.cs
csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using WinFormThreeLayerDemo.Core.Entities;
using WinFormThreeLayerDemo.Core.Models;
namespace WinFormThreeLayerDemo.DataAccess.Interfaces
{
/// <summary>
/// 用户仓储接口(定义数据操作规范)
/// </summary>
public interface IUserRepository
{
/// <summary>
/// 添加用户
/// </summary>
Task<User> AddUserAsync(User user);
/// <summary>
/// 根据ID查询用户
/// </summary>
Task<User?> GetUserByIdAsync(Guid id);
/// <summary>
/// 分页查询用户(支持搜索)
/// </summary>
Task<PaginationResult<User>> GetUsersByPageAsync(PaginationQuery query);
/// <summary>
/// 删除用户
/// </summary>
Task<bool> DeleteUserAsync(Guid id);
/// <summary>
/// 编辑用户
/// </summary>
Task<bool> UpdateUserAsync(User user);
/// <summary>
/// 导出所有用户(用于Excel)
/// </summary>
Task<List<User>> GetAllUsersForExportAsync();
}
}
5.2.3 仓储实现(UserRepository)
DataAccess/Repositories/UserRepository.cs
csharp
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WinFormThreeLayerDemo.Core.Entities;
using WinFormThreeLayerDemo.Core.Models;
using WinFormThreeLayerDemo.DataAccess.Contexts;
using WinFormThreeLayerDemo.DataAccess.Interfaces;
namespace WinFormThreeLayerDemo.DataAccess.Repositories
{
/// <summary>
/// 用户仓储实现(EF Core操作数据库)
/// </summary>
public class UserRepository : IUserRepository
{
private readonly UserDbContext _dbContext;
public UserRepository(UserDbContext dbContext)
{
_dbContext = dbContext;
}
/// <summary>
/// 添加用户
/// </summary>
public async Task<User> AddUserAsync(User user)
{
await _dbContext.Users.AddAsync(user);
await _dbContext.SaveChangesAsync();
return user;
}
/// <summary>
/// 根据ID查询用户
/// </summary>
public async Task<User?> GetUserByIdAsync(Guid id)
{
return await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == id);
}
/// <summary>
/// 分页查询(支持模糊搜索)
/// </summary>
public async Task<PaginationResult<User>> GetUsersByPageAsync(PaginationQuery query)
{
// 构建查询(支持关键词搜索)
var queryable = _dbContext.Users.AsQueryable();
if (!string.IsNullOrWhiteSpace(query.Keyword))
{
queryable = queryable.Where(u => u.Username.Contains(query.Keyword) || u.Email.Contains(query.Keyword));
}
// 总条数
int totalCount = await queryable.CountAsync();
// 分页数据
var data = await queryable
.OrderByDescending(u => u.CreateTime) // 按创建时间倒序
.Skip((query.PageIndex - 1) * query.PageSize)
.Take(query.PageSize)
.ToListAsync();
return new PaginationResult<User>
{
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)query.PageSize),
Data = data
};
}
/// <summary>
/// 删除用户
/// </summary>
public async Task<bool> DeleteUserAsync(Guid id)
{
var user = await GetUserByIdAsync(id);
if (user == null) return false;
_dbContext.Users.Remove(user);
return await _dbContext.SaveChangesAsync() > 0;
}
/// <summary>
/// 编辑用户
/// </summary>
public async Task<bool> UpdateUserAsync(User user)
{
_dbContext.Users.Update(user);
return await _dbContext.SaveChangesAsync() > 0;
}
/// <summary>
/// 导出所有用户(Excel用)
/// </summary>
public async Task<List<User>> GetAllUsersForExportAsync()
{
return await _dbContext.Users.OrderByDescending(u => u.CreateTime).ToListAsync();
}
}
}
5.3 BusinessLogic层:业务逻辑
5.3.1 服务接口(IUserService)
BusinessLogic/Interfaces/IUserService.cs
csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using WinFormThreeLayerDemo.Core.Entities;
using WinFormThreeLayerDemo.Core.Models;
namespace WinFormThreeLayerDemo.BusinessLogic.Interfaces
{
/// <summary>
/// 用户业务服务接口
/// </summary>
public interface IUserService
{
/// <summary>
/// 添加用户(带业务校验)
/// </summary>
Task<User> CreateUserAsync(string username, string email);
/// <summary>
/// 分页查询用户
/// </summary>
Task<PaginationResult<User>> GetUsersByPageAsync(PaginationQuery query);
/// <summary>
/// 删除用户
/// </summary>
Task<bool> DeleteUserAsync(Guid id);
/// <summary>
/// 编辑用户
/// </summary>
Task<bool> UpdateUserAsync(Guid id, string newUsername, string newEmail);
/// <summary>
/// 导出所有用户到Excel
/// </summary>
Task<byte[]> ExportUsersToExcelAsync();
}
}
5.3.2 服务实现(UserService)
BusinessLogic/Services/UserService.cs
csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using WinFormThreeLayerDemo.BusinessLogic.Interfaces;
using WinFormThreeLayerDemo.Core.Entities;
using WinFormThreeLayerDemo.Core.Models;
using WinFormThreeLayerDemo.DataAccess.Interfaces;
namespace WinFormThreeLayerDemo.BusinessLogic.Services
{
/// <summary>
/// 用户业务服务实现(核心规则处理)
/// </summary>
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
/// <summary>
/// 添加用户(业务校验)
/// </summary>
public async Task<User> CreateUserAsync(string username, string email)
{
// 业务规则1:用户名不能为空
if (string.IsNullOrWhiteSpace(username))
throw new ArgumentException("用户名不能为空!");
// 业务规则2:邮箱格式校验
if (!email.Contains("@") || !email.Contains("."))
throw new ArgumentException("邮箱格式不正确(需包含@和.)!");
// 封装实体
var user = new User
{
Username = username.Trim(),
Email = email.Trim()
};
// 调用数据层添加
return await _userRepository.AddUserAsync(user);
}
/// <summary>
/// 分页查询用户(转发到数据层)
/// </summary>
public async Task<PaginationResult<User>> GetUsersByPageAsync(PaginationQuery query)
{
return await _userRepository.GetUsersByPageAsync(query);
}
/// <summary>
/// 删除用户(转发到数据层)
/// </summary>
public async Task<bool> DeleteUserAsync(Guid id)
{
return await _userRepository.DeleteUserAsync(id);
}
/// <summary>
/// 编辑用户(带业务校验)
/// </summary>
public async Task<bool> UpdateUserAsync(Guid id, string newUsername, string newEmail)
{
// 业务校验
if (string.IsNullOrWhiteSpace(newUsername))
throw new ArgumentException("用户名不能为空!");
if (!newEmail.Contains("@") || !newEmail.Contains("."))
throw new ArgumentException("邮箱格式不正确(需包含@和.)!");
// 查询用户是否存在
var user = await _userRepository.GetUserByIdAsync(id);
if (user == null)
throw new KeyNotFoundException("用户不存在,无法编辑!");
// 更新信息
user.Username = newUsername.Trim();
user.Email = newEmail.Trim();
// 调用数据层更新
return await _userRepository.UpdateUserAsync(user);
}
/// <summary>
/// 导出用户到Excel(NPOI实现)
/// </summary>
public async Task<byte[]> ExportUsersToExcelAsync()
{
// 获取所有用户
var users = await _userRepository.GetAllUsersForExportAsync();
// 创建Excel工作簿
IWorkbook workbook = new XSSFWorkbook();
ISheet sheet = workbook.CreateSheet("用户列表");
// 创建表头
IRow headerRow = sheet.CreateRow(0);
headerRow.CreateCell(0).SetCellValue("用户ID");
headerRow.CreateCell(1).SetCellValue("用户名");
headerRow.CreateCell(2).SetCellValue("邮箱");
headerRow.CreateCell(3).SetCellValue("创建时间");
// 填充数据
int rowIndex = 1;
foreach (var user in users)
{
IRow row = sheet.CreateRow(rowIndex++);
row.CreateCell(0).SetCellValue(user.Id.ToString());
row.CreateCell(1).SetCellValue(user.Username);
row.CreateCell(2).SetCellValue(user.Email);
row.CreateCell(3).SetCellValue(user.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"));
}
// 自动调整列宽
for (int i = 0; i < 4; i++)
{
sheet.AutoSizeColumn(i);
}
// 转换为字节数组(用于保存文件)
using (MemoryStream ms = new MemoryStream())
{
workbook.Write(ms);
return ms.ToArray();
}
}
}
}
5.4 Infrastructure层:基础设施
5.4.1 Autofac依赖注入配置
Infrastructure/Dependency/AutofacConfig.cs
csharp
using Autofac;
using WinFormThreeLayerDemo.BusinessLogic.Interfaces;
using WinFormThreeLayerDemo.BusinessLogic.Services;
using WinFormThreeLayerDemo.DataAccess.Contexts;
using WinFormThreeLayerDemo.DataAccess.Interfaces;
using WinFormThreeLayerDemo.DataAccess.Repositories;
namespace WinFormThreeLayerDemo.Infrastructure.Dependency
{
/// <summary>
/// Autofac依赖注入配置
/// </summary>
public static class AutofacConfig
{
/// <summary>
/// 构建Autofac容器
/// </summary>
public static IContainer BuildContainer()
{
var builder = new ContainerBuilder();
// 注册EF Core上下文(单例)
builder.RegisterType<UserDbContext>().SingleInstance();
// 注册数据访问层:接口→实现
builder.RegisterType<UserRepository>().As<IUserRepository>().InstancePerLifetimeScope();
// 注册业务逻辑层:接口→实现
builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
// 注册主窗体
builder.RegisterType<MainForm>().InstancePerDependency();
return builder.Build();
}
}
}
5.4.2 全局异常处理+日志
Infrastructure/Exception/GlobalExceptionHandler.cs
csharp
using Serilog;
using System;
using System.Windows.Forms;
namespace WinFormThreeLayerDemo.Infrastructure.Exception
{
/// <summary>
/// 全局异常处理器(统一捕获+弹窗+日志)
/// </summary>
public static class GlobalExceptionHandler
{
/// <summary>
/// 初始化全局异常处理
/// </summary>
public static void Init()
{
// 配置Serilog日志(输出到文件,按天分割)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error() // 只记录错误日志
.WriteTo.File(
path: "Logs/Error-.log",
rollingInterval: RollingInterval.Day,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"
)
.CreateLogger();
// 捕获UI线程异常
Application.ThreadException += (sender, e) =>
{
HandleException(e.Exception, "UI线程异常");
};
// 捕获非UI线程异常
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
if (e.ExceptionObject is System.Exception ex)
{
HandleException(ex, "非UI线程异常");
}
};
}
/// <summary>
/// 统一处理异常
/// </summary>
/// <param name="ex">异常对象</param>
/// <param name="source">异常来源</param>
public static void HandleException(System.Exception ex, string source)
{
// 写入日志
Log.Error(ex, $"{source}:{ex.Message}");
// 友好弹窗提示
string message = $"【系统提示】\n{ex.Message}\n\n详情请查看Logs文件夹下的日志文件!";
MessageBox.Show(message, "操作失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
5.5 Program.cs:程序入口
csharp
using Autofac;
using System;
using System.Windows.Forms;
using WinFormThreeLayerDemo.Infrastructure.Dependency;
using WinFormThreeLayerDemo.Infrastructure.Exception;
namespace WinFormThreeLayerDemo
{
internal static class Program
{
/// <summary>
/// 应用程序的主入口点
/// </summary>
[STAThread]
static void Main()
{
// 1. 初始化全局异常处理
GlobalExceptionHandler.Init();
// 2. 启用Windows视觉样式
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 3. 构建Autofac容器并解析主窗体
var container = AutofacConfig.BuildContainer();
using (var scope = container.BeginLifetimeScope())
{
var mainForm = scope.Resolve<MainForm>();
Application.Run(mainForm);
}
}
}
}
5.6 MainForm:核心窗体(完整功能)
5.6.1 窗体控件布局(必看!)
| 控件类型 | 控件Name | 显示文本 | 用途 |
|---|---|---|---|
| TextBox | txtUsername | 空 | 新增/编辑用户名输入 |
| TextBox | txtEmail | 空 | 新增/编辑邮箱输入 |
| TextBox | txtSearch | 空 | 搜索关键词输入 |
| Button | btnAdd | 新增用户 | 触发新增功能 |
| Button | btnEdit | 编辑选中 | 触发编辑功能 |
| Button | btnDelete | 删除选中 | 触发删除功能 |
| Button | btnSearch | 搜索 | 触发搜索功能 |
| Button | btnExport | 导出Excel | 触发Excel导出 |
| Button | btnFirstPage | 首页 | 分页-首页 |
| Button | btnPrevPage | 上一页 | 分页-上一页 |
| Button | btnNextPage | 下一页 | 分页-下一页 |
| Button | btnLastPage | 尾页 | 分页-尾页 |
| Label | lblPageInfo | 空 | 显示分页信息(共X页/当前第X页) |
| DataGridView | dgvUserList | - | 用户列表展示 |
| Label | lblTip | 空 | 操作提示(红色字体) |
5.6.2 窗体完整代码
MainForm.cs
csharp
using System;
using System.IO;
using System.Windows.Forms;
using WinFormThreeLayerDemo.BusinessLogic.Interfaces;
using WinFormThreeLayerDemo.Core.Entities;
using WinFormThreeLayerDemo.Core.Models;
using WinFormThreeLayerDemo.Infrastructure.Exception;
namespace WinFormThreeLayerDemo
{
public partial class MainForm : Form
{
// 业务服务(Autofac注入)
private readonly IUserService _userService;
// 当前分页参数
private PaginationQuery _currentQuery = new PaginationQuery();
// 当前编辑的用户ID
private Guid _editUserId = Guid.Empty;
/// <summary>
/// 构造函数(Autofac自动注入IUserService)
/// </summary>
public MainForm(IUserService userService)
{
InitializeComponent();
_userService = userService;
// 初始化DataGridView
InitDataGridView();
// 加载第一页数据
LoadUserListAsync();
}
/// <summary>
/// 初始化DataGridView(自适应+列配置)
/// </summary>
private void InitDataGridView()
{
// 清空默认列
dgvUserList.Columns.Clear();
// 添加列(绑定User实体属性)
dgvUserList.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colId",
HeaderText = "用户ID",
DataPropertyName = "Id",
Width = 180
});
dgvUserList.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colUsername",
HeaderText = "用户名",
DataPropertyName = "Username",
AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
});
dgvUserList.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colEmail",
HeaderText = "邮箱",
DataPropertyName = "Email",
AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
});
dgvUserList.Columns.Add(new DataGridViewTextBoxColumn
{
Name = "colCreateTime",
HeaderText = "创建时间",
DataPropertyName = "CreateTime",
DefaultCellStyle = { Format = "yyyy-MM-dd HH:mm:ss" },
Width = 180
});
// 样式配置
dgvUserList.ReadOnly = true; // 只读
dgvUserList.SelectionMode = DataGridViewSelectionMode.FullRowSelect; // 整行选中
dgvUserList.AllowUserToAddRows = false; // 禁止新增行
dgvUserList.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; // 自适应窗体
}
/// <summary>
/// 加载用户列表(分页)
/// </summary>
private async void LoadUserListAsync()
{
try
{
lblTip.Text = "正在加载数据...";
// 调用业务层获取分页数据
var result = await _userService.GetUsersByPageAsync(_currentQuery);
// 绑定到DataGridView
dgvUserList.DataSource = result.Data;
// 更新分页信息
lblPageInfo.Text = $"共{result.TotalCount}条数据 | 共{result.TotalPages}页 | 当前第{_currentQuery.PageIndex}页";
// 禁用/启用分页按钮
btnFirstPage.Enabled = _currentQuery.PageIndex > 1;
btnPrevPage.Enabled = _currentQuery.PageIndex > 1;
btnNextPage.Enabled = _currentQuery.PageIndex < result.TotalPages;
btnLastPage.Enabled = _currentQuery.PageIndex < result.TotalPages;
lblTip.Text = "加载完成!";
}
catch (System.Exception ex)
{
GlobalExceptionHandler.HandleException(ex, "加载用户列表");
lblTip.Text = "加载失败!";
}
}
/// <summary>
/// 新增用户按钮点击事件
/// </summary>
private async void btnAdd_Click(object sender, EventArgs e)
{
try
{
// 获取输入值
string username = txtUsername.Text.Trim();
string email = txtEmail.Text.Trim();
// 调用业务层新增
await _userService.CreateUserAsync(username, email);
// 清空输入框
txtUsername.Text = string.Empty;
txtEmail.Text = string.Empty;
// 刷新列表(回到第一页)
_currentQuery.PageIndex = 1;
LoadUserListAsync();
lblTip.Text = "新增用户成功!";
}
catch (System.Exception ex)
{
GlobalExceptionHandler.HandleException(ex, "新增用户");
lblTip.Text = "新增失败!";
}
}
/// <summary>
/// 编辑选中用户按钮点击事件
/// </summary>
private void btnEdit_Click(object sender, EventArgs e)
{
// 检查是否选中行
if (dgvUserList.SelectedRows.Count == 0)
{
lblTip.Text = "请先选中要编辑的用户!";
return;
}
// 获取选中用户信息
var selectedUser = (User)dgvUserList.SelectedRows[0].DataBoundItem;
// 填充到输入框
txtUsername.Text = selectedUser.Username;
txtEmail.Text = selectedUser.Email;
// 记录编辑的用户ID
_editUserId = selectedUser.Id;
lblTip.Text = "请修改信息后点击【新增用户】按钮保存!";
}
/// <summary>
/// 删除选中用户按钮点击事件
/// </summary>
private async void btnDelete_Click(object sender, EventArgs e)
{
// 检查是否选中行
if (dgvUserList.SelectedRows.Count == 0)
{
lblTip.Text = "请先选中要删除的用户!";
return;
}
// 确认删除
if (MessageBox.Show("确定要删除选中的用户吗?此操作不可恢复!", "确认删除", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes)
{
return;
}
try
{
// 获取选中用户ID
var selectedUser = (User)dgvUserList.SelectedRows[0].DataBoundItem;
// 调用业务层删除
bool isSuccess = await _userService.DeleteUserAsync(selectedUser.Id);
if (isSuccess)
{
lblTip.Text = "删除用户成功!";
// 刷新列表
LoadUserListAsync();
}
else
{
lblTip.Text = "删除失败:用户不存在!";
}
}
catch (System.Exception ex)
{
GlobalExceptionHandler.HandleException(ex, "删除用户");
lblTip.Text = "删除失败!";
}
}
/// <summary>
/// 搜索按钮点击事件
/// </summary>
private void btnSearch_Click(object sender, EventArgs e)
{
// 更新搜索关键词
_currentQuery.Keyword = txtSearch.Text.Trim();
// 回到第一页
_currentQuery.PageIndex = 1;
// 刷新列表
LoadUserListAsync();
lblTip.Text = "搜索完成!";
}
/// <summary>
/// 导出Excel按钮点击事件
/// </summary>
private async void btnExport_Click(object sender, EventArgs e)
{
try
{
lblTip.Text = "正在导出Excel...";
// 调用业务层生成Excel字节数组
byte[] excelBytes = await _userService.ExportUsersToExcelAsync();
// 保存文件对话框
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "Excel文件 (*.xlsx)|*.xlsx";
sfd.FileName = $"用户列表_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
if (sfd.ShowDialog() == DialogResult.OK)
{
// 写入文件
File.WriteAllBytes(sfd.FileName, excelBytes);
lblTip.Text = "Excel导出成功!";
MessageBox.Show("Excel导出成功!文件路径:" + sfd.FileName, "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
catch (System.Exception ex)
{
GlobalExceptionHandler.HandleException(ex, "导出Excel");
lblTip.Text = "导出失败!";
}
}
#region 分页按钮事件
/// <summary>
/// 首页
/// </summary>
private void btnFirstPage_Click(object sender, EventArgs e)
{
_currentQuery.PageIndex = 1;
LoadUserListAsync();
}
/// <summary>
/// 上一页
/// </summary>
private void btnPrevPage_Click(object sender, EventArgs e)
{
if (_currentQuery.PageIndex > 1)
{
_currentQuery.PageIndex--;
LoadUserListAsync();
}
}
/// <summary>
/// 下一页
/// </summary>
private void btnNextPage_Click(object sender, EventArgs e)
{
_currentQuery.PageIndex++;
LoadUserListAsync();
}
/// <summary>
/// 尾页
/// </summary>
private void btnLastPage_Click(object sender, EventArgs e)
{
// 先获取总页数
Task.Run(async () =>
{
var result = await _userService.GetUsersByPageAsync(_currentQuery);
_currentQuery.PageIndex = result.TotalPages;
// 切换到UI线程刷新
this.Invoke(new Action(LoadUserListAsync));
});
}
#endregion
}
}
六、数据库迁移(生成表结构)
- 打开VS2022 → 工具 → NuGet包管理器 → 包管理器控制台;
- 执行以下命令(生成建表代码并执行):
powershell
# 初始化迁移(生成建表脚本)
Add-Migration InitialCreate
# 执行迁移(创建数据库表)
Update-Database
- 验证:打开SQL Server/MySQL,查看
Users表是否创建成功。
七、运行教程(一步到位)
- 替换
UserDbContext.cs中的数据库连接字符串(修改为你的账号密码); - 执行数据库迁移(生成
Users表); - 按F5启动项目;
- 功能测试:
- 新增用户:输入用户名+邮箱 → 点击【新增用户】;
- 编辑用户:选中列表行 → 点击【编辑选中】→ 修改信息 → 点击【新增用户】保存;
- 删除用户:选中列表行 → 点击【删除选中】→ 确认;
- 搜索用户:输入关键词 → 点击【搜索】;
- 分页:点击【上一页/下一页/首页/尾页】切换;
- 导出Excel:点击【导出Excel】→ 选择保存路径。
八、配套资源(免费获取)
8.1 完整源码
关注后私信「WinForm三层架构」,自动获取:
- 完整项目源码(VS2022可直接打开);
- 数据库脚本(SQL Server/MySQL手动建表语句);
- 窗体布局截图(控件位置参考)。
8.2 常见问题解决
| 问题 | 解决方案 |
|---|---|
| 数据库连接失败 | 检查连接字符串中的服务器名、账号、密码;SQL Server需开启TCP/IP协议;MySQL需允许远程连接 |
| 迁移命令报错 | 确保安装了Microsoft.EntityFrameworkCore.Tools包;设置项目为启动项目 |
| Excel导出失败 | 确保安装了NPOI 2.6.0包;目标文件夹有写入权限 |
| Autofac注入报错 | 检查AutofacConfig中接口和实现类是否匹配;确保MainForm构造函数参数正确 |
九、进阶扩展方向
- 用户登录/权限控制:添加LoginForm+Role实体+权限校验;
- 多条件查询:扩展PaginationQuery,支持按时间范围/邮箱域名查询;
- 批量操作:批量删除/批量导入Excel;
- 数据缓存:添加MemoryCache缓存用户列表,提升查询性能;
- 本地化:支持中英文切换;
- 皮肤美化:集成DevExpress/MetroFramework美化界面。
十、总结
这套项目是企业级WinForm开发的标准模板,核心亮点:
- 分层清晰:UI只做交互、BLL只做规则、DAL只做数据,解耦易维护;
- 功能完整:增删改查+分页+搜索+导出,满足大部分小型桌面工具需求;
- 企业特性:依赖注入+全局异常+日志,符合工业级开发规范;
- 双数据库:一键切换SQL Server/MySQL,适配不同环境。