应用上下文(App/InternalApp)
本页介绍系统全局应用上下文的设计与用法,包括启动期注入、可用的“服务对象”清单、每个对象的作用与示例、以及服务解析和性能注意事项。
适用场景
- 在“非控制器/非中间件/静态方法/后台任务”中获取 DI 容器中的服务
- 快速访问配置、环境、HttpContext、当前用户、缓存、多语言、映射、选项配置等
内外部 App
说明:框架把“运行时实现/注入逻辑”与“对外调用入口”分成两个层次:
InternalApp(实现层)
- 实现位置:
Ape.Volo.Core/Internal/InternalApp.cs - 作用:在启动阶段写入环境、配置、RootServices,并维护临时 Scope/容器(UnmanagedObjects)、服务解析策略、Dispose 逻辑等内部细节。
- 特性:负责 ConfigureApplication 的三个重载注入、临时 ServiceProvider 的创建与回收,以及性能/线程安全相关的内部实现。
- 注意:InternalApp 包含框架内部用的 API,不推荐在业务代码中直接调用或依赖其内部状态。
- 实现位置:
App(对外层)
- 实现位置:
Ape.Volo.Core/App.cs - 作用:对外暴露安全、稳定的全局访问点(Configuration、HttpContext、GetService/GetRequiredService、GetOptions 等),并委托给 InternalApp 完成实际解析与生命周期管理。
- 特性:面向业务,提供简单友好的辅助方法,屏蔽 InternalApp 的实现细节与资源管理。
- 使用规范:在任意位置可通过
App快速获取服务或环境,但应遵守生命周期最佳实践(避免从 RootServices 长期持有 Scoped 实例;后台任务使用显式创建的 Scope 并在结束时释放)。
- 实现位置:
启动注入(Program.cs)
应用启动阶段会把关键上下文写入 InternalApp,并由 App 对外暴露统一入口:
csharp
// 配置容器与配置源
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
hostingContext.Configuration.ConfigureApplication(); // 注入 IConfiguration
config.Sources.Clear();
config.AddJsonFile(builder.Environment.IsDevelopment() ? "appsettings.Development.json" : "appsettings.json",
optional: true, reloadOnChange: false)
.AddJsonFile("IpRateLimit.json", optional: true, reloadOnChange: false);
})
.UseSerilogMiddleware()
.ConfigureContainer<ContainerBuilder>(b => b.RegisterModule(new AutofacExtensions()));
// 注入环境与 IServiceCollection
builder.ConfigureApplication();
var app = builder.Build();
// 注入 RootServices(IServiceProvider)
app.ConfigureApplication();InternalApp 的三个重载:
ConfigureApplication(WebApplicationBuilder):注入 Host/Web 环境与 Services;ConfigureApplication(IConfiguration):注入配置对象;ConfigureApplication(IHost):注入 RootServices。
服务对象一览与示例
以下属性/方法均通过 App 暴露,便于在任意位置调用。
1) Configuration(配置)
- 类型:
IConfiguration - 作用:读取应用配置。
- 示例:
csharp
var conn = App.Configuration["ConnectionStrings:Default"];2) WebHostEnvironment / HostEnvironment(环境)
- 类型:
IWebHostEnvironment/IHostEnvironment - 作用:判断当前环境(Development/Staging/Production)与内容根路径等。
- 示例:
csharp
if (App.WebHostEnvironment.IsDevelopment()) { /* dev-only */ }
var contentRoot = App.HostEnvironment.ContentRootPath;3) RootServices(根服务提供器)
- 类型:
IServiceProvider - 作用:框架构建完成后的根级 ServiceProvider。
- 建议:仅用于解析单例;解析 Scoped 服务应优先使用当前请求的 RequestServices。
4) HttpContext(请求上下文)
- 类型:
HttpContext - 作用:仅在 Web 请求期间可用;来自
IHttpContextAccessor。 - 示例:
csharp
var traceId = App.HttpContext?.TraceIdentifier;
var userAgent = App.HttpContext?.Request.Headers["User-Agent"].ToString();5) HttpUser(当前用户)
- 类型:
IHttpUser - 作用:封装当前登录用户的帐号、Id、TenantId 等。
- 示例:
csharp
var account = App.HttpUser?.Account;
var tenantId = App.HttpUser?.TenantId;6) Cache(缓存)
- 类型:
ICache - 作用:统一缓存抽象;在启用 Redis 时可获取底层数据库进行更丰富操作。
- 示例:
csharp
// 直接使用抽象
var cache = App.Cache;
// 使用底层 Redis 数据库(若启用)
await App.GetService<ICache>().GetDatabase()
.StringSetAsync("key", "value", TimeSpan.FromMinutes(5));7) Mapper(对象映射)
- 类型:
IMapper(AutoMapper) - 作用:DTO 与实体之间的映射。
- 示例:
csharp
var dto = App.Mapper.Map<UserDto>(userEntity);8) L(多语言)
- 类型:
ILocalizationService - 作用:多语言资源获取/格式化。
- 示例:
csharp
var text = App.L.R("Error.AppSettings.DataConnection");服务解析方法与差异
GetService / GetRequiredService
GetService<T>():找不到返回 null;GetRequiredService<T>():找不到抛异常(推荐在“必需依赖”场景使用)。
示例:
csharp
var logger = App.GetRequiredService<ILogger<MySvc>>();
var optional = App.GetService<IMaybeSvc>();GetServices(多实现解析)
csharp
var strategies = App.GetServices<IPaymentStrategy>();GetServiceProvider(提供器选择策略)
内部选择顺序:
- 若解析的服务为单例且 RootServices 可用 → 直接用 RootServices;
- 若当前存在 HttpContext → 使用
HttpContext.RequestServices; - 若 RootServices 可用 → 创建新的 Scope 返回其 ServiceProvider(会被加入 UnmanagedObjects,需释放);
- 否则临时构建一个新的 ServiceProvider(性能最差,亦需释放)。
释放方式:
csharp
// 在批量或后台场景,使用完后建议释放临时作用域/容器
App.DisposeUnmanagedObjects();选项(Options)辅助
已封装三种读取方式,对应 ASP.NET Core 的 Options 模型:
GetOptions<T>():等价IOptions<T>.Value(单例,适合稳定配置);GetOptionsMonitor<T>():等价IOptionsMonitor<T>.CurrentValue(支持变更监听);GetOptionsSnapshot<T>():等价IOptionsSnapshot<T>.Value(Scoped,针对每次请求快照)。
示例:
csharp
var system = App.GetOptions<SystemOptions>();
var serilog = App.GetOptionsMonitor<SerilogOptions>();
var middleware = App.GetOptionsSnapshot<MiddlewareOptions>();配合 Options 自动绑定与注册:详见《配置选项(Options)》。
运行时辅助
csharp
// 当前线程 Id
var tid = App.GetThreadId();
// 当前请求 TraceId(无请求时尝试从 Activity 或 HttpContext 获取)
var traceId = App.GetTraceId();
// 统计执行耗时(毫秒)
var cost = App.GetExecutionTime(() => DoWork());
// 查看服务生命周期(Singleton/Scoped/Transient)
var lifetime = App.GetServiceLifetime(typeof(IMyService));最佳实践与注意事项
- 解析 Scoped 服务应优先使用当前请求的
RequestServices;避免从 RootServices 解析 Scoped 服务并跨请求持有。 - 在后台批处理等场景若创建了临时作用域/容器,调用
App.DisposeUnmanagedObjects()及时释放,避免泄漏。 GetRequiredService用于“硬依赖”,可尽早暴露注册问题;GetService适合“可选依赖”。HttpContext可能为 null(如控制台任务/队列消费者),请注意空判断。- 读取可变配置建议使用
GetOptionsMonitor<T>();请求范围内的配置快照使用GetOptionsSnapshot<T>()。
常见问题(FAQ)
- 为什么
App.HttpContext为 null?
- 当前不在 Web 请求线程(如定时任务/消息消费);请使用显式创建的作用域注入所需服务。
- 解析到的服务生命周期不符合预期?
- 使用
App.GetServiceLifetime(type)检查注册;确保未从 RootServices 长期持有 Scoped 服务。
- 创建了很多作用域会泄漏吗?
- 框架会把临时作用域/容器加入
UnmanagedObjects;请在合适时机调用App.DisposeUnmanagedObjects(),框架内部带有 GC 间隔节流(默认 5 秒)。
相关文档:
- 《配置选项(Options)》
- 《多语言支持》
- 《DB 仓储》

