简介
在 C#.NET 中,主机 (Host) 是一个用于封装应用程序资源和生命周期管理的组件。它提供了一种统一的方式来配置、启动和停止应用程序,是构建现代 .NET 应用的基础架构。
普通主机 (HostBuilder)
普通主机是 .NET Core 2.1 引入的基础主机模型,主要用于非 Web 应用场景。它提供了以下核心功能:
- 依赖注入 (
DI) - 注册服务和组件 - 配置管理 - 读取应用配置
- 日志记录 - 统一日志系统
- 生命周期管理 - 控制应用启动和停止
普通主机的基本示例:
csharp
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// 注册服务
services.AddTransient<IMyService, MyService>();
})
.Build();
// 启动主机
await host.StartAsync();
// 应用逻辑...
// 停止主机
await host.StopAsync();
通用主机 (Generic Host)
通用主机是普通主机的增强版本,从 .NET Core 3.0 开始引入,专为构建非 HTTP 应用 (如控制台应用、Worker 服务、后台任务等) 而设计。
核心特性
-
内置依赖注入容器 - 支持
Scoped、Singleton和Transient生命周期 -
配置系统 - 支持多来源配置 (环境变量、命令行、
JSON文件等) -
日志框架 - 集成
Microsoft.Extensions.Logging -
IHostedService接口 - 定义后台服务的标准方式 -
环境区分 - 支持开发、测试和生产环境
通用主机创建 Worker 服务的示例:
csharp
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
// 主机配置
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// 添加Worker服务
services.AddHostedService<Worker>();
})
.ConfigureLogging(logging =>
{
logging.AddConsole();
})
.Build();
await host.RunAsync();
核心组件
-
IHost:主机接口,表示应用程序的运行时环境。 -
HostBuilder:用于配置和构建主机的构建器。 -
IHostedService:定义后台服务的接口,用于实现长时间运行的任务。 -
Dependency Injection (DI):内置依赖注入容器(Microsoft.Extensions.DependencyInjection)。 -
Configuration:支持从多种配置源加载设置。 -
Logging:集成的日志系统。
优点
-
统一性:为
Web和非Web应用程序提供一致的托管模型。 -
灵活性:支持自定义服务、配置和生命周期管理。
-
模块化:通过依赖注入和中间件机制实现模块化开发。
-
跨平台:适用于
Windows、Linux 和 macOS。 -
可扩展性:易于集成第三方库和自定义逻辑。
创建通用主机
通用主机通过 Host.CreateDefaultBuilder() 创建,它会自动配置一些默认行为(如日志、配置、依赖注入)。
csharp
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// 创建主机
using var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// 注册服务
services.AddHostedService<MyHostedService>();
})
.Build();
// 启动主机
await host.RunAsync();
}
}
配置主机
Host.CreateDefaultBuilder() 提供默认配置,包括:
-
配置源:加载
appsettings.json、环境变量、命令行参数。 -
日志:配置控制台日志和调试日志。
-
依赖注入:提供内置
DI容器。
可以通过 HostBuilder 自定义配置:
csharp
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
using var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
// 添加额外的配置源
config.AddJsonFile("customsettings.json", optional: true);
})
.ConfigureServices((context, services) =>
{
// 注册自定义服务
services.AddSingleton<IMyService, MyService>();
services.AddHostedService<MyHostedService>();
})
.ConfigureLogging(logging =>
{
// 配置日志
logging.AddConsole();
})
.Build();
await host.RunAsync();
}
}
实现 IHostedService
IHostedService 是通用主机中用于定义后台服务的接口,适合长时间运行的任务(如定时任务、消息队列处理)。它包含两个主要方法:
-
StartAsync:在主机启动时调用。 -
StopAsync:在主机关闭时调用。
csharp
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
public class MyHostedService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
// 启动逻辑
Console.WriteLine("MyHostedService is starting...");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
// 关闭逻辑
Console.WriteLine("MyHostedService is stopping...");
return Task.CompletedTask;
}
}
使用 BackgroundService
BackgroundService 是 IHostedService 的一个抽象基类,简化了后台任务的实现。它适合需要循环运行的任务(如定时任务)。
csharp
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
public class TimedHostedService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine($"TimedHostedService is running at {DateTime.Now}");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
}
注册服务:
csharp
services.AddHostedService<TimedHostedService>();
依赖注入
通用主机内置了依赖注入容器,可以注册服务并在应用程序中使用:
csharp
public interface IMyService
{
void DoWork();
}
public class MyService : IMyService
{
public void DoWork() => Console.WriteLine("MyService is working!");
}
public class MyHostedService : BackgroundService
{
private readonly IMyService _myService;
public MyHostedService(IMyService myService)
{
_myService = myService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_myService.DoWork();
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
}
注册服务:
csharp
services.AddSingleton<IMyService, MyService>();
services.AddHostedService<MyHostedService>();
通用主机的典型应用场景
-
控制台应用程序:如定时任务、数据处理脚本。
-
Windows服务或Linux守护进程:托管长时间运行的服务。 -
消息队列处理:处理
RabbitMQ、Kafka等消息队列。 -
后台任务:如日志清理、数据同步、通知服务。
-
ASP.NET Core Web应用:.NET 6及以上版本的Web应用也使用通用主机。
主机生命周期管理
主机提供了完整的生命周期管理,主要通过以下接口实现:
-
IHostedService- 定义启动和停止逻辑 -
IApplicationLifetime- 访问应用生命周期事件
IHostedService 接口
csharp
public interface IHostedService
{
Task StartAsync(CancellationToken cancellationToken);
Task StopAsync(CancellationToken cancellationToken);
}
生命周期事件
主机提供了三个主要的生命周期事件:
-
ApplicationStarted- 应用完全启动后触发 -
ApplicationStopping- 应用开始停止时触发 -
ApplicationStopped- 应用完全停止后触发
csharp
var host = Host.CreateDefaultBuilder(args)
.Build();
var applicationLifetime = host.Services.GetRequiredService<IApplicationLifetime>();
applicationLifetime.ApplicationStarted.Register(() =>
{
Console.WriteLine("应用已启动");
});
applicationLifetime.ApplicationStopping.Register(() =>
{
Console.WriteLine("应用正在停止...");
});
applicationLifetime.ApplicationStopped.Register(() =>
{
Console.WriteLine("应用已停止");
});
await host.RunAsync();
配置与依赖注入
- 配置管理
csharp
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
// 添加JSON配置文件
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
// 添加环境变量配置
config.AddEnvironmentVariables();
// 添加命令行参数配置
config.AddCommandLine(args);
})
.Build();
// 获取配置
var configuration = host.Services.GetRequiredService<IConfiguration>();
var value = configuration["MySetting"];
- 依赖注入
csharp
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// 注册单例服务
services.AddSingleton<IMySingletonService, MySingletonService>();
// 注册作用域服务
services.AddScoped<IMyScopedService, MyScopedService>();
// 注册瞬态服务
services.AddTransient<IMyTransientService, MyTransientService>();
// 注册主机服务
services.AddHostedService<MyBackgroundService>();
})
.Build();
背景与演进
ASP.NET Core 1.x--2.x 的 Web 主机
-
引入了
IWebHostBuilder、WebHost、Startup模式,专注于Web应用。 -
负责:
HTTP Server(Kestrel)、中间件管道、依赖注入、配置与日志等。
.NET Core 3.0+ 的通用主机(Generic Host)
-
为了在控制台服务、后台任务、
Worker Service、桌面应用等非Web场景中复用ASP.NET Core的依赖注入、配置、日志与生命周期管理,.NET Core 3.0推出了通用主机。 -
主要接口升级为
IHostBuilder/IHost,并在内部可按需挂载Kestrel、gRPC、SignalR、定时任务等功能。 -
在
.NET 6/.NET 7/.NET 8中持续完善,已成为构建所有.NET Core/5+应用的统一入口。
主机类型对比
| 特性/接口 | Web Host (IWebHostBuilder) |
Generic Host (IHostBuilder) |
|---|---|---|
| 适用场景 | 仅限 ASP.NET Core Web 应用 | 控制台、后台 Worker、Web 及混合场景 |
| 核心接口 | IWebHostBuilder → IWebHost |
IHostBuilder → IHost |
| 默认组件 | Kestrel、IIS 集成、中间件管道 | DI、配置(JSON/Env/Args)、日志、HostedService |
| Startup 支持 | Startup 类 => ConfigureServices、Configure |
无 Startup,服务配置与管道在 Program.cs 中完成 |
| 扩展灵活度 | 对 Web 优化,扩展槽较少 | 核心抽象化,扩展性极强 |
核心概念与关键接口
IHostBuilder & Host
-
Host.CreateDefaultBuilder(args)-
加载默认配置源(
appsettings.json、环境变量、命令行等) -
注册
ILogger、IConfiguration、IHostEnvironment -
注册
IHostedService管理生命周期
-
-
IHostBuilder-
.ConfigureAppConfiguration(...):自定义配置源 -
.ConfigureServices(...):注册依赖注入服务 -
.ConfigureLogging(...):配置日志提供器与级别 -
.UseServiceProviderFactory(...):替换DI容器(如Autofac)
-
-
IHost-
.StartAsync() / .RunAsync():启动所有IHostedService -
.StopAsync():优雅停止 -
.Services:获得顶级IServiceProvider
-
通用主机(Generic Host)常用扩展包
-
Worker Service(
Microsoft.Extensions.Hosting.WindowsServices、...Linux)(systemd) -
gRPC、SignalR、Grain(Orleans)等中间件 -
定时任务库:
IHostedService或BackgroundService
IHostedService 与 BackgroundService
-
IHostedService:定义StartAsync(CancellationToken)、StopAsync(CancellationToken) -
BackgroundService:抽象类,重写ExecuteAsync(CancellationToken),自动注册为单例IHostedService
csharp
public class TimedWorker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 周期性执行
await DoWorkAsync();
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
}
配置与启动流程解析
Program.cs(.NET 6+ 最简模板)
csharp
var builder = Host.CreateDefaultBuilder(args)
.ConfigureServices((hostCtx, services) =>
{
services.AddHostedService<TimedWorker>();
// 其它服务注册
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
});
var host = builder.Build();
await host.RunAsync();
配置加载顺序
-
appsettings.json -
appsettings.{Environment}.json -
环境变量(
ASPNETCORE_ENVIRONMENT) -
命令行参数
依赖注入生命周期
-
Singleton:与主机同生命周期 -
Scoped:在Web场景中每次请求新创建;在非HTTP场景,仅在显式IServiceScope中有效 -
Transient:每次注入新实例
日志管道
-
默认支持
Console、Debug、EventSource等 -
可接入
Seq、ELK、Application Insights等第三方Provider
主机构建流程
-
CreateDefaultBuilder注册服务与配置 -
用户链式调用
.ConfigureXxx()定制 -
Build()汇总并生成IHost -
RunAsync()启动注册的所有IHostedService
最小主机WebApplication
概念背景
-
最小主机(
Minimal Hosting) 出现在.NET 6中,旨在通过减少样板代码,用几行代码就能启动一个Web应用。 -
核心目标是"开箱即用":无需显式
Startup类,也不用手动创建IHostBuilder和IWebHostBuilder,而是统一通过WebApplicationBuilder构建。
基本概念
WebApplication 是一个集成了主机配置、服务注册和请求处理管道的单一类型,它结合了早期版本中的 HostBuilder、WebHostBuilder 和 Startup 类的功能。
核心特点包括:
-
单文件结构:通常在
Program.cs中完成所有配置 -
简化的依赖注入:直接通过
WebApplicationBuilder注册服务 -
最小
API:无需控制器,直接定义路由处理逻辑 -
隐式中间件:自动包含常用中间件(如路由、静态文件)
核心类型
| 类型 | 作用 |
|---|---|
WebApplicationBuilder |
构建器,负责加载配置、DI 注册、日志及 Kestrel 默认设置 |
WebApplication |
真正的应用主机,承载中间件管道、路由和运行生命周期 |
IEndpointRouteBuilder |
定义路由与端点,支持最小 API 风格 |
典型 Program.cs 模板
csharp
var builder = WebApplication.CreateBuilder(args);
// 1. 服务注册(依赖注入)
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
// 2. 构建应用
var app = builder.Build();
// 3. 中间件配置
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
// 4. 路由与端点
app.MapControllers();
app.MapGet("/ping", () => Results.Ok("pong"));
// 5. 启动
app.Run();
-
CreateBuilder:加载appsettings.json、环境变量、命令行等配置,注册日志与IHostEnvironment。 -
Services:在builder.Services上注册所需服务(DbContext、Identity、Swagger、HttpClient等)。 -
Build:生成WebApplication实例,此时DI容器与中间件管道均已准备完毕。 -
Middleware:在app上按顺序配置请求管道。 -
Endpoints:使用app.MapXXX定义路由。 -
Run:启动Kestrel(或IIS集成)并开始监听。
依赖注入与配置
csharp
var builder = WebApplication.CreateBuilder(args);
// 添加控制器支持
builder.Services.AddControllers();
// 添加 Swagger/OpenAPI 支持
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// 添加自定义服务
builder.Services.AddScoped<IMyService, MyService>();
var app = builder.Build();
csharp
var builder = WebApplication.CreateBuilder(args);
// 读取配置值
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
// 绑定配置到强类型对象
var settings = builder.Configuration.GetSection("AppSettings").Get<AppSettings>();
// 添加配置源
builder.Configuration.AddJsonFile("appsettings.Development.json", optional: true);
-
DI容器-
默认是
Microsoft.Extensions.DependencyInjection,支持三种生命周期:Singleton、Scoped、Transient。 -
在最小主机中,
builder.Services相当于传统ConfigureServices,而app.Services可以在运行时获取构建好的IServiceProvider。
-
-
配置(
Configuration)-
builder.Configuration已包含appsettings.json、appsettings.{Env}.json、环境变量、命令行。 -
可通过
builder.Configuration.GetSection("Section").Bind(myOptions)或者
csharpbuilder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MyOptions"));- 在端点中也可直接注入
IConfiguration或已绑定的IOptions<T>。
-
中间件与管道
csharp
// 配置中间件
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
// 映射控制器
app.MapControllers();
// 或使用最小 API
app.MapGet("/greet/{name}", (string name) => $"Hello, {name}!");
-
在
app上直接调用UseXxx即可。常见中间件:-
UseDeveloperExceptionPage()、UseHttpsRedirection() -
UseAuthentication()、UseAuthorization() -
UseCors()、UseStaticFiles()
-
-
顺序重要:认证/授权应在路由映射(
Map)前,静态文件一般要放在最前面或合适位置。
最小 API(Minimal API)
MapGet/MapPost/...:最简端点定义方式,无需控制器。
csharp
// 定义路由和处理逻辑
app.MapGet("/users", () =>
{
var users = new[] {
new { Id = 1, Name = "Alice" },
new { Id = 2, Name = "Bob" }
};
return Results.Ok(users);
});
app.MapPost("/users", (User user) =>
{
// 处理用户创建逻辑
return Results.Created($"/users/{user.Id}", user);
});
// 带参数的路由
app.MapGet("/users/{id}", (int id) =>
{
// 查找用户逻辑
var user = GetUserById(id);
return user is not null ? Results.Ok(user) : Results.NotFound();
});
-
自动绑定:路径参数、查询参数、主体和服务会自动注入到委托参数。
-
Results:用于生成各种HTTP响应,如Results.Ok(), Results.NotFound()等。
依赖注入与服务使用
csharp
app.MapGet("/todos", async (ITodoService todoService) =>
await todoService.GetAllTodos());
app.MapGet("/todos/{id}", async (int id, ITodoService todoService) =>
await todoService.GetTodoById(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
环境感知
通过 app.Environment 可以获取当前环境信息:
csharp
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
启动与生命周期
-
启动钩子
-
app.Lifetime.ApplicationStarted.Register(() => { /* 启动后 */ }); -
ApplicationStopping、ApplicationStopped类似。
-
-
优雅关闭
app.Run()默认会监听Ctrl+C(SIGINT)和SIGTERM,自动触发优雅停机。
-
后台任务
- 在最小主机中同样可注册
BackgroundService:
csharpbuilder.Services.AddHostedService<MyWorker>(); - 在最小主机中同样可注册
高级特性
- 过滤器
可以为路由添加全局或特定的过滤器:
csharp
// 全局过滤器
app.MapGet("/", () => "Hello World!")
.AddEndpointFilter(async (context, next) =>
{
Console.WriteLine("Before endpoint execution");
var result = await next(context);
Console.WriteLine("After endpoint execution");
return result;
});
- 组路由
组织相关路由到组中:
csharp
var usersGroup = app.MapGroup("/users");
usersGroup.MapGet("/", GetAllUsers);
usersGroup.MapGet("/{id}", GetUserById);
usersGroup.MapPost("/", CreateUser);
- 请求验证
csharp
app.MapPost("/products", (ProductRequest request) =>
{
if (!ModelState.IsValid)
{
return Results.ValidationProblem(ModelState);
}
// 处理有效请求
return Results.Created($"/products/{request.Id}", request);
});
public class ProductRequest
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(0, 100)]
public decimal Price { get; set; }
}
- 自定义中间件
csharp
app.Use(async (context, next) =>
{
// 请求前处理
var start = DateTime.UtcNow;
await next.Invoke();
// 请求后处理
var duration = DateTime.UtcNow - start;
logger.LogInformation($"Request took {duration.TotalMilliseconds}ms");
});
- 错误处理:
csharp
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new {
Error = "An unexpected error occurred"
});
});
});
与传统 Startup/Host 对比
| 特性 | 传统模式(.NET 5 及以前) | 最小主机(.NET 6+) |
|---|---|---|
| 样板代码 | Host.CreateDefaultBuilder + Startup 类 |
WebApplication.CreateBuilder |
| 服务注册分散 | ConfigureServices in Startup |
统一在 builder.Services |
| 中间件配置 | Configure in Startup |
直接在 app 上链式调用 |
| 路由定义 | 控制器 + MapControllers() |
最小 API + 控制器混合 |
| 可读性 | 分文件稍散 | 单文件、扁平化,更易快速浏览 |
实战示例:带 Swagger 的最小 API
csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
.WithName("SayHello")
.WithOpenApi();
app.Run();
最佳实践
-
按功能拆分文件:当路由和服务增多时,可将端点拆到拓展方法,如
app.MapUserEndpoints()。 -
中间件有序:注意静态文件、异常处理、认证授权的正确顺序。
-
Options验证:使用services.AddOptions<T>().Bind(...).ValidateDataAnnotations()提前发现配置错误。 -
日志分级:在
builder.Logging中清理或添加日志提供器,避免过多噪音。 -
单元测试:最小主机可通过
WebApplicationFactory<TEntryPoint>在测试中轻松启动。