.NET 7 中的 HostApplicationBuilder

.NET 7 Preview 3 引入的 HostApplicationBuilder

Intro

在 .NET 6 中,ASP.NET Core 引入了 Minimal API,对于简单的应用使用 Minimal API 我们可以使用非常精简的代码来实现我们的 API,在 .NET 7 Preview 3 中,引入了一个 HostApplicationBuilder ,我们使用普通的 Host 也可以使用 Minimal API 的方式来配置 Host 的 Logging/Configuration/Services等,下面我们来看一下如何使用吧

Sample

首先为可能不熟悉 Minimal API 使用的童鞋看一下 Minimal API 的简单使用:

var builder = WebApplication.CreateBuilder(args);

builder.Logging.AddJsonConsole(options =>
{
    options.JsonWriterOptions = new JsonWriterOptions
    {
        // https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-encoding?WT.mc_id=DT-MVP-5004222
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };
});

builder.Services.AddDbContextPool<SparkTodoDbContext>(options => options.UseInMemoryDatabase("SparkTodo"));

var app = builder.Build();

app.Map("/health", ()=> "Healthy");

await app.RunAsync();

Minimal API 中的 Logging/Configuration/Services 相对来说更为简单一些,更多可以参考之前的一个 Todo 示例:Minimal API Todo Sample

HostApplicationBuilder 使得我们也可以使用这样的配置方式,示例如下:

var builder = Host.CreateApplicationBuilder();
        
builder.Logging.AddJsonConsole(config =>
{
    config.UseUtcTimestamp = true;
    config.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff";
    config.JsonWriterOptions = new System.Text.Json.JsonWriterOptions()
    {
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        Indented = true
    };
});

builder.Configuration.AddJsonFile("env.json", true);

builder.Services.AddHostedService<TestHostedService>();

var host = builder.Build();
await host.StartAsync();

上面的 TestHostedService 是一个简单的每秒输出一下当前时间的后台任务, 使用 .NET 6 引入的 PeriodicTimer,实现如下:

private sealed class TestHostedService : BackgroundService
{
    protected async override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
        while (await timer.WaitForNextTickAsync(stoppingToken))
        {
            Console.WriteLine($"{DateTime.Now}");
        }
    }
}

示例运行效果如下:

4f12be9516d008fdcd88854e432e5fd1.png

Inside

内部是怎么实现的呢?我们可以使用 Host.CreateApplicationBuilder() 也可以使用 new HostApplicationBuilder() 来创建

在创建的时候我们可以指定一个 HostApplicationBuilderSettings 来初始化 Host 的一些配置

Host.CreateApplicationBuilder() 实现如下:

public static HostApplicationBuilder CreateApplicationBuilder() => new HostApplicationBuilder();

public static HostApplicationBuilder CreateApplicationBuilder(string[]? args) => new HostApplicationBuilder(args);

Host.CreateApplicationBuilder()Host.CreateDefaultBuilder() 的行为保持一致,也会加载默认 DOTNET_ 环境变量作为 Host Configuration 以及加载 appsettings.json 环境变量、 注册 Logging 等,详细可以参考:https://github.com/dotnet/runtime/blob/0e84780fd2280166cd82374c770177613527f06b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs

HostApplicationBuilder 有三个构造方法

public sealed class HostApplicationBuilder
{
    private readonly HostBuilderContext _hostBuilderContext;
    private readonly ServiceCollection _serviceCollection = new();

    private Func<IServiceProvider> _createServiceProvider;
    private Action<object> _configureContainer = _ => { };
    private HostBuilderAdapter? _hostBuilderAdapter;

    private IServiceProvider? _appServices;
    private bool _hostBuilt;

    public HostApplicationBuilder()
        : this(args: null)
        {
        }
    public HostApplicationBuilder(string[]? args)
        : this(new HostApplicationBuilderSettings { Args = args })
        {
        }

    public HostApplicationBuilder(HostApplicationBuilderSettings? settings)
    {
        // ...
    }
}

由于代码过多,这里不贴太多具体实现的代码,感兴趣的可以看源码实现 https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs

可以看到上面的代码中,会有一个 HostApplicationBuilderSettings 来决定 Host 的构建行为,我们来看一下它的源码

public sealed class HostApplicationBuilderSettings
{
    public bool DisableDefaults { get; set; }

    public string[]? Args { get; set; }

    public ConfigurationManager? Configuration { get; set; }

    public string? EnvironmentName { get; set; }

    public string? ApplicationName { get; set; }

    public string? ContentRootPath { get; set; }
}

除了 HostingEnvironment 相关的参数之外,增加了 DisableDefault/Args/Configuration 三个参数

  • DisableDefaults 前面我们提到 Host.CreateApplicationBuilder 会和 Host.CreateDefaultBuilder 的行为保持一致,这样提供了一个可以禁用默认配置的选项

  • Args 是从命令行获取到的参数,会作为默认的一个配置源,当然只有 DisableDefaultsfalse 时才会生效

  • Configuration 使得我们可以使用一个已有的配置信息

构造方法里的主要实现如下:

settings ??= new HostApplicationBuilderSettings();
Configuration = settings.Configuration ?? new ConfigurationManager();

if (!settings.DisableDefaults)
{
    HostingHostBuilderExtensions.ApplyDefaultHostConfiguration(Configuration, settings.Args);
}

// HostApplicationBuilderSettings override all other config sources.
List<KeyValuePair<string, string?>>? optionList = null;
if (settings.ApplicationName is not null)
{
    optionList ??= new List<KeyValuePair<string, string?>>();
    optionList.Add(new KeyValuePair<string, string?>(HostDefaults.ApplicationKey, settings.ApplicationName));
}
if (settings.EnvironmentName is not null)
{
    optionList ??= new List<KeyValuePair<string, string?>>();
    optionList.Add(new KeyValuePair<string, string?>(HostDefaults.EnvironmentKey, settings.EnvironmentName));
}
if (settings.ContentRootPath is not null)
{
    optionList ??= new List<KeyValuePair<string, string?>>();
    optionList.Add(new KeyValuePair<string, string?>(HostDefaults.ContentRootKey, settings.ContentRootPath));
}
if (optionList is not null)
{
    Configuration.AddInMemoryCollection(optionList);
}

(HostingEnvironment hostingEnvironment, PhysicalFileProvider physicalFileProvider) = HostBuilder.CreateHostingEnvironment(Configuration);

Configuration.SetFileProvider(physicalFileProvider);

_hostBuilderContext = new HostBuilderContext(new Dictionary<object, object>())
{
    HostingEnvironment = hostingEnvironment,
    Configuration = Configuration,
};

Environment = hostingEnvironment;

HostBuilder.PopulateServiceCollection(
    Services,
    _hostBuilderContext,
    hostingEnvironment,
    physicalFileProvider,
    Configuration,
    () => _appServices!);

Logging = new LoggingBuilder(Services);

ServiceProviderOptions? serviceProviderOptions = null;

if (!settings.DisableDefaults)
{
    HostingHostBuilderExtensions.ApplyDefaultAppConfiguration(_hostBuilderContext, Configuration, settings.Args);
    HostingHostBuilderExtensions.AddDefaultServices(_hostBuilderContext, Services);
    serviceProviderOptions = HostingHostBuilderExtensions.CreateDefaultServiceProviderOptions(_hostBuilderContext);
}

_createServiceProvider = () =>
{
    _configureContainer(Services);
    return serviceProviderOptions is null ? Services.BuildServiceProvider() : Services.BuildServiceProvider(serviceProviderOptions);
};

从这里可以比较清晰的看得出来前面 HostApplicationBuilderSettings 中的参数的使用了

More

在新版本 Minimal API 中的 WebApplicationBuilder 内部也使用了 HostApplicationBuilder 来重构之前的实现,感兴趣的童鞋可以看一下 https://github.com/dotnet/aspnetcore/blob/main/src/DefaultBuilder/src/WebApplicationBuilder.cs

9204cd009bce36fdaedef612ac6f4dc4.png

更多细节可以参考文末给出的源码链接

References

  • https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs

  • https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilderSettings.cs

  • https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs

  • https://github.com/dotnet/runtime/blob/0e84780fd2280166cd82374c770177613527f06b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs

  • https://github.com/dotnet/runtime/blob/9299bc47f060b822807a88f729b5c1944104417a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs

  • https://github.com/dotnet/aspnetcore/blob/main/src/DefaultBuilder/src/WebApplicationBuilder.cs

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/net7Sample/Net7Sample/HostApplicationBuilderSample.cs

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值