# 从零到一:用C#三层架构构建一个健壮的用户管理系统
很多刚开始接触C#企业级开发的朋友,都听说过“三层架构”这个概念,但真正动手实现时,却常常卡在如何将理论转化为实际代码的环节。我自己刚开始学的时候,也经历过这个阶段——知道UI、BLL、DAL各司其职,但一到具体项目,就不知道该怎么组织代码,各层之间怎么传递数据,数据库连接又该怎么处理。今天,我就以一个完整的用户管理系统为例,带你一步步搭建一个基于WinForm和SQL Server的三层架构应用,不只是照搬概念,而是深入每个层次的实现细节和设计考量。
这个系统虽然功能上看起来简单——无非就是用户的增删改查,但麻雀虽小,五脏俱全。通过它,你能清晰地看到数据如何从界面流向数据库,业务规则如何被封装,以及各层之间如何保持松耦合。更重要的是,你会理解为什么我们要费这么大劲去分层:当你的应用从几十行代码扩展到几千、几万行时,一个清晰的结构能让你和你的团队省下大量维护和调试的时间。
## 1. 项目准备与环境搭建
在开始编码之前,我们需要先规划好整个项目的结构。我建议使用Visual Studio 2022或更高版本,社区版就完全够用。打开VS后,不要直接新建一个Windows窗体应用项目,而是先创建一个空的解决方案,这样能更好地管理我们即将创建的多个项目。
### 1.1 解决方案与项目结构规划
一个标准的三层架构解决方案通常包含4个独立的类库项目,外加一个启动项目。下面是具体的创建步骤:
1. **创建空白解决方案**:打开Visual Studio,选择“创建新项目”,搜索“空白解决方案”,命名为`UserManagementSystem`。
2. **添加Model层项目**:在解决方案资源管理器中右键单击解决方案,选择“添加”->“新建项目”。选择“类库(.NET Framework或.NET Core/.NET 6+)”,命名为`UserManagement.Model`。这个项目将存放我们的实体类,它不依赖任何其他层。
3. **添加DAL层项目**:同样的方式创建类库项目,命名为`UserManagement.DAL`。这个项目需要引用`UserManagement.Model`,因为数据访问层需要操作实体对象。
4. **添加BLL层项目**:创建类库项目`UserManagement.BLL`。这个项目需要引用`UserManagement.Model`和`UserManagement.DAL`,因为业务逻辑层既要使用实体对象,又要调用数据访问层的方法。
5. **添加UI层项目**:这次选择“Windows窗体应用(.NET Framework)”或“Windows窗体应用(.NET)”,命名为`UserManagement.UI`。这个项目需要引用`UserManagement.Model`和`UserManagement.BLL`。
> **注意**:这里有个关键点——UI层不应该直接引用DAL层。这是三层架构的一个重要原则:表现层只能通过业务逻辑层来访问数据,不能绕过业务规则直接操作数据库。
完成后的解决方案结构应该像这样:
```
UserManagementSystem (解决方案)
├── UserManagement.Model (类库)
├── UserManagement.DAL (类库)
├── UserManagement.BLL (类库)
└── UserManagement.UI (Windows窗体应用)
```
### 1.2 数据库设计与创建
在编码之前,我们先设计数据库。打开SQL Server Management Studio,创建一个名为`UserDB`的数据库,然后执行以下SQL脚本创建用户表:
```sql
CREATE DATABASE UserDB;
GO
USE UserDB;
GO
CREATE TABLE Users (
UserID INT IDENTITY(1,1) PRIMARY KEY,
UserName NVARCHAR(50) NOT NULL,
Password NVARCHAR(100) NOT NULL,
Email NVARCHAR(100),
Phone NVARCHAR(20),
CreateTime DATETIME DEFAULT GETDATE(),
LastLoginTime DATETIME,
IsActive BIT DEFAULT 1
);
GO
-- 插入一些测试数据
INSERT INTO Users (UserName, Password, Email, Phone)
VALUES
('admin', '123456', 'admin@example.com', '13800138000'),
('testuser', 'test123', 'test@example.com', '13900139000');
GO
```
这个表设计考虑了实际应用中的常见需求:自增主键、必填字段、默认值约束,以及一些扩展字段如邮箱、电话、创建时间等。在实际项目中,密码字段应该存储加密后的哈希值,而不是明文,这里为了简化演示,暂时使用明文。
### 1.3 配置数据库连接字符串
在DAL层,我们需要一个地方来存储数据库连接字符串。在.NET中,通常使用配置文件来管理这类设置。对于类库项目,我们不能直接使用`app.config`,但可以在UI层的`app.config`或`appsettings.json`中配置,然后在DAL层通过代码读取。
在`UserManagement.UI`项目中,添加一个`App.config`文件(如果使用.NET Framework)或`appsettings.json`文件(如果使用.NET Core/.NET 6+)。这里以.NET Framework为例:
```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="UserDBConnection"
connectionString="Server=.;Database=UserDB;Integrated Security=True;"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
```
> **提示**:在实际生产环境中,连接字符串应该加密存储,并且不要将包含敏感信息的配置文件提交到版本控制系统。可以考虑使用Azure Key Vault或类似的服务来管理机密信息。
## 2. Model层:构建数据实体
Model层在三层架构中扮演着数据载体的角色。它不包含任何业务逻辑,只是简单定义了数据的结构。这种设计符合单一职责原则,也让数据在层间传递时更加清晰。
### 2.1 创建用户实体类
在`UserManagement.Model`项目中,删除默认的`Class1.cs`,然后添加一个名为`User`的类:
```csharp
using System;
namespace UserManagement.Model
{
/// <summary>
/// 用户实体类
/// 对应数据库中的Users表
/// </summary>
public class User
{
public int UserID { get; set; }
private string _userName;
public string UserName
{
get => _userName;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("用户名不能为空");
if (value.Length > 50)
throw new ArgumentException("用户名长度不能超过50个字符");
_userName = value;
}
}
private string _password;
public string Password
{
get => _password;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("密码不能为空");
if (value.Length < 6)
throw new ArgumentException("密码长度不能少于6个字符");
_password = value;
}
}
public string Email { get; set; }
public string Phone { get; set; }
public DateTime CreateTime { get; set; }
public DateTime? LastLoginTime { get; set; }
public bool IsActive { get; set; }
public User()
{
CreateTime = DateTime.Now;
IsActive = true;
}
public User(string userName, string password, string email = null, string phone = null)
{
UserName = userName;
Password = password;
Email = email;
Phone = phone;
CreateTime = DateTime.Now;
IsActive = true;
}
}
}
```
注意这里我在属性的setter中添加了基本的验证逻辑。虽然Model层通常只负责数据承载,但像这种基础的数据完整性检查放在这里也是合理的。更复杂的业务规则(比如用户名是否已存在)应该放在BLL层。
### 2.2 添加其他辅助实体
在实际项目中,我们可能还需要其他实体类。比如,用户查询条件、分页信息等。这里我们添加一个`UserQuery`类,用于封装查询条件:
```csharp
namespace UserManagement.Model
{
/// <summary>
/// 用户查询条件
/// </summary>
public class UserQuery
{
public string UserName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public bool? IsActive { get; set; }
public DateTime? CreateTimeStart { get; set; }
public DateTime? CreateTimeEnd { get; set; }
public int PageIndex { get; set; } = 1;
public int PageSize { get; set; } = 20;
public string SortField { get; set; } = "CreateTime";
public bool SortDesc { get; set; } = true;
}
}
```
以及一个`PagedResult`类,用于返回分页查询结果:
```csharp
using System.Collections.Generic;
namespace UserManagement.Model
{
/// <summary>
/// 分页查询结果
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
public class PagedResult<T>
{
public List<T> Data { get; set; }
public int TotalCount { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int TotalPages => PageSize > 0 ? (TotalCount + PageSize - 1) / PageSize : 0;
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
}
}
```
这些辅助类虽然不是数据库表的直接映射,但它们作为数据载体在层间传递时非常有用,能让我们的接口更加清晰。
## 3. DAL层:数据访问的实现
DAL层是直接与数据库打交道的层次。它的职责很明确:执行CRUD操作。这一层应该只关注数据访问技术细节,不包含任何业务逻辑。
### 3.1 创建数据库帮助类
为了避免在每个数据访问方法中重复编写数据库连接和命令执行的代码,我们创建一个`DbHelper`类。这个类封装了ADO.NET的基本操作:
```csharp
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace UserManagement.DAL
{
/// <summary>
/// 数据库操作帮助类
/// </summary>
public static class DbHelper
{
private static readonly string ConnectionString;
static DbHelper()
{
// 在实际项目中,这里应该从配置文件读取连接字符串
// 为了简化演示,我们直接写在这里
ConnectionString = ConfigurationManager.ConnectionStrings["UserDBConnection"]?.ConnectionString
?? "Server=.;Database=UserDB;Integrated Security=True;";
}
/// <summary>
/// 执行非查询SQL语句
/// </summary>
public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters)
{
using (var connection = new SqlConnection(ConnectionString))
using (var command = new SqlCommand(sql, connection))
{
if (parameters != null && parameters.Length > 0)
{
command.Parameters.AddRange(parameters);
}
connection.Open();
return command.ExecuteNonQuery();
}
}
/// <summary>
/// 执行查询并返回第一行第一列
/// </summary>
public static object ExecuteScalar(string sql, params SqlParameter[] parameters)
{
using (var connection = new SqlConnection(ConnectionString))
using (var command = new SqlCommand(sql, connection))
{
if (parameters != null && parameters.Length > 0)
{
command.Parameters.AddRange(parameters);
}
connection.Open();
return command.ExecuteScalar();
}
}
/// <summary>
/// 执行查询并返回DataReader
/// </summary>
public static SqlDataReader ExecuteReader(string sql, params SqlParameter[] parameters)
{
var connection = new SqlConnection(ConnectionString);
var command = new SqlCommand(sql, connection);
if (parameters != null && parameters.Length > 0)
{
command.Parameters.AddRange(parameters);
}
connection.Open();
// CommandBehavior.CloseConnection确保Reader关闭时连接也关闭
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
/// <summary>
/// 执行查询并返回DataTable
/// </summary>
public static DataTable ExecuteDataTable(string sql, params SqlParameter[] parameters)
{
using (var connection = new SqlConnection(ConnectionString))
using (var command = new SqlCommand(sql, connection))
{
if (parameters != null && parameters.Length > 0)
{
command.Parameters.AddRange(parameters);
}
var dataTable = new DataTable();
var adapter = new SqlDataAdapter(command);
connection.Open();
adapter.Fill(dataTable);
return dataTable;
}
}
/// <summary>
/// 执行事务
/// </summary>
public static void ExecuteTransaction(Action<SqlConnection, SqlTransaction> action)
{
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
action(connection, transaction);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
}
}
```
这个帮助类提供了几种常用的数据库操作方法。注意`ExecuteReader`方法没有使用`using`语句包裹`SqlConnection`,这是因为我们需要保持连接打开直到DataReader读取完毕。通过`CommandBehavior.CloseConnection`参数,我们确保当DataReader关闭时,连接也会自动关闭。
### 3.2 实现用户数据访问类
现在我们来创建`UserDAL`类,它负责具体的用户数据操作:
```csharp
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using UserManagement.Model;
namespace UserManagement.DAL
{
/// <summary>
/// 用户数据访问类
/// </summary>
public class UserDAL
{
/// <summary>
/// 添加用户
/// </summary>
public int AddUser(User user)
{
const string sql = @"
INSERT INTO Users (UserName, Password, Email, Phone, CreateTime, IsActive)
VALUES (@UserName, @Password, @Email, @Phone, @CreateTime, @IsActive);
SELECT SCOPE_IDENTITY();";
var parameters = new[]
{
new SqlParameter("@UserName", user.UserName),
new SqlParameter("@Password", user.Password),
new SqlParameter("@Email", (object)user.Email ?? DBNull.Value),
new SqlParameter("@Phone", (object)user.Phone ?? DBNull.Value),
new SqlParameter("@CreateTime", user.CreateTime),
new SqlParameter("@IsActive", user.IsActive)
};
var result = DbHelper.ExecuteScalar(sql, parameters);
return Convert.ToInt32(result);
}
/// <summary>
/// 更新用户信息
/// </summary>
public bool UpdateUser(User user)
{
const string sql = @"
UPDATE Users
SET UserName = @UserName,
Password = @Password,
Email = @Email,
Phone = @Phone,
IsActive = @IsActive
WHERE UserID = @UserID";
var parameters = new[]
{
new SqlParameter("@UserID", user.UserID),
new SqlParameter("@UserName", user.UserName),
new SqlParameter("@Password", user.Password),
new SqlParameter("@Email", (object)user.Email ?? DBNull.Value),
new SqlParameter("@Phone", (object)user.Phone ?? DBNull.Value),
new SqlParameter("@IsActive", user.IsActive)
};
return DbHelper.ExecuteNonQuery(sql, parameters) > 0;
}
/// <summary>
/// 删除用户(逻辑删除)
/// </summary>
public bool DeleteUser(int userID)
{
const string sql = "UPDATE Users SET IsActive = 0 WHERE UserID = @UserID";
var parameter = new SqlParameter("@UserID", userID);
return DbHelper.ExecuteNonQuery(sql, parameter) > 0;
}
/// <summary>
/// 根据ID获取用户
/// </summary>
public User GetUserByID(int userID)
{
const string sql = "SELECT * FROM Users WHERE UserID = @UserID";
var parameter = new SqlParameter("@UserID", userID);
using (var reader = DbHelper.ExecuteReader(sql, parameter))
{
if (reader.Read())
{
return MapReaderToUser(reader);
}
return null;
}
}
/// <summary>
/// 根据用户名获取用户
/// </summary>
public User GetUserByName(string userName)
{
const string sql = "SELECT * FROM Users WHERE UserName = @UserName";
var parameter = new SqlParameter("@UserName", userName);
using (var reader = DbHelper.ExecuteReader(sql, parameter))
{
if (reader.Read())
{
return MapReaderToUser(reader);
}
return null;
}
}
/// <summary>
/// 查询用户列表
/// </summary>
public List<User> GetUsers(UserQuery query)
{
var users = new List<User>();
var sql = BuildQuerySql(query, out var parameters);
using (var reader = DbHelper.ExecuteReader(sql, parameters))
{
while (reader.Read())
{
users.Add(MapReaderToUser(reader));
}
}
return users;
}
/// <summary>
/// 获取查询结果总数(用于分页)
/// </summary>
public int GetUserCount(UserQuery query)
{
var sql = BuildCountSql(query, out var parameters);
var result = DbHelper.ExecuteScalar(sql, parameters);
return Convert.ToInt32(result);
}
/// <summary>
/// 分页查询用户
/// </summary>
public PagedResult<User> GetUsersPaged(UserQuery query)
{
var result = new PagedResult<User>
{
PageIndex = query.PageIndex,
PageSize = query.PageSize,
TotalCount = GetUserCount(query)
};
if (result.TotalCount == 0)
{
result.Data = new List<User>();
return result;
}
var sql = BuildPagedQuerySql(query, out var parameters);
result.Data = new List<User>();
using (var reader = DbHelper.ExecuteReader(sql, parameters))
{
while (reader.Read())
{
result.Data.Add(MapReaderToUser(reader));
}
}
return result;
}
/// <summary>
/// 更新用户最后登录时间
/// </summary>
public bool UpdateLastLoginTime(int userID)
{
const string sql = "UPDATE Users SET LastLoginTime = GETDATE() WHERE UserID = @UserID";
var parameter = new SqlParameter("@UserID", userID);
return DbHelper.ExecuteNonQuery(sql, parameter) > 0;
}
/// <summary>
/// 检查用户名是否存在
/// </summary>
public bool UserNameExists(string userName, int? excludeUserID = null)
{
var sql = "SELECT COUNT(1) FROM Users WHERE UserName = @UserName";
var parameters = new List<SqlParameter>
{
new SqlParameter("@UserName", userName)
};
if (excludeUserID.HasValue)
{
sql += " AND UserID != @ExcludeUserID";
parameters.Add(new SqlParameter("@ExcludeUserID", excludeUserID.Value));
}
var count = Convert.ToInt32(DbHelper.ExecuteScalar(sql, parameters.ToArray()));
return count > 0;
}
/// <summary>
/// 将DataReader映射到User对象
/// </summary>
private User MapReaderToUser(SqlDataReader reader)
{
return new User
{
UserID = Convert.ToInt32(reader["UserID"]),
UserName = reader["UserName"].ToString(),
Password = reader["Password"].ToString(),
Email = reader["Email"] == DBNull.Value ? null : reader["Email"].ToString(),
Phone = reader["Phone"] == DBNull.Value ? null : reader["Phone"].ToString(),
CreateTime = Convert.ToDateTime(reader["CreateTime"]),
LastLoginTime = reader["LastLoginTime"] == DBNull.Value ?
(DateTime?)null : Convert.ToDateTime(reader["LastLoginTime"]),
IsActive = Convert.ToBoolean(reader["IsActive"])
};
}
/// <summary>
/// 构建查询SQL
/// </summary>
private string BuildQuerySql(UserQuery query, out SqlParameter[] parameters)
{
var sql = "SELECT * FROM Users WHERE 1=1";
var paramList = new List<SqlParameter>();
if (!string.IsNullOrWhiteSpace(query.UserName))
{
sql += " AND UserName LIKE @UserName";
paramList.Add(new SqlParameter("@UserName", $"%{query.UserName}%"));
}
if (!string.IsNullOrWhiteSpace(query.Email))
{
sql += " AND Email LIKE @Email";
paramList.Add(new SqlParameter("@Email", $"%{query.Email}%"));
}
if (!string.IsNullOrWhiteSpace(query.Phone))
{
sql += " AND Phone LIKE @Phone";
paramList.Add(new SqlParameter("@Phone", $"%{query.Phone}%"));
}
if (query.IsActive.HasValue)
{
sql += " AND IsActive = @IsActive";
paramList.Add(new SqlParameter("@IsActive", query.IsActive.Value));
}
if (query.CreateTimeStart.HasValue)
{
sql += " AND CreateTime >= @CreateTimeStart";
paramList.Add(new SqlParameter("@CreateTimeStart", query.CreateTimeStart.Value));
}
if (query.CreateTimeEnd.HasValue)
{
sql += " AND CreateTime <= @CreateTimeEnd";
paramList.Add(new SqlParameter("@CreateTimeEnd", query.CreateTimeEnd.Value));
}
sql += $" ORDER BY {query.SortField} {(query.SortDesc ? "DESC" : "ASC")}";
parameters = paramList.ToArray();
return sql;
}
/// <summary>
/// 构建计数SQL
/// </summary>
private string BuildCountSql(UserQuery query, out SqlParameter[] parameters)
{
var sql = "SELECT COUNT(1) FROM Users WHERE 1=1";
var paramList = new List<SqlParameter>();
// 这里重用BuildQuerySql中的条件构建逻辑
// 为了简化,我们直接调用BuildQuerySql,然后替换SELECT部分
var querySql = BuildQuerySql(query, out var queryParams);
sql = "SELECT COUNT(1) FROM Users" + querySql.Substring(querySql.IndexOf("WHERE"));
parameters = queryParams;
return sql;
}
/// <summary>
/// 构建分页查询SQL
/// </summary>
private string BuildPagedQuerySql(UserQuery query, out SqlParameter[] parameters)
{
var baseSql = BuildQuerySql(query, out parameters);
// SQL Server 2012+ 使用OFFSET FETCH语法
var pagedSql = $@"
{baseSql}
OFFSET {(query.PageIndex - 1) * query.PageSize} ROWS
FETCH NEXT {query.PageSize} ROWS ONLY";
return pagedSql;
}
}
}
```
这个`UserDAL`类实现了完整的CRUD操作,包括分页查询、条件查询等。注意我使用了参数化查询来防止SQL注入攻击,这是数据库编程中必须注意的安全问题。
## 4. BLL层:业务逻辑的封装
BLL层是整个架构的核心,它包含了所有的业务规则和逻辑。这一层决定了应用程序能做什么、不能做什么,以及怎么做。
### 4.1 创建用户业务逻辑类
在`UserManagement.BLL`项目中,添加`UserManager`类:
```csharp
using System;
using System.Collections.Generic;
using UserManagement.DAL;
using UserManagement.Model;
namespace UserManagement.BLL
{
/// <summary>
/// 用户业务逻辑类
/// </summary>
public class UserManager
{
private readonly UserDAL _userDAL;
public UserManager()
{
_userDAL = new UserDAL();
}
/// <summary>
/// 用户注册
/// </summary>
public OperationResult Register(User user)
{
var result = new OperationResult();
try
{
// 1. 验证输入
if (user == null)
{
result.Success = false;
result.Message = "用户信息不能为空";
return result;
}
// 2. 检查用户名是否已存在
if (_userDAL.UserNameExists(user.UserName))
{
result.Success = false;
result.Message = "用户名已存在";
return result;
}
// 3. 密码强度验证(这里只是简单示例,实际应该更复杂)
if (user.Password.Length < 8)
{
result.Success = false;
result.Message = "密码长度至少8位";
return result;
}
// 4. 邮箱格式验证
if (!string.IsNullOrEmpty(user.Email) && !IsValidEmail(user.Email))
{
result.Success = false;
result.Message = "邮箱格式不正确";
return result;
}
// 5. 执行注册
var userID = _userDAL.AddUser(user);
result.Success = true;
result.Message = "注册成功";
result.Data = userID;
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"注册失败:{ex.Message}";
// 在实际项目中,这里应该记录日志
}
return result;
}
/// <summary>
/// 用户登录
/// </summary>
public OperationResult<User> Login(string userName, string password)
{
var result = new OperationResult<User>();
try
{
// 1. 基本验证
if (string.IsNullOrWhiteSpace(userName))
{
result.Success = false;
result.Message = "用户名不能为空";
return result;
}
if (string.IsNullOrWhiteSpace(password))
{
result.Success = false;
result.Message = "密码不能为空";
return result;
}
// 2. 查询用户
var user = _userDAL.GetUserByName(userName);
if (user == null)
{
result.Success = false;
result.Message = "用户名或密码错误";
return result;
}
// 3. 验证密码(这里应该使用加密比较)
if (user.Password != password) // 实际项目中应该比较加密后的密码
{
result.Success = false;
result.Message = "用户名或密码错误";
return result;
}
// 4. 检查用户状态
if (!user.IsActive)
{
result.Success = false;
result.Message = "用户已被禁用";
return result;
}
// 5. 更新最后登录时间
_userDAL.UpdateLastLoginTime(user.UserID);
result.Success = true;
result.Message = "登录成功";
result.Data = user;
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"登录失败:{ex.Message}";
}
return result;
}
/// <summary>
/// 更新用户信息
/// </summary>
public OperationResult UpdateUser(User user)
{
var result = new OperationResult();
try
{
// 1. 验证输入
if (user == null)
{
result.Success = false;
result.Message = "用户信息不能为空";
return result;
}
// 2. 检查用户是否存在
var existingUser = _userDAL.GetUserByID(user.UserID);
if (existingUser == null)
{
result.Success = false;
result.Message = "用户不存在";
return result;
}
// 3. 检查用户名是否被其他用户使用
if (_userDAL.UserNameExists(user.UserName, user.UserID))
{
result.Success = false;
result.Message = "用户名已被其他用户使用";
return result;
}
// 4. 执行更新
var success = _userDAL.UpdateUser(user);
if (success)
{
result.Success = true;
result.Message = "更新成功";
}
else
{
result.Success = false;
result.Message = "更新失败";
}
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"更新失败:{ex.Message}";
}
return result;
}
/// <summary>
/// 删除用户
/// </summary>
public OperationResult DeleteUser(int userID)
{
var result = new OperationResult();
try
{
// 1. 检查用户是否存在
var user = _userDAL.GetUserByID(userID);
if (user == null)
{
result.Success = false;
result.Message = "用户不存在";
return result;
}
// 2. 不能删除自己(这里只是示例,实际业务可能更复杂)
// 在实际项目中,这里可能需要检查当前登录用户等
// 3. 执行删除(逻辑删除)
var success = _userDAL.DeleteUser(userID);
if (success)
{
result.Success = true;
result.Message = "删除成功";
}
else
{
result.Success = false;
result.Message = "删除失败";
}
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"删除失败:{ex.Message}";
}
return result;
}
/// <summary>
/// 获取用户列表
/// </summary>
public OperationResult<List<User>> GetUsers(UserQuery query)
{
var result = new OperationResult<List<User>>();
try
{
var users = _userDAL.GetUsers(query);
result.Success = true;
result.Message = "查询成功";
result.Data = users;
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"查询失败:{ex.Message}";
result.Data = new List<User>();
}
return result;
}
/// <summary>
/// 分页获取用户列表
/// </summary>
public OperationResult<PagedResult<User>> GetUsersPaged(UserQuery query)
{
var result = new OperationResult<PagedResult<User>>();
try
{
// 验证分页参数
if (query.PageIndex < 1)
{
query.PageIndex = 1;
}
if (query.PageSize < 1 || query.PageSize > 100)
{
query.PageSize = 20; // 默认每页20条,最大100条
}
var pagedResult = _userDAL.GetUsersPaged(query);
result.Success = true;
result.Message = "查询成功";
result.Data = pagedResult;
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"查询失败:{ex.Message}";
result.Data = new PagedResult<User> { Data = new List<User>() };
}
return result;
}
/// <summary>
/// 根据ID获取用户
/// </summary>
public OperationResult<User> GetUserByID(int userID)
{
var result = new OperationResult<User>();
try
{
var user = _userDAL.GetUserByID(userID);
if (user != null)
{
result.Success = true;
result.Message = "查询成功";
result.Data = user;
}
else
{
result.Success = false;
result.Message = "用户不存在";
}
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"查询失败:{ex.Message}";
}
return result;
}
/// <summary>
/// 验证邮箱格式
/// </summary>
private bool IsValidEmail(string email)
{
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
}
/// <summary>
/// 操作结果泛型类
/// </summary>
public class OperationResult<T> : OperationResult
{
public T Data { get; set; }
}
/// <summary>
/// 操作结果类
/// </summary>
public class OperationResult
{
public bool Success { get; set; }
public string Message { get; set; }
public object Data { get; set; }
public OperationResult()
{
Success = false;
Message = string.Empty;
}
}
}
```
BLL层有几个关键设计值得注意:
1. **统一的返回类型**:我使用了`OperationResult`类来封装所有方法的返回结果。这样UI层可以统一处理成功和失败的情况,不需要到处写try-catch。
2. **完整的业务验证**:每个方法都包含了必要的业务验证,比如用户注册时要检查用户名是否已存在、密码强度等。
3. **异常处理**:所有方法都包含try-catch块,确保异常不会直接抛给UI层,而是转换为友好的错误信息。
4. **事务控制**:虽然这个示例中没有复杂的事务操作,但在实际项目中,BLL层应该负责管理事务边界。比如用户注册时可能需要同时向多个表插入数据,这些操作应该在一个事务中完成。
### 4.2 添加密码加密功能
在实际项目中,密码绝对不能明文存储。我们需要在BLL层添加密码加密功能。修改`UserManager`类,添加密码加密相关方法:
```csharp
using System.Security.Cryptography;
using System.Text;
namespace UserManagement.BLL
{
public partial class UserManager
{
/// <summary>
/// 生成密码哈希
/// </summary>
private string HashPassword(string password)
{
using (var sha256 = SHA256.Create())
{
var bytes = Encoding.UTF8.GetBytes(password);
var hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
/// <summary>
/// 验证密码
/// </summary>
private bool VerifyPassword(string inputPassword, string storedHash)
{
var inputHash = HashPassword(inputPassword);
return inputHash == storedHash;
}
/// <summary>
/// 带加密的用户注册
/// </summary>
public OperationResult RegisterWithEncryption(User user, string plainPassword)
{
// 加密密码
user.Password = HashPassword(plainPassword);
return Register(user);
}
/// <summary>
/// 带加密的用户登录
/// </summary>
public OperationResult<User> LoginWithEncryption(string userName, string plainPassword)
{
var result = new OperationResult<User>();
try
{
var user = _userDAL.GetUserByName(userName);
if (user == null || !VerifyPassword(plainPassword, user.Password))
{
result.Success = false;
result.Message = "用户名或密码错误";
return result;
}
if (!user.IsActive)
{
result.Success = false;
result.Message = "用户已被禁用";
return result;
}
_userDAL.UpdateLastLoginTime(user.UserID);
result.Success = true;
result.Message = "登录成功";
result.Data = user;
}
catch (Exception ex)
{
result.Success = false;
result.Message = $"登录失败:{ex.Message}";
}
return result;
}
}
}
```
这里使用了SHA256进行密码哈希。在实际生产环境中,你可能还需要添加盐值(salt)来进一步增强安全性。
## 5. UI层:用户界面的实现
UI层是用户直接交互的界面。在三层架构中,UI层应该尽可能简单,只负责显示数据和收集用户输入,所有业务逻辑都应该委托给BLL层处理。
### 5.1 设计主界面
在`UserManagement.UI`项目中,打开默认的Form1,将其重命名为`MainForm`。我们需要设计一个包含以下功能的界面:
1. 用户列表显示(使用DataGridView)
2. 用户查询功能
3. 用户添加/编辑/删除按钮
4. 分页控件
首先,在窗体上添加必要的控件。这里是一个简单的布局示例:
| 控件类型 | 名称 | 用途 |
|---------|------|------|
| DataGridView | dgvUsers | 显示用户列表 |
| TextBox | txtSearchUserName | 按用户名搜索 |
| TextBox | txtSearchEmail | 按邮箱搜索 |
| ComboBox | cmbIsActive | 按状态筛选(全部/启用/禁用) |
| Button | btnSearch | 搜索按钮 |
| Button | btnAdd | 添加用户按钮 |
| Button | btnEdit | 编辑用户按钮 |
| Button | btnDelete | 删除用户按钮 |
| Label | lblPageInfo | 显示分页信息 |
| Button | btnPrev | 上一页按钮 |
| Button | btnNext | 下一页按钮 |
| NumericUpDown | numPageSize | 每页显示条数 |
### 5.2 实现用户列表显示
在`MainForm`的代码文件中,添加以下代码:
```csharp
using System;
using System.Windows.Forms;
using UserManagement.BLL;
using UserManagement.Model;
namespace UserManagement.UI
{
public partial class MainForm : Form
{
private UserManager _userManager;
private UserQuery _currentQuery;
private int _currentPage = 1;
private int _pageSize = 20;
private int _totalCount = 0;
public MainForm()
{
InitializeComponent();
_userManager = new UserManager();
_currentQuery = new UserQuery();
InitializeDataGridView();
LoadUsers();
}
private void InitializeDataGridView()
{
// 设置DataGridView的列
dgvUsers.AutoGenerateColumns = false;
dgvUsers.Columns.Clear();
dgvUsers.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = "UserID",
HeaderText = "用户ID",
Width = 80
});
dgvUsers.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = "UserName",
HeaderText = "用户名",
Width = 120
});
dgvUsers.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = "Email",
HeaderText = "邮箱",
Width = 150
});
dgvUsers.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = "Phone",
HeaderText = "电话",
Width = 120
});
dgvUsers.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = "CreateTime",
HeaderText = "创建时间",
Width = 150
});
dgvUsers.Columns.Add(new DataGridViewCheckBoxColumn
{
DataPropertyName = "IsActive",
HeaderText = "启用",
Width = 60
});
// 设置选择模式
dgvUsers.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgvUsers.MultiSelect = false;
}
private void LoadUsers()
{
try
{
// 更新查询条件
_currentQuery.UserName = txtSearchUserName.Text.Trim();
_currentQuery.Email = txtSearchEmail.Text.Trim();
if (cmbIsActive.SelectedIndex > 0)
{
_currentQuery.IsActive = cmbIsActive.SelectedIndex == 1;
}
else
{
_currentQuery.IsActive = null;
}
_currentQuery.PageIndex = _currentPage;
_currentQuery.PageSize = _pageSize;
// 调用BLL层获取数据
var result = _userManager.GetUsersPaged(_currentQuery);
if (result.Success)
{
dgvUsers.DataSource = result.Data.Data;
_totalCount = result.Data.TotalCount;
// 更新分页信息
UpdatePageInfo();
}
else
{
MessageBox.Show(result.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
dgvUsers.DataSource = null;
}
}
catch (Exception ex)
{
MessageBox.Show($"加载用户列表失败:{ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void UpdatePageInfo()
{
var totalPages = (int)Math.Ceiling(_totalCount / (double)_pageSize);
lblPageInfo.Text = $"第 {_currentPage} 页,共 {totalPages} 页,总计 {_totalCount} 条记录";
btnPrev.Enabled = _currentPage > 1;
btnNext.Enabled = _currentPage < totalPages;
}
private void btnSearch_Click(object sender, EventArgs e)
{
_currentPage = 1;
LoadUsers();
}
private void btnPrev_Click(object sender, EventArgs e)
{
if (_currentPage > 1)
{
_currentPage--;
LoadUsers();
}
}
private void btnNext_Click(object sender, EventArgs e)
{
var totalPages = (int)Math.Ceiling(_totalCount / (double)_pageSize);
if (_currentPage < totalPages)
{
_currentPage++;
LoadUsers();
}
}
private void numPageSize_ValueChanged(object sender, EventArgs e)
{
_pageSize = (int)numPageSize.Value;
_currentPage = 1;
LoadUsers();
}
}
}
```
### 5.3 实现用户添加/编辑功能
我们需要创建一个新的窗体`UserEditForm`用于添加和编辑用户:
```csharp
using System;
using System.Windows.Forms;
using UserManagement.BLL;
using UserManagement.Model;
namespace UserManagement.UI
{
public partial class UserEditForm : Form
{
private UserManager _userManager;
private User _currentUser;
private bool _isEditMode;
public UserEditForm(User user = null)
{
InitializeComponent();
_userManager = new UserManager();
_currentUser = user;
_isEditMode = user != null;
InitializeForm();
}
private void InitializeForm()
{
Text = _isEditMode ? "编辑用户" : "添加用户";
if (_isEditMode)
{
txtUserName.Text = _currentUser.UserName;
txtPassword.Text = _currentUser.Password;
txtPassword.Enabled = false; // 编辑时不显示密码
btnChangePassword.Visible = true;
txtEmail.Text = _currentUser.Email;
txtPhone.Text = _currentUser.Phone;
chkIsActive.Checked = _currentUser.IsActive;
}
else
{
btnChangePassword.Visible = false;
chkIsActive.Checked = true;
chkIsActive.Enabled = false; // 添加时默认启用
}
}
private void btnSave_Click(object sender, EventArgs e)
{
if (!ValidateInput())
return;
try
{
if (_isEditMode)
{
UpdateUser();
}
else
{
AddUser();
}
}
catch (Exception ex)
{
MessageBox.Show($"保存失败:{ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private bool ValidateInput()
{
if (string.IsNullOrWhiteSpace(txtUserName.Text))
{
MessageBox.Show("用户名不能为空", "验证错误",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtUserName.Focus();
return false;
}
if (!_isEditMode && string.IsNullOrWhiteSpace(txtPassword.Text))
{
MessageBox.Show("密码不能为空", "验证错误",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtPassword.Focus();
return false;
}
if (!string.IsNullOrWhiteSpace(txtEmail.Text) &&
!txtEmail.Text.Contains("@"))
{
MessageBox.Show("邮箱格式不正确", "验证错误",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtEmail.Focus();
return false;
}
return true;
}
private void AddUser()
{
var user = new User
{
UserName = txtUserName.Text.Trim(),
Password = txtPassword.Text, // 实际应该加密
Email = txtEmail.Text.Trim(),
Phone = txtPhone.Text.Trim(),
IsActive = chkIsActive.Checked
};
var result = _userManager.Register(user);
if (result.Success)
{
MessageBox.Show("用户添加成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
Close();
}
else
{
MessageBox.Show(result.Message, "添加失败",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void UpdateUser()
{
_currentUser.UserName = txtUserName.Text.Trim();
_currentUser.Email = txtEmail.Text.Trim();
_currentUser.Phone = txtPhone.Text.Trim();
_currentUser.IsActive = chkIsActive.Checked;
var result = _userManager.UpdateUser(_currentUser);
if (result.Success)
{
MessageBox.Show("用户更新成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
Close();
}
else
{
MessageBox.Show(result.Message, "更新失败",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void btnChangePassword_Click(object sender, EventArgs e)
{
using (var form = new ChangePasswordForm(_currentUser.UserID))
{
if (form.ShowDialog() == DialogResult.OK)
{
MessageBox.Show("密码修改成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
}
```
### 5.4 实现密码修改功能
添加一个`ChangePasswordForm`用于修改密码:
```csharp
using System;
using System.Windows.Forms;
using UserManagement.BLL;
using UserManagement.Model;
namespace UserManagement.UI
{
public partial class ChangePasswordForm : Form
{
private int _userID;
private UserManager _userManager;
public ChangePasswordForm(int userID)
{
InitializeComponent();
_userID = userID;
_userManager = new UserManager();
}
private void btnSave_Click(object sender, EventArgs e)
{
if (!ValidateInput())
return;
try
{
// 这里应该先验证旧密码,然后更新为新密码
// 为了简化,我们直接更新
var result = _userManager.GetUserByID(_userID);
if (result.Success && result.Data != null)
{
var user = result.Data;
user.Password = txtNewPassword.Text; // 实际应该加密
var updateResult = _userManager.UpdateUser(user);
if (updateResult.Success)
{
MessageBox.Show("密码修改成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
Close();
}
else
{
MessageBox.Show(updateResult.Message, "修改失败",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("用户不存在", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
MessageBox.Show($"修改失败:{ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private bool ValidateInput()
{
if (string.IsNullOrWhiteSpace(txtNewPassword.Text))
{
MessageBox.Show("新密码不能为空", "验证错误",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtNewPassword.Focus();
return false;
}
if (txtNewPassword.Text.Length < 6)
{
MessageBox.Show("密码长度不能少于6位", "验证错误",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtNewPassword.Focus();
return false;
}
if (txtNewPassword.Text != txtConfirmPassword.Text)
{
MessageBox.Show("两次输入的密码不一致", "验证错误",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtConfirmPassword.Focus();
return false;
}
return true;
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
}
}
```
### 5.5 在主窗体中添加用户管理功能
回到`MainForm`,添加以下事件处理方法:
```csharp
private void btnAdd_Click(object sender, EventArgs e)
{
using (var form = new UserEditForm())
{
if (form.ShowDialog() == DialogResult.OK)
{
LoadUsers(); // 刷新列表
}
}
}
private void btnEdit_Click(object sender, EventArgs e)
{
if (dgvUsers.SelectedRows.Count == 0)
{
MessageBox.Show("请选择要编辑的用户", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var selectedUser = dgvUsers.SelectedRows[0].DataBoundItem as User;
if (selectedUser == null)
return;
// 重新从数据库获取最新数据
var result = _userManager.GetUserByID(selectedUser.UserID);
if (!result.Success || result.Data == null)
{
MessageBox.Show("获取用户信息失败", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
using (var form = new UserEditForm(result.Data))
{
if (form.ShowDialog() == DialogResult.OK)
{
LoadUsers(); // 刷新列表
}
}
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (dgvUsers.SelectedRows.Count == 0)
{
MessageBox.Show("请选择要删除的用户", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var selectedUser = dgvUsers.SelectedRows[0].DataBoundItem as User;
if (selectedUser == null)
return;
if (MessageBox.Show($"确定要删除用户【{selectedUser.UserName}】吗?",
"确认删除", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
try
{
var result = _userManager.DeleteUser(selectedUser.UserID);
if (result.Success)
{
MessageBox.Show("删除成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
LoadUsers(); // 刷新列表
}
else
{
MessageBox.Show(result.Message, "删除失败",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
MessageBox.Show($"删除失败:{ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void dgvUsers_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0)
{
btnEdit_Click(sender, e);
}
}
```
## 6. 架构优化与扩展思考
通过上面的代码,我们已经完成了一个基本的三层架构用户管理系统。但在实际项目中,我们还可以从以下几个方面进行优化和扩展:
### 6.1 引入依赖注入
目前的代码中,`UserManager`直接实例化了`UserDAL`,这造成了紧耦合。我们可以使用依赖注入来解耦:
```csharp
// 首先定义一个接口
public interface IUserDAL
{
int AddUser(User user);
bool UpdateUser(User user);
bool DeleteUser(int userID);
User GetUserByID(int userID);
// ... 其他方法
}
// 修改UserDAL实现接口
public class UserDAL : IUserDAL
{
// 实现接口方法
}
// 修改UserManager,通过构造函数注入
public class UserManager
{
private readonly IUserDAL _userDAL;
public UserManager(IUserDAL userDAL)
{
_userDAL = userDAL;
}
// ... 其他代码
}
// 在UI层使用依赖注入容器(如Autofac、Microsoft.Extensions.DependencyInjection)
var services = new ServiceCollection();
services.AddScoped<IUserDAL, UserDAL>();
services.AddScoped<UserManager>();
var serviceProvider = services.BuildServiceProvider();
var userManager = serviceProvider.GetService<UserManager>();
```
### 6.2 添加日志记录
在BLL层添加日志记录,便于问题排查:
```csharp
using Microsoft.Extensions.Logging;
namespace UserManagement.BLL
{
public class UserManager
{
private readonly IUserDAL _userDAL;
private readonly ILogger<UserManager> _logger;
public UserManager(IUserDAL userDAL, ILogger<UserManager> logger)
{
_userDAL = userDAL;
_logger = logger;
}
public OperationResult Register(User user)
{
_logger.LogInformation("开始用户注册,用户名:{UserName}", user.UserName);
try
{
// ... 注册逻辑
_logger.LogInformation("用户注册成功,用户ID:{UserID}", userID);
return new OperationResult { Success = true, Message = "注册成功" };
}
catch (Exception ex)
{
_logger.LogError(ex, "用户注册失败,用户名:{UserName}", user.UserName);
return new OperationResult { Success = false, Message = $"注册失败:{ex.Message}" };
}
}
}
}
```
### 6.3 实现缓存机制
对于频繁访问但不经常变化的数据,可以添加缓存:
```csharp
using Microsoft.Extensions.Caching.Memory;
namespace UserManagement.BLL
{
public class UserManager
{
private readonly IUserDAL _userDAL;
private readonly IMemoryCache _cache;
public UserManager(IUserDAL userDAL, IMemoryCache cache)
{
_userDAL = userDAL;
_cache = cache;
}
public User GetUserByID(int userID)
{
var cacheKey = $"User_{userID}";
// 尝试从缓存获取
if (_cache.TryGetValue(cacheKey, out User cachedUser))
{
return cachedUser;
}
// 从数据库获取
var user = _userDAL.GetUserByID(userID);
if (user != null)
{
// 放入缓存,设置5分钟过期
_cache.Set(cacheKey, user, TimeSpan.FromMinutes(5));
}
return user;
}
public bool UpdateUser(User user)
{
var success = _userDAL.UpdateUser(user);
if (success)
{
// 更新缓存
var cacheKey = $"User_{user.UserID}";
_cache.Remove(cacheKey);
_cache.Set(cacheKey, user, TimeSpan.FromMinutes(5));
}
return success;
}
}
}
```
### 6.4 添加单元测试
为BLL层添加单元测试,确保业务逻辑的正确性:
```csharp
using Moq;
using Xunit;
namespace UserManagement.BLL.Tests
{
public class UserManagerTests
{
[Fact]
public void Register_ShouldFail_WhenUserNameExists()
{
// 安排
var mockDAL = new Mock<IUserDAL>();
mockDAL.Setup(x => x.UserNameExists("existinguser", null))
.Returns(true);
var userManager = new UserManager(mockDAL.Object);
var user = new User { UserName = "existinguser", Password = "password123" };
// 执行
var result = userManager.Register(user);
// 断言
Assert.False(result.Success);
Assert.Equal("用户名已存在", result.Message);
}
[Fact]
public void Register_ShouldSuccess_WhenValidInput()
{
// 安排
var mockDAL = new Mock<IUserDAL>();
mockDAL.Setup(x => x.UserNameExists("newuser", null))
.Returns(false);
mockDAL.Setup(x => x.AddUser(It.IsAny<User>()))
.Returns(1);
var userManager = new UserManager(mockDAL.Object);
var user = new User { UserName = "newuser", Password = "password123" };
// 执行
var result = userManager.Register(user);
// 断言
Assert.True(result.Success);
Assert.Equal("注册成功", result.Message);
}
}
}
```
### 6.5 考虑异步操作
对于可能耗时的操作,可以考虑使用异步方法:
```csharp
public interface IUserDAL
{
Task<int> AddUserAsync(User user);
Task<bool> UpdateUserAsync(User user);
Task<User> GetUserByIDAsync(int userID);
// ... 其他异步方法
}
public class UserManager
{
public async Task<OperationResult> RegisterAsync(User user)
{
try
{
// 异步检查用户名是否存在
var exists = await _userDAL.UserNameExistsAsync(user.UserName);
if (exists)
{
return new OperationResult { Success = false, Message = "用户名已存在" };
}
// 异步添加用户
var userID = await _userDAL.AddUserAsync(user);
return new OperationResult { Success = true, Message = "注册成功", Data = userID };
}
catch (Exception ex)
{
return new OperationResult { Success = false, Message = $"注册失败:{ex.Message}" };
}
}
}
```
### 6.6 配置文件管理
将配置信息集中管理,便于维护:
```csharp
public class AppSettings
{
public DatabaseSettings Database { get; set; }
public CacheSettings Cache { get; set; }
public LoggingSettings Logging { get; set; }
public class DatabaseSettings
{
public string ConnectionString { get; set; }
public int CommandTimeout { get; set; } = 30;
}
public class CacheSettings
{
public int DefaultExpirationMinutes { get; set; } = 5;
}
public class LoggingSettings
{
public string LogPath { get; set; }
public string LogLevel { get; set; } = "Information";
}
}
// 在Program.cs或启动类中加载配置
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
var appSettings = configuration.Get<AppSettings>();
```
通过以上优化,我们的三层架构应用变得更加健壮、可维护和可测试。每个层次都有明确的职责,代码结构清晰,便于团队协作和后续功能扩展。
在实际开发中,你可能会遇到更多复杂的需求,比如权限管理、数据验证、事务处理、性能优化等。但只要你遵循三层架构的基本原则——关注点分离、高内聚低耦合,就能构建出结构清晰、易于维护的应用程序。记住,好的架构不是一次性设计出来的,而是在不断重构和优化中逐渐形成的。