Blog Entry
试用.Net8
试用.Net8
- Created
- 2023/08/24
- Updated
- 2023/08/24
环境
- dotnet
❯ dotnet --list-sdks7.0.400 [C:\Program Files\dotnet\sdk]8.0.100-preview.7.23376.3 [C:\Program Files\dotnet\sdk]- Microsoft Visual Studio Professional 2022 (64-bit) - Current Version 17.7.2
准备
目前.net8还是preview状态,需要在VS中开启,否则无法选用.net8版本的SDK。

Sample
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable>
<!-- AOT --> <PublishAot>true</PublishAot> <IsAotCompatible>true</IsAotCompatible> <InvariantGlobalization>true</InvariantGlobalization>
<!-- NuGet --> <PackAsTool>true</PackAsTool> <ToolCommandName>clicky</ToolCommandName> <RootNamespace>Kx.Toolx.Clicky</RootNamespace> </PropertyGroup>
<ItemGroup> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.7.23375.6" /> <PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.2.0-dev-00918" /> <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.18-beta"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup>
<ItemGroup> <None Update="appsettings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>
</Project>-
目前AOT还存在不少限制,具体见Native AOT deployment overview # Limitations
-
目前选项模式与AOT的兼容不是很好,发布时勾选裁剪也要注意,可能会读取配置为空。
选项模式的简易替代方案
直接使用选项模式会报警告:

原因是Configure方法已经标了RequiresDynamicCode特性:
[RequiresDynamicCode(OptionsBuilderConfigurationExtensions.RequiresDynamicCodeMessage)] [RequiresUnreferencedCode(OptionsBuilderConfigurationExtensions.TrimmingRequiredUnreferencedCodeMessage)] public static IServiceCollection Configure<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(this IServiceCollection services, IConfiguration config) where TOptions : class => services.Configure<TOptions>(Options.Options.DefaultName, config);跟进代码后发现核心代码如下:
[RequiresDynamicCode(OptionsBuilderConfigurationExtensions.RequiresDynamicCodeMessage)] [RequiresUnreferencedCode(OptionsBuilderConfigurationExtensions.TrimmingRequiredUnreferencedCodeMessage)] public static IServiceCollection Configure<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(this IServiceCollection services, string? name, IConfiguration config, Action<BinderOptions>? configureBinder) where TOptions : class { ThrowHelper.ThrowIfNull(services); ThrowHelper.ThrowIfNull(config);
services.AddOptions(); services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config)); return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config, configureBinder)); }真正受影响的是NamedConfigureFromConfigurationOptions这个类:
[RequiresDynamicCode(OptionsBuilderConfigurationExtensions.RequiresDynamicCodeMessage)] [RequiresUnreferencedCode(OptionsBuilderConfigurationExtensions.TrimmingRequiredUnreferencedCodeMessage)] public NamedConfigureFromConfigurationOptions(string? name, IConfiguration config, Action<BinderOptions>? configureBinder) : base(name, options => config.Bind(options, configureBinder)) { ThrowHelper.ThrowIfNull(config); }其核心作用是将config中的配置信息填充给被注入的options对象。
我们做如下替换:
- 初始化
await Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { // 服务注册 services.AddHostedService<HostedService>();
// Options
// 原配置 // services.Configure<ClickyConfig>(hostContext.Configuration.GetSection(ClickyConfig.KEY));
// 替换后 services.AddOptions(); var config = hostContext.Configuration.GetSection(ClickyConfig.KEY); services.AddSingleton<IOptionsChangeTokenSource<ClickyConfig>>(new ConfigurationChangeTokenSource<ClickyConfig>(string.Empty, config)); // 使用 ClickyConfigureNamedOptions 替换 NamedConfigureFromConfigurationOptions services.AddSingleton<IConfigureOptions<ClickyConfig>>(new ClickyConfigureNamedOptions(string.Empty, config)); }) .UseConsoleLifetime() .Build() .RunAsync() ;ClickyConfigureNamedOptions
using System.Drawing;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.Options;
namespace Kx.Toolx.Clicky;
internal class ClickyConfigureNamedOptions : ConfigureNamedOptions<ClickyConfig>{ public ClickyConfigureNamedOptions(string? name, IConfiguration configuration) : base(name, cc => Bind(cc, configuration)) { }
private static void Bind(ClickyConfig config, IConfiguration configuration) { if (config == null) { return; }
if (configuration == null) { return; }
// 填充config的具体字段 config.Hwnd = configuration[nameof(config.Hwnd)] ?? string.Empty; config.TimerPeriod = double.TryParse(configuration[nameof(config.TimerPeriod)], out var tp) ? tp : 5; config.Points = configuration.GetSection(nameof(config.Points)) .GetChildren() .Select(p => new Point((int.TryParse(p["X"], out var x) ? x : 0), (int.TryParse(p["Y"], out var y) ? y : 0))) .ToList() ; }}相当于手动解析了一下配置,目前项目配置不复杂还可以接受,复杂的话就够呛。
不过我相信官方不久就应该要解决这个问题了,毕竟选型模式挺好用且挺好用的。躺着等就好。