【问题标题】:Call Component.InvokeAsync() from within an IViewComponentHelper extension method从 IViewComponentHelper 扩展方法中调用 Component.InvokeAsync()
【发布时间】:2020-06-29 21:57:51
【问题描述】:

我有一个解决方案,它同时具有 ASP.NET Core 3.1 Web 应用程序项目和 Razor 客户端库 (RCL) 项目。我正在尝试编写一个视图组件,该组件将与 Razor 客户端库一起分发,但可以从 ASP.NET Core Web 应用程序中引用。

当我在 Web 应用程序的 _Layout.cshtml 上调用 InvokeAsync() Tag Helper 时,我可以成功呈现此 ViewComponent:

@await Component.InvokeAsync("NavBar", new {})

但我想让 RCL 不依赖于 Web 应用程序上提供的字符串名称。相反,我希望喜欢通过这样的扩展方法调用ViewComponent:

@{ await Component.RenderMyViewComponentAsync(); }

对我来说,这种方式对使用这个共享 RCL 库的人更有利,因为他们不需要指定 ViewComponent 的确切名称,并且可以依靠 IntelliSense 来完成扩展方法。

我创建了一个辅助类,它接受ViewComponent 对象并在扩展方法中简单地调用它的InvokeAsync() 方法:

public static class PartialHelper
{
    public static async Task<IHtmlContent> RenderMyViewComponentAsync(this IViewComponentHelper vcHelper)
    {
        return await vcHelper.InvokeAsync("NavBar", new { });
    }
}

当然,在NavBarViewComponent.cs 内部,我已经实现了InvokeAsync() 方法:

public class NavBarViewComponent : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync()
    {
        return View();
    }
}

但问题是:我没有看到后一种方法在屏幕上呈现我的视图,尽管这两种方法仍然会击中我的NavBarViewComponent.InvokeAsync()。

据我所知,返回ViewComponents 的两种方式在功能上是等效的并且使用相同的方法,除了@{ await Component.RenderMyViewComponentAsync(); } 首先通过一个辅助函数。

我尝试了两种方式调试,但无济于事。我被卡住了!

有什么方法可以实现我在这里的要求吗?如果需要任何澄清,请询问。

【问题讨论】:

    标签: c# razor extension-methods asp.net-core-3.1 asp.net-core-viewcomponent


    【解决方案1】:

    您的扩展方法实际上完全按照应有的方式工作。问题在于您如何从_Layout.cshtml 调用它。您正在使用以下语法:

    @{ await Component.RenderMyViewComponentAsync(); }
    

    这会将其视为在脚本块中,而不是将IHtmlContent 呈现给视图。 IHtmlContent 被正确返回,但它没有分配给任何东西或写入输出。

    相反,您只需使用调用 InvokeAsync() 时使用的相同样式语法:

    @await Component.RenderMyViewComponentAsync()
    

    然后这将完全按照您的预期呈现。

    【讨论】:

      【解决方案2】:

      扩展方法有同样的问题,所以谢谢你的回答。

      一个补充——不是使用字符串,而是使用泛型在编译时捕获错误:

      await Component.InvokeAsync<NavBar>();
      

      或直接从控制器:

      public IActionResult Index()
      {
          return ViewComponent(typeof(NavBar));
      }
      

      【讨论】:

      • 嗯,我完全错过了InvokeAsync() 上的泛型类型选项。这很有用。我仍然更喜欢使用标签助手语法 (per my other answer),但这对于我需要使用 InvokeAsync() 的情况要好得多。谢谢。
      【解决方案3】:

      除了回答你的具体问题,我还想回答后面你的问题。您的总体目标似乎是简化语法,而不依赖于不提供任何设计或编译时验证的字符串。

      我真的很喜欢您使用扩展方法作为解决方案的方法,并且可能会自己借用它。但是解决类似目标的另一种方法是invoke your ViewComponent as a Tag Helper:

      <vc:NavBar />
      

      这为您在 Visual Studio 中提供了基本的 IntelliSense 支持,并且比 InvokeAsync() 方法更简洁。

      也就是说,值得注意的是 Tag Helper 方法确实有几个关键限制,as I've discussed in a previous answer:

      1. 不支持排除您在InvokeAsync() 方法中定义的任何可选参数*。
      2. 如果您缺少任何参数,Visual Studio 和编译器都不会提供(明显的)警告。

      鉴于此,如果您的视图组件具有任何参数,尤其是如果这些参数中的任何一个具有逻辑默认值,您使用扩展方法的方法可能仍然更可取。

      *注意:从 ASP.NET Core 6 Preview 6 开始,作为标记帮助程序调用的视图组件现在将支持可选参数 (source)。

      【讨论】:

      • 更新: 从 ASP.NET Core 6 预览版 6 开始,作为标记帮助程序调用的视图组件现在将支持可选参数 (source)。这使得它们非常适合这种类型的场景。
      猜你喜欢
      • 1970-01-01
      • 2016-11-25
      • 2011-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-30
      • 2014-02-23
      相关资源
      最近更新 更多