Blazor Admin 请求到底是怎么跑起来的?从页面到数据库的完整调用链
本文对应版本EasyAdminBlazor 2.3.0。源码位置EasyAdminBlazor/AdminExtensions.cs注册与中间件EasyAdminBlazor/AdminContext.cs及 8 个 partialAuth/Authorization/Cookie/Favorites/Navigation/Tenant/UserEasyAdminBlazor/Components/AdminTable.razor、Components/Layout/MainLayout.razor(.cs)EasyAdminBlazor.Core/实体与抽象、Extensions/可选扩展EasyAdminBlazor.Test/Program.cs宿主接入已发布的《从项目结构到核心架构》讲的是目录和分层。这篇讲运行时的调用链一次页面访问、一次表格查询、一次保存代码到底经过了哪些地方。一、先看依赖方向EasyAdminBlazor.Test 宿主项目Program.cs 实体 页面 │ 引用 ▼ EasyAdminBlazor 主包组件 AdminContext 文件/日志/安全 │ 引用 ▼ EasyAdminBlazor.Core 实体 抽象接口ICacheService / ITenantService / ISchedulerService / IApprovalGateway / SecurityOptions … Extensions/* 可选扩展反向依赖主包通过 DI 替换抽象实现关键点扩展是替换实现而不是改主包代码。核心包里注册的都是空实现或默认实现// AdminExtensions.csbuilder.Services.TryAddSingletonITenantService,NullTenantService();builder.Services.TryAddSingletonICacheService,MemoryCacheService();builder.Services.TryAddSingletonISchedulerService,NullSchedulerService();builder.Services.TryAddScopedIApprovalGateway,NullApprovalGateway();builder.Services.TryAddTransientIEditorComponent,DefaultEditorPlugin();builder.Services.TryAddTransientIChatComponent,DefaultChatPlugin();builder.Services.TryAddTransientIEmailService,DefaultEmailService();builder.Services.TryAddSingletonISmsService,DefaultSmsService();扩展则用TryAdd*/RemoveAll覆盖它们例AddEasyAdminBlazorMultiTenant会RemoveAllITenantService()再注册MultiTenantService。这是整个框架能按需安装的根本原因主包只依赖接口扩展提供实现。二、启动期AddEasyAdminBlazor 做了四件事1. 建主库 ORM 并初始化种子数据varmainBuildernewFreeSqlBuilder().UseNoneCommandParameter(true);if(options.FreeSqlBuilder!null)options.FreeSqlBuilder.Invoke(mainBuilder);elsemainBuilder.UseConnectionString(DataType.Sqlite,Data Sourcemaster.db).UseMonitorCommand(cmdSystem.Console.WriteLine(...)).UseAutoSyncStructure(true);varfsqlmainBuilder.Build();FreeSqlConfiguration.Configure(fsql);SeedData.InitSeedData(fsql);builder.Services.AddSingleton(newMainOrmHandle(fsql));注意new MainOrmHandle(fsql)注册为Singleton这是多租户能成立的前提AdminContext需要同时持有主库引用和当前租户库引用。2. 注册仓储与审计// 用户部门if(e.AuditValueTypeAuditValueType.Inserte.ObjectisIDataPermissionobj3obj3!null){obj3.OrgIduser.OrgId;return;}builder.Services.AddScoped(typeof(IBaseRepository),typeof(BasicRepository));builder.Services.AddScoped(typeof(IAggregateRootRepository),typeof(DddRepository));DddRepository.Select返回base.SelectDiy所以AdminTable注入IAggregateRootRepositoryTItem后能直接拿到ISelectTItem。3. 注册基础设施类别注册内容认证自定义登录票据认证处理器 默认认证方案仅在宿主未指定时生效CSRFAddAntiforgeryEasyAdminAntiforgeryFilter实时AddSignalRKeepAlive 15s / ClientTimeout 3min Circuit 保留配置缓存TryAddSingletonICacheService, MemoryCacheService()日志DatabaseLoggerQueueDatabaseLoggerBackgroundServiceDatabaseLoggerProvider本地化AddRequestLocalizationCookie Accept-Language服务OperationLogService/PrintService/AdminMessageService/FileService后台上下文AddScopedAdminContext()4. 映射 HubpublicstaticWebApplicationUseEasyAdminBlazor(thisWebApplicationapp){// 映射 SignalR 通知 Hubapp.MapHubNotificationHub(/hubs/notification);returnapp;}UseEasyAdminBlazor()只做这一件事——这也是它必须在app.MapRazorComponentsApp()之后调用的原因。三、请求期一次后台页面访问以打开产品管理页为例浏览器 GET /admin/xxx/Admin/Product ↓ EasyAdminAuthStartupFilter中间件 ↓ 校验 AdminRouteSecret不带正确安全码直接 404 ↓ MapRazorComponentsApp() → Routes → MainLayout ↓ MainLayout.OnInitializedAsync → admin.Init() ↓ 解密登录 Cookie → 加载 SysUser → 检查多端登录 ↓ Layout 的 OnAuthorizing(path) → admin.AuthPath(path) ↓ 加载角色与菜单含缓存→ 匹配 PathLower → 设置 CurrentMenu ↓ admin.AuthPathSuccess true → 渲染 Body ↓ Product.razor → AdminTable.OnParametersSetAsync ↓ AuthButton(add/edit/remove) 决定按钮显隐 ↓ OnQueryDataAsync → 查询登录态是怎么读出来的publicasyncTaskInit(){if(!string.IsNullOrEmpty(_adminOptions.AdminRouteSecret)){varpathnewUri(nav.Uri).AbsolutePath;if(path.StartsWith(/admin/,StringComparison.OrdinalIgnoreCase)||path.Equals(/admin,StringComparison.OrdinalIgnoreCase)){varsecretCookieawaitGetCookie($easyadminblazor_secret_{_adminOptions.CookieName});if(secretCookie!_adminOptions.AdminRouteSecret){// 强制刷新页面触发 EasyAdminAuthStartupFilter 中间件实现 404 拦截nav.NavigateTo(nav.Uri,forceLoad:true);return;}}}varcookieawaitGetCookie(CookieKey);if(cookie!nullTryParseCookie(cookie,outvaruserId,outvarloginTime)userId0){UserawaitOrm.SelectSysUser().Where(aa.IduserId).FirstAsync();if(Usernull){awaitSignOut();RedirectLogin();return;}awaitRemoveCache();// 检查多端登录覆盖if(!awaitCheckOtherLogin(User,loginTime))return;}else{awaitSignOut();}}三个细节AdminRouteSecret是双重校验中间件层面对/admin/...直接 404组件层再校验一次 Cookie缺失就强制整页刷新回到中间件拦截。登录票据是加密字符串内容是userId|loginTimevarencryptedData_loginTicketProtector.Protect(${user.Id}|{user.LoginTime.ToString(LoginTimeFormat,CultureInfo.InvariantCulture)});loginTime参与多端登录判断EnableKickOnOtherLogin打开时后登录会把前一端的LoginTime改掉前端的CheckOtherLogin发现不一致就踢下线。四、数据链路从组件到数据库AdminTableTItem ↓ GetSelect() IAggregateRootRepositoryTItemDI 注入的 DddRepository ↓ .Select → base.SelectDiy FreeSql ISelectTItem ↓ ApplyDataPermission / WhereDynamicFilter / ApplyOrder / Page SQL → 数据库AdminContext.Orm是租户感知的入口/// summary/// 获取 FreeSql 实例多租户模式下自动切换到租户数据库。/// /summarypublicIFreeSqlOrmTenant!null?_tenantService!.GetTenantFreeSql(Tenant.Code):_mainOrmHandle.Orm;所以注入IFreeSql的服务如FileService、ApprovalService会自动拿到当前租户库——多租户对业务代码是透明的。五、Blazor Server 带来的两个特殊处理1. HttpContext 在 SignalR 回调里可能不可用AdminContext不缓存HttpContext实例并且对WebSocket 请求单独处理publicvoidRedirect(stringurl){varfinalUrlurl;if(_pendingLoginTicket!null){finalUrl$/api/sys/auth/set-cookie?ticket{_pendingLoginTicket}remember{_pendingLoginRemember}redirect{Uri.EscapeDataString(url)};_pendingLoginTicketnull;}...varctxGetHttpContextSafely();if(ctxnull||IsWebSocketRequest(ctx)||ctx.Response.HasStarted){nav.NavigateTo(finalUrl,true);}else{ctx.Response.Redirect(finalUrl);}}IsWebSocketRequest()为真时不能用Response.Redirect响应已经开始/是长连接改成客户端导航需要写 Cookie 的操作则先放进_pendingLoginTicket再由专门的路由/api/sys/auth/set-cookie完成。2. 租户解析要缓存publicSysTenant?Tenant{get{if(!_tenantResolved){_cachedTenant_tenantService?.GetCurrentTenant();_tenantResolvedtrue;}return_cachedTenant;}}Circuit 生命周期很长HttpContext却可能已经不可用所以解析结果要缓存同时提供InvalidateTenantCache()/SetTenant()让切换与失效可控第 13 篇。六、一次保存的完整链路编辑弹窗点保存 ↓ EditContextCapture.Validate() 客户端校验 ↓ OnSaveAsync → AdminTable.OnSaveDataAsync ↓ admin.AuthButton(add/edit) 服务端权限校验 ↓ FilterAuthorizedAsync更新时 数据权限回查 ↓ OnBeforeSaveAsync 页面业务校验 ↓ ApprovalGateway.CheckModifyAsync 审批中禁止修改 ↓ _repo.InsertAsync / UpdateAsync ↓ AuditValue 填充创建人/时间/OrgId ↓ 提交后审批自动提交若配置 OnFinishSaveAsync 清草稿这条链路里出现的AdminContext、IAggregateRootRepository、IApprovalGateway都是 DI 注入的所以每个环节都可以被扩展替换比如自定义IApprovalGateway。七、扩展的插入点一览想改什么替换哪个抽象用哪个扩展/方式缓存实现ICacheServiceAddEasyAdminBlazorFusionCache()多租户ITenantServiceAddEasyAdminBlazorMultiTenant()定时任务ISchedulerServiceAddEasyAdminBlazorScheduler()审批IApprovalGateway/IApprovalServiceAddEasyAdminBlazorApproval()富文本IEditorComponentAddEasyAdminBlazorTinyMCEEditor()聊天IChatComponentAddEasyAdminBlazorChat()邮件 / 短信IEmailService/ISmsServiceAddEasyAdminBlazorMail()/AddEasyAdminBlazorAliyunSms()日志落库ILoggerProvider内置DatabaseLoggerProvider可关闭八、一张总图┌──────────────── 启动期 ────────────────┐ Program.cs → AddEasyAdminBlazor() ├─ 构建主库 FreeSqlMainOrmHandle, Singleton ├─ SeedData 初始化菜单/角色/管理员 ├─ 注册仓储、审计、缓存、日志队列、本地化 └─ 注册空实现接口等待扩展替换 app.UseEasyAdminBlazor() → MapHub(/hubs/notification) └─────────────────────────────────────────┘ ┌──────────────── 请求期 ────────────────┐ HTTP 请求 → EasyAdminAuthStartupFilterAdminRouteSecret / 404 → MainLayout → admin.Init()票据解密 → SysUser → 多端登录 → admin.AuthPath(path)角色菜单 → CurrentMenu → Body 渲染 → AdminTableIsSelect → 数据权限 → 动态筛选 → 分页 → 保存/删除AuthButton → 数据权限 → 事务/审批 └─────────────────────────────────────────┘ ┌──────────────── 数据层 ────────────────┐ AdminContext.Orm →有租户 ? 租户库 : 主库→ FreeSql → 数据库 └─────────────────────────────────────────┘九、小结理解这套架构抓住三句话就够了主包只依赖接口扩展替换实现——所以功能可以按需安装、按需关闭AdminContext是运行时中枢——用户、角色、菜单、配置、租户、导航都在它身上其余组件围绕它工作Blazor Server 没有 HTTP 边界——页面、权限、数据访问都在同一进程里链路短但要专门处理 Circuit 带来的HttpContext 不可用和长生命周期缓存问题。下一篇会顺着这条链路回答一个常见质疑既然没有 Controller、Service、DTO那这套架构是怎么保证可维护性的如果你正在用 .NET 10 Blazor 做后台想搞清楚框架到底帮你做了什么可以直接对着这篇的调用链读源码每一段都能在仓库里找到对应文件。文档https://easyadmin.wang-zhan.com.cn/doc源码https://gitee.com/gudufy/EasyAdminBlazor

相关新闻

SQLDelight 类型投影(Type Projections)与自定义 Mapper 实战指南

SQLDelight 类型投影(Type Projections)与自定义 Mapper 实战指南

后端ORM 【免费下载链接】sqldelight SQLDelight - Generates typesafe Kotlin APIs from SQL 项目地址: https://gitcode.com/gh_mirrors/sq/sqldelight 点击查看 免费下载 SQLDelight 默认会把每条 SELECT 查询的结果集映射为自动生成的数据类,但实际…

2026/10/14 2:21:48 阅读更多 →
MCP for Beginners 课程变更史:从 changelog.md 看 MCP 2026-07-28 规范演进与课程实战化路径

MCP for Beginners 课程变更史:从 changelog.md 看 MCP 2026-07-28 规范演进与课程实战化路径

教程文档人工智能 【免费下载链接】mcp-for-beginners This open-source curriculum introduces the fundamentals of Model Context Protocol (MCP) through real-world, cross-language examples in .NET, Java, TypeScript, JavaScript, Rust and Python. Designed for deve…

2026/10/9 14:48:15 阅读更多 →
Nuxt UI InputDate 组件实战指南:日期输入、范围选择与日期选择器的完整解析

Nuxt UI InputDate 组件实战指南:日期输入、范围选择与日期选择器的完整解析

前端UI组件 【免费下载链接】ui The Intuitive Vue UI Library powered by Reka UI & Tailwind CSS. 项目地址: https://gitcode.com/gh_mirrors/ui4/ui 点击查看 免费下载 UInputDate 是 Nuxt UI(本仓库 ui 项目)内置的日期输入组件&am…

2026/10/11 2:11:11 阅读更多 →

最新新闻

风电水电联合调度优化算法与工程实践

风电水电联合调度优化算法与工程实践

1. 项目背景与核心价值去年参与某省级电网清洁能源消纳项目时,我第一次接触到风-水联合调度这个课题。当时面对风电出力的强随机性和水电调节的滞后性,团队在制定优化运行方案时遇到了不少挑战。这个经历让我深刻认识到:在双碳目标下&#xf…

2026/10/14 2:21:24 阅读更多 →
从PowerDesigner迁移到PDMan:开源数据库建模与Git版本管理实践

从PowerDesigner迁移到PDMan:开源数据库建模与Git版本管理实践

1. 为什么我最终把数据库建模从商业软件迁到了PDMan做后端开发时间长了,数据库建模这件事几乎绕不开。早些年团队里清一色用PowerDesigner,画物理模型、生成DDL、导出字段文档,一套流程走下来确实顺手。但问题也很明显:正版授权费…

2026/10/14 2:21:24 阅读更多 →
微信小程序仿智行火车票开发实战:云开发+数据离线化方案

微信小程序仿智行火车票开发实战:云开发+数据离线化方案

简介:本资源是一套完整的微信小程序仿智行火车票12306系统实战项目,面向Java后端与小程序前端初学者及全栈学习者,聚焦铁路订票核心业务场景,涵盖用户查询、车次管理、订单生成等全流程功能实现。项目采用SSM(SpringSp…

2026/10/14 2:21:23 阅读更多 →
RTMP Flash播放插件替代方案:flv.js迁移实战与参数调优

RTMP Flash播放插件替代方案:flv.js迁移实战与参数调优

简介:这份资源是面向需要在网页中播放RTMP流媒体的开发者与运维人员的一套Flash播放插件实现方案,核心解决旧系统或特定场景下通过Flash技术播放RTMP直播、点播流的问题。压缩包共5个文件,约1.15MB,包含2个swf播放器与安装检测组件…

2026/10/14 2:21:23 阅读更多 →
AI编程助手代码质量救星:TypeScript静态类型约束实战

AI编程助手代码质量救星:TypeScript静态类型约束实战

AI 编程助手越帮越乱,这个话题我最近真的是感同身受。我们团队接入 AI 编程助手之后,合并请求里的代码量倒是上去了,可编译期错误非但没减少,反而冒出来一批"语法完全合法、类型对不上、一跑就崩"的代码。后来大家坐下来…

2026/10/14 2:21:23 阅读更多 →
LaTeX 2025环境配置:texlive + TeXstudio

LaTeX 2025环境配置:texlive + TeXstudio

文章目录Latex2025环境配置一、LaTeX 简介1.1 什么是 LaTeX?1.2 为什么使用 LaTeX?1.3 如何开始使用 LaTeX?二、texlive下载安装2.1 texlive下载2.2 texlive安装2.3 常见问题2.3.1 用管理员权限打开install-tl-windows.bat 报错2.3.2 Latex安…

2026/10/14 2:20:23 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →