Skip to main content

试用.Net8

· 4分钟阅读

环境

  • dotnet
❯ dotnet --list-sdks
7.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

Kx.Toolx.Clicky.csproj
<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>

选项模式的简易替代方案

直接使用选项模式会报警告:

原因是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对象。

我们做如下替换:

  1. 初始化

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()
;
  1. 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()
;
}
}

相当于手动解析了一下配置,目前项目配置不复杂还可以接受,复杂的话就够呛。

不过我相信官方不久就应该要解决这个问题了,毕竟选型模式挺好用且挺好用的。躺着等就好。

参考