.NET 6候选发布版2(RC2)现在已经推出。.NET 6 RC2非常接近于.NET 6的最终版本,我们预计将在今年11月为2021年的.NET会议及时发货。这也是一个 "上线 "版本,所以欢迎你在生产中使用它。.NET 6 RC2主要包含质量改进和错误修复,尽管它也包括一些新功能。
以下是这个预览版中的新功能:
- 对Blazor WebAssembly应用程序的本地依赖性支持
- 极少的API更新
开始使用
要开始使用.NET 6 RC2中的ASP.NET Core,请安装.NET 6 SDK。
如果你在Windows上使用Visual Studio,请安装Visual Studio 2022的最新预览版,其中包括.NET 6 RC2。Mac用户应安装Visual Studio for Mac 2022的最新预览版。
升级一个现有的项目
要将现有的ASP.NET Core应用程序从.NET 6 Preview RC1升级到.NET 6 RC2:
- 将所有Microsoft.AspNetCore.*包的引用更新为
6.0.0-rc.2.*。 - 将所有Microsoft.Extensions.*包的引用更新到
6.0.0-rc.2.*。
请参阅ASP.NET Core for .NET 6中的全部中断变化列表。
对Blazor WebAssembly应用程序的本地依赖性支持
.NET 6中的Blazor WebAssembly应用程序现在可以使用在WebAssembly上运行的本地依赖项。你可以使用.NET WebAssembly构建工具将本地依赖关系静态地链接到.NET WebAssembly运行时,这些工具与你在.NET 6中用来提前(AOT)编译Blazor应用程序到WebAssembly或重新链接运行时以删除未使用的功能的工具相同。
要安装.NET WebAssembly构建工具,请在Visual Studio安装程序中选择可选的组件,或在高位命令提示符下运行dotnet workload install wasm-tools 。.NET WebAssembly构建工具是基于Emscripten,这是一个用于网络平台的编译器工具链。
你可以通过在项目文件中添加NativeFileReference 项目来为你的Blazor WebAssembly应用程序添加本地依赖项。当你构建项目时,每个NativeFileReference ,由.NET WebAssembly构建工具传递给Emscripten,以便它们被编译和链接到运行时。然后你可以从你的.NET代码中p/invoke到本地代码。
一般来说,任何可移植的本地代码都可以作为Blazor WebAssembly的本地依赖项。你可以在C/C++代码或以前用Emscripten编译的代码中添加本地依赖关系:对象文件(.o)、存档文件(.a)、比特码(.bc)或独立的WebAssembly模块(.wasm)。预构建的依赖通常需要使用与构建.NET WebAssembly运行时相同的Emscripten版本(目前是2.0.23)来构建。
使用来自Blazor WebAssembly应用程序的本地代码
让我们在Blazor WebAssembly应用程序中添加一个简单的本地C函数:
- 创建一个新的Blazor WebAssembly项目。
- 在该项目中添加一个Test.c文件。
- 在Test.c中添加一个C函数,用于计算阶乘。
int fact(int n)
{
if (n == 0) return 1;
return n * fact(n - 1);
}
- 在你的项目文件中为Test.c添加一个
NativeFileReference:
<ItemGroup>
<NativeFileReference Include="Test.c" />
</ItemGroup>
- 在Pages/Index.razor中,为生成的Test库中的
fact函数添加一个DllImport。
@using System.Runtime.InteropServices
...
@code {
[DllImport("Test")]
static extern int fact(int n);
}
- 从你的.NET代码中调用
fact方法:
<p>@fact(3)</p>
当你用安装的.NET WebAssembly构建工具构建应用程序时,本地C代码会被编译并链接到dotnet.wasm中。这可能需要几分钟的时间。一旦应用程序完成构建,运行它以查看渲染的阶乘值。
注意:在随后的构建中,你可能会得到一个构建错误,说输出程序集正在被另一个进程使用。这是一个已知的问题,将在.NET 6版本中解决。要解决这个问题,请再次重建该项目。
使用具有本地依赖性的库
NuGet包可以包含在WebAssembly上使用的本地依赖性。这些库和它们的本地功能可以在任何Blazor WebAssembly应用程序中使用。本机依赖的文件应该为WebAssembly构建,并打包在browser-wasm 架构专用文件夹中。WebAssembly特定的依赖文件不会被自动引用,需要手动引用NativeFileReference 。软件包作者可以选择在软件包中加入一个.props文件来添加本地引用。
SkiaSharp是一个基于本地Skia图形库的.NET跨平台2D图形库,现在它对Blazor WebAssembly有预览支持。让我们试一试吧
要在Blazor WebAssembly应用程序中使用SkiaSharp。
-
从你的Blazor WebAssembly项目中添加一个对SkiaSharp.Views.Blazor包的引用。
dotnet add package -prerelease SkiaSharp.Views.Blazor
-
在你的应用程序中添加一个
SKCanvasView组件:
<SKCanvasView OnPaintSurface="OnPaintSurface" />
- 添加一些绘图逻辑:
@code {
void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);
using var paint = new SKPaint
{
Color = SKColors.Black,
IsAntialias = true,
TextSize = 24
};
canvas.DrawText("SkiaSharp", 0, 24, paint);
}
}
- 运行应用程序,查看你用SkiaSharp定制的绘图

你可以在SkiaSharp repo中找到一个使用SkiaSharp与Blazor WebAssembly的完整示例。
最小的API更新
参数绑定
在RC2中,我们在TryParse 和BindAsync 中增加了对继承方法的支持。我们还检查了公共TryParse 和BindAsync 方法的正确格式,并抛出一个错误,这样你就知道你的方法使用了错误的语法。此外,BindAsync 方法现在有另一个支持的重载,不需要ParameterInfo:public static ValueTask<T?> BindAsync(HttpContext context) 。
如果你想从路由、标题属性和查询字符串中绑定值,你可以在你的自定义类型上添加一个静态的TryParse 方法,如下所示。TryParse 方法必须是以下形式:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
下面是一个复杂类型的TryParse 的例子:
app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public static bool TryParse(string? value, IFormatProvider? provider, out Point? point)
{
// Format is "(12.3,10.1)"
var trimmedValue = value?.TrimStart('(').TrimEnd(')');
var segments = trimmedValue?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (segments?.Length == 2
&& double.TryParse(segments[0], out var x)
&& double.TryParse(segments[1], out var y))
{
point = new Point { X = x, Y = y };
return true;
}
point = null;
return false;
}
}
对上述端点的请求/map?point=(10.1, 11.4) ,将返回一个具有以下属性值的Point 对象X=10.1, Y=11.4 。
此外,如果你想控制你的类型的绑定过程,你可以使用以下形式的BindAsync:
public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo? parameter);
public static ValueTask<T?> BindAsync(HttpContext context);
想使用继承来绑定一个复杂的类型吗?BindAsync ,你可以做到这一点,正如下面的例子所演示的:
app.MapPost("/widget", (CreateWidgetDTO dto) =>
{
// Use the DTO
});
public abstract class DTO<T>
{
// typeof(T) must equal ParameterInfo.Type otherwise we throw
public static T BindAsync(HttpContext context, ParameterInfo parameter)
{
// Use reflection to bind the properties on T
}
}
public class CreateWidgetDTO : DTO<CreateWidgetDTO>
{
[FromRoute]
public string? Name { get; set; }
[FromQuery]
public int Id { get; set; }
}
我们还通过BindAsync 增加了对可选的自定义参数类型的支持,包括如下所示的可忽略的结构:
app.MapGet("/webhook", (CloudEventStruct? cloudEvent) =>
{
redis.Publish(cloudEvent);
});
public struct CloudEventStruct
{
public static async ValueTask<CloudEventStruct?> BindAsync(HttpContext context, ParameterInfo parameter)
{
return await CloudEventParser.ReadEventAsync(context, parameter.Name);
}
}
OpenAPI
我们增加了一些增强功能,允许你使用Accepts<TRequest>() 扩展方法或[Consumes(typeof(Todo), "application/json", IsRequired = false)] 属性来描述请求主体是否需要。Accepts 扩展方法和Consumes 属性允许你为你生成的open-api文档同时表达类型Todo 和内容类型application/json ,如下面的例子所示:
app.MapPost("/todo", async (HttpRequest request) =>
{
var todo = await request.Body.ReadAsJsonAsync<Todo>();
return todo is Todo ? Results.Ok(todo) : Results.Ok();
})
.Accepts<Todo>(isRequired: false, contentType: "application/json");
app.MapPost("/todo", HandlePostTodo);
[Consumes(typeof(Todo), "application/json", IsRequired = false)]
IResult HandlePostTodo(HttpRequest request)
{
var todo = await request.Body.ReadAsJsonAsync<Todo>();
return todo is Todo ? Results.Ok(todo) : Results.Ok();
}
如果你想在OpenAPI文档(Swagger)中把相关的端点分成不同的集合,你可以使用WithTags 扩展方法来实现,该方法允许你提供分组标签的元数据。见下面的使用例子:
app.MapGet("/", () => "Hello World!")
.WithTags("Examples");
app.MapGet("/todos/sample", () => new[]
{
new Todo { Id = 1, Title = "Do this" },
new Todo { Id = 2, Title = "Do this too" }
})
.WithTags("Examples", "TodoApi");
源代码分析
在RC2中,我们增加了一些分析器,以帮助你快速发现路由处理程序的问题,或在中间件存在错误配置问题时向你发出警告。分析器将为WebApplicationBuilder ,并在检测到不正确的中间件配置或顺序时警告你。
我们还增加了对检测从路由处理程序返回的实现IActionResult 的类型的支持,并对无意中将结果序列化为JSON的情况发出警告。见下面的例子:
app.Map("/", () => new JsonResult(new { Hello = "World" }));
此外,我们引入了一个新的分析器,它将检测到属性被放在一个由lambda调用的方法上,而不是lambda本身。例如,下面的代码将产生一个警告:
app.Map("/payment", () => SubmitPayment());
[Authorize]
void SubmitPayment() { }
最后但并非最不重要的是,对于可选的参数绑定,我们增加了一个分析器,当参数的可选性不匹配时,将抛出一个异常。请注意,下面的路由字符串将uuid 参数定义为可选,但在lambda函数(string uuid) => $"{uuid}" 中却定义为必需。
app.MapGet("/todo/{uuid?}", (string uuid) => $"{uuid}");
突破性变化(API重命名)
我们重新命名了以下API,以提供清晰度并正确描述其意图:
DelegateEndpointConventionBuilder->RouteHandlerBuilderOpenApiDelegateEndpointConventionBuilderExtensions->OpenApiRouteHandlerBuilderExtensionsDelegateEndpointRouteBuilderExtensions与现有的 合并。EndpointRouteBuilderExtensions
上述改动用RouteHandler 替换了DelegateEndpoint ,并删除了类名中的Convention 。
给予反馈
我们希望你喜欢这个.NET 6中的ASP.NET Core预览版。我们很想听听你对这个版本的体验。通过在GitHub上提交问题,让我们知道你的想法。