Asp.net core 在ASP.NET Core 3.x MVC中,将nameof()与Url.Action()和异步方法一起使用

Asp.net core 在ASP.NET Core 3.x MVC中,将nameof()与Url.Action()和异步方法一起使用,asp.net-core,asp.net-core-mvc,url-routing,asp.net-core-3.0,nameof,Asp.net Core,Asp.net Core Mvc,Url Routing,Asp.net Core 3.0,Nameof,假设我有一个ASP.NET Core 3.0 MVC应用程序,它的特点是一个简单的控制器,包含两个操作,并使用基于属性的路由: [路线(“家”)] 公共类HomeController:控制器 { 公共静态字符串控制器名{get;}=“Home”; 公共家庭控制器() { } 字符串生成器() { 字符串url1=Url.Action(nameof(Action1),ControllerName); 字符串url2=Url.Action(nameof(Action2Async),Controlle

假设我有一个ASP.NET Core 3.0 MVC应用程序,它的特点是一个简单的控制器,包含两个操作,并使用基于属性的路由:

[路线(“家”)]
公共类HomeController:控制器
{
公共静态字符串控制器名{get;}=“Home”;
公共家庭控制器()
{
}
字符串生成器()
{
字符串url1=Url.Action(nameof(Action1),ControllerName);
字符串url2=Url.Action(nameof(Action2Async),ControllerName);
返回$“Action1:'{url1}'\nAction2:'{url2}';
}
[HttpGet(“a1”)]
公共IActionResult操作1()
{
返回Ok(GenerateUrls());
}
[HttpGet(“a2”)]
公共异步任务Action2Async()
{
等待任务。完成任务;
返回Ok(GenerateUrls());
}
}
因此,调用任何一个操作都应该生成一个页面,显示两个操作的URL

打开
/home/a1
/home/a2
可以正确调用相应的操作,但输出有点意外:

Action1: '/home/a1'
Action2: ''
这表明
Url.Action()
为第二个操作返回了一个空字符串,而它对第一个操作的效果非常好

在调试了很长一段时间后,我发现了一个问题,这就是ASP.NET Core 3.0中的一个突破性变化,其中
Async
后缀不知何故被
Url.Action()
忽略了

作者通过将字符串硬编码为操作名称解决了这个问题(
“Action1”
und
“Action2”
)。他还上传了一些复制这种行为的视频

但是,我更愿意保留的
名称,以避免以后重命名/重构时出现问题


是否有一种干净的方法可以使用
nameof
或其他类型安全构造为
Url.Action
函数提供带有
Async
后缀的方法?

如果将方法名称Action2Async更改为Action2,问题将得到解决

从链接:

在ASP.NET Core 3中,如果您的操作方法后缀为Async,但路由路径不包含Async,则在通过类似URL.Action()解析URL时,引用这些方法而不使用Async后缀。这似乎是ASP.NET Core 2.2的一个突破性变化,该版本尚未正式记录

publicstaticstringcontrollername{get;}=“Home”;
字符串生成器()
{
字符串url1=Url.Action(nameof(Action1),ControllerName);
字符串url2=Url.Action(nameof(Action2),ControllerName);
返回$“Action1:'{url1}'\nAction2:'{url2}';
}
[HttpGet(“操作1”)]
公共IActionResult操作1()
{
返回Ok(GenerateUrls());
}
[HttpGet(“action2”)]
公共异步任务Action2()
{
等待任务。完成任务;
返回Ok(GenerateUrls());
}

所描述的行为是由ASP.NET Core 3.0引入的破坏性更改引起的

您可以通过禁用以下功能返回到旧行为:

获取或设置一个值,该值确定MVC是否删除应用于控制器操作名称的后缀“Async”

配置应用程序服务时,在
AddControllers
调用中禁用此开关:

services.AddControllers(options => {
    options.SuppressAsyncSuffixInActionNames = false;
});

您可以在和中找到有关此更改的更多信息。

虽然这可以解决所描述的问题,但它并不能真正回答我的问题。
Async
后缀约定有很好的理由——在我的例子中,删除它并不是一个真正的选项。我正在寻找一种类型安全的方法来使用
Url.Action
,即使这些方法具有
Async
后缀。@janw您找到解决方案了吗?没有,这只会使一个非属性化的Async后缀Action方法无法通过非后缀路由访问。虽然这似乎是相关的,但与手头的问题无关。@CodeCaster据我所知,这正是他想要的。正如在他的问题中所看到的,他的所有操作方法都使用了所需的路由名称,因此异步后缀不应该引起任何问题。那么问题是您不理解它。他们希望动作方法中包含强类型字符串,是吗?但是如果您使用
nameof(Action2Async)
,您的URL中就会出现
“Action2Async”
。这与任何路由都不匹配,因为它们的操作方法是通过“action2”路由的(通过属性
[HttpGet(“action2”)]
,但也可以通过命名约定)。请求“action2Async”不会以该操作方法结束。虽然您的更改需要这样做,但当他们省略
[HttpGet(“action2”)]
属性时,他们会使用URL“action2Async”!因此,您的建议归结为:禁用“从路由中删除异步后缀”约定,删除路由属性,并接受您的路由将是
Action2Async
,这会适得其反,并导致丑陋的URL。谢谢!这将导致生成所需的URL。它仍然感觉有点“凌乱”,使框架回归到旧的行为。我将简要介绍其他选项,但这肯定是一个有效的解决方案。您可以查看(或R4MVC为核心)生成强类型帮助程序(参数)代码。这与您的问题无关,但请避免使用类似
wait Task.CompletedTask在您的代码中。改为使用
返回任务。FromResult()
。@CodeCaster谢谢你的指针,看起来很有希望!这也可能解决控制器名称属性的问题…@MarcusWichelmann好吧,这更多是尽可能简化示例的结果,但仍然感谢您的提示:)
services.AddControllers(options => {
    options.SuppressAsyncSuffixInActionNames = false;
});