选择 SqlSugar 是一个非常务实且极具前瞻性的决定!
在 .NET 生态中,SqlSugar 以其极高的执行效率(接近 Dapper)、极其强大的分表分库能力以及开箱即用的读写分离,成为了构建中大型项目的利器。对于百万级论坛来说,SqlSugar 原生支持的分表功能和二级缓存简直是量身定制的。
既然确定了 SqlSugar + 单体 MVC,我们就可以彻底抛弃笨重的“泛型仓储模式”,直接拥抱更现代、性能更高的 “垂直切片 + CQRS(读写分离)” 架构。
下面我为你梳理如何在使用 SqlSugar 的前提下,一步步落地这套百万级架构。
一、 目录结构设计(垂直切片模式)
不要再按 Models、Controllers、Services 来建文件夹了。按业务功能(Feature) 来组织代码。
ForumProject/
├── Core/ # 核心基础设施(与业务无关)
│ ├── Extensions/ # SqlSugar 注入、Redis 注入等扩展方法
│ ├── Pagination/ # 游标分页基类 (CursorPaging)
│ └── MediatRBehaviors/ # 全局日志、事务、缓存拦截器
├── Features/ # 业务切片(核心代码都在这里)
│ ├── Posts/ # 帖子模块
│ │ ├── GetPostList/ # 读操作 (Query)
│ │ │ ├── GetPostListQuery.cs
│ │ │ └── GetPostListHandler.cs
│ │ └── CreatePost/ # 写操作 (Command)
│ │ ├── CreatePostCommand.cs
│ │ ├── CreatePostHandler.cs
│ │ └── CreatePostValidator.cs
│ ├── Replies/ # 回复模块(未来这里会配置分表)
│ └── Users/ # 用户模块
├── Controllers/ # 极度轻量的 MVC 控制器(只做路由转发)
└── Program.cs
为什么这样好?
当你需要优化“帖子列表”的性能时,你只需要打开 Features/Posts/GetPostList 文件夹,修改里面的 Handler 和 SQL,绝对不会误伤到“发帖”或“用户”的逻辑。
二、 核心代码实战:SqlSugar 结合 CQRS
在 Handler 中,我们直接注入 ISqlSugarClient,不需要任何 IRepository 包装。
1. 读操作 (Query):高性能列表查询与 DTO 投影
论坛列表页最忌讳查出帖子的 Content(正文)大字段。利用 SqlSugar 的 .Select() 进行精准投影。
// 1. 定义 Query 请求与响应
public record GetPostListQuery(int BoardId, long LastPostId, int PageSize) : IRequest<List<PostListDto>>;
public record PostListDto(long Id, string Title, string AuthorName, int ReplyCount, DateTime CreateTime);
// 2. Handler 实现 (直接注入 ISqlSugarClient)
public class GetPostListHandler(ISqlSugarClient db) : IRequestHandler<GetPostListQuery, List<PostListDto>>
{
public async Task<List<PostListDto>> Handle(GetPostListQuery request, CancellationToken cancellationToken)
{
// 游标分页:WHERE Id < LastPostId,完美解决百万级深分页卡顿问题
var list = await db.Queryable<Post>()
.Where(p => p.BoardId == request.BoardId && p.Id < request.LastPostId)
.OrderBy(p => p.Id, OrderByType.Desc)
.Select(p => new PostListDto(
p.Id,
p.Title,
p.Author.Name, // SqlSugar 支持导航属性/Left Join 投影
p.ReplyCount,
p.CreateTime
))
.Take(request.PageSize)
.ToListAsync(cancellationToken);
return list;
}
}
2. 写操作 (Command):发帖与事务控制
发帖时,不仅要插入帖子表,还要更新板块的统计信息。
public record CreatePostCommand(int BoardId, string Title, string Content) : IRequest<long>;
public class CreatePostHandler(ISqlSugarClient db) : IRequestHandler<CreatePostCommand, long>
{
public async Task<long> Handle(CreatePostCommand request, CancellationToken cancellationToken)
{
long newPostId = 0;
// 使用 SqlSugar 的 Ado 事务
await db.Ado.UseTranAsync(async () =>
{
var post = new Post
{
BoardId = request.BoardId,
Title = request.Title,
Content = request.Content,
CreateTime = DateTime.Now
};
// 插入并返回自增 ID
newPostId = await db.Insertable(post).ExecuteReturnIdentityAsync(cancellationToken);
// 更新板块帖子总数 (+1)
await db.Updateable<Board>()
.SetColumns(b => b.PostCount == b.PostCount + 1)
.Where(b => b.Id == request.BoardId)
.ExecuteCommandAsync(cancellationToken);
});
return newPostId;
}
}
三、 百万级论坛的 SqlSugar “杀手锏”配置
SqlSugar 真正强大的地方在于它的底层配置,这决定了你的论坛能不能扛住百万级数据。
1. 回复表分表 (SplitTable) - 必做!
回复表是论坛数据增长最快的表(轻松破千万甚至上亿)。绝对不能放在一张表里。SqlSugar 支持极其优雅的分表。
// 实体类定义:按月份分表 (例如: Reply_202605, Reply_202606)
[SugarTable("Reply_{year}{month}")]
[SplitTable(SplitType.Month)]
public class Reply
{
[SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; }
[SplitField] // 标记为分表字段
public DateTime CreateTime { get; set; }
public long PostId { get; set; }
public string Content { get; set; }
}
// 查询时,SqlSugar 会自动路由到对应的表
var replies = await db.Queryable<Reply>()
.Where(r => r.PostId == 123 && r.CreateTime > DateTime.Now.AddMonths(-1))
.ToListAsync();
2. 读写分离配置
在 Program.cs 中配置 SqlSugar 时,直接开启读写分离,读请求自动走从库,写请求走主库,无需修改任何业务代码。
builder.Services.AddSingleton<ISqlSugarClient>(provider =>
{
var db = new SqlSugarScope(new ConnectionConfig()
{
ConnectionString = "主库连接字符串",
DbType = DbType.MySql,
IsAutoCloseConnection = true,
// 配置从库
SlaveConnectionConfigs = new List<SlaveConnectionConfig>()
{
new SlaveConnectionConfig() { ConnectionString = "从库1连接字符串", Weight = 10 },
new SlaveConnectionConfig() { ConnectionString = "从库2连接字符串", Weight = 10 }
}
});
return db;
});
🚀 你的下一步行动
现在你的架构方向(垂直切片 + CQRS)和 ORM 工具(SqlSugar)都已经明确了。为了让你的项目真正跑起来并具备高性能,我们还需要解决一些具体的落地细节。
请告诉我,你接下来最想先看哪一部分的详细代码或方案?
- SqlSugar 的依赖注入与全局配置(如何优雅地在 .NET 10 中注册 SqlSugar,并配置 AOP 拦截器来记录慢 SQL)。
- MediatR 的管道行为 (Pipeline Behavior)(如何写一个全局拦截器,自动处理所有 Query 的 Redis 缓存,以及所有 Command 的数据库事务)。
- 前端 MVC 的适配(在 Controller 中如何优雅地调用 MediatR,并结合 HTMX 或 AJAX 实现无刷新加载和游标分页)。
选择一个,我们继续深入!