【问题标题】:ViewComponent in external assembly cannot be found无法找到外部程序集中的 ViewComponent
【发布时间】:2017-05-10 20:34:06
【问题描述】:

我正在为 MVC .NET Core Web 应用程序使用最新的 VS.2017 更新和模板。我决定要在外部程序集中使用 ViewComponents,因为我阅读了几篇文章,表明如果没有奇怪的技巧,这是不可能的。

我有我的主 Web 应用程序,然后我创建了一个名为 MySite.Components 的 .NET Framework 类库,它是“外部程序集”。我在其中安装了 ViewFeatures NuGet。我在 /Views/Shared/Components/GoogleAdsense/Default.cshtml 中创建了我的 View 组件 CSHTML。

我注意到我的 CSPROJ 已经将 GoogleAdSense 作为嵌入式资源:

 <ItemGroup>
  <None Include="app.config" />
  <None Include="packages.config" />
  <EmbeddedResource Include="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
 </ItemGroup>

视图组件其实很简单:

namespace MySite.Components.ViewComponents {
     [ViewComponent(Name = "GoogleAdsense")]
     public class GoogleAdsense : ViewComponent {
        public async Task<IViewComponentResult> InvokeAsync(string adSlot, string clientId, string adStyle = "")
        {
          var model = await GetConfigAsync(adSlot, clientId, adStyle); 
          return View(model); 
        }

        private Task<GoogleAdUnitCompModel> GetConfigAsync(string adSlot, string clientId, string adStyle)
        {
             GoogleAdUnitCompModel model = new GoogleAdUnitCompModel
           {
            ClientId = clientId,    // apparently we can't access App_Data because there is no AppDomain in .NET core
            SlotNr = adSlot,
            Style = adStyle
           };
           return Task.FromResult(model); 
        }
     }
}

然后在主项目(ASP.NET Core Web 应用程序)中,我安装了 File Provider NuGet 并修改了我的 Startup:

services.Configure<RazorViewEngineOptions>(options =>
        {
            options.FileProviders.Add(new EmbeddedFileProvider(
                 typeof(MySite.Components.ViewComponents.GoogleAdsense).GetTypeInfo().Assembly,
                 "MySite.Components.ViewComponents"
            ));
        });

然后我尝试在这样的视图中使用视图组件:

@using MySite.Components.ViewComponents
            :
@Component.InvokeAsync(nameof(GoogleAdsense), new { adSlot = "2700000000", clientId = "ca-pub-0000000000000000", adStyle="" }) 

我得到一个错误提示

*InvalidOperationException: A view component named 'GoogleAdsense' could not be found.*

还尝试使用不带 nameof() 的表示法,它使用 InvokeAsync 的通用参数,但也失败了,但使用

 *"Argument 1: cannot convert from 'method group' to 'object'"*

而使用 TagHelper 表单只会将其呈现为无法识别的 HTML:

<vc:GoogleAdsense adSlot = "2700000000" clientId = "ca-pub-0000000000000000"></vc:GoogleAdsense>

最后,在主程序集(实际的 Web 应用程序)上,我在外部程序集类型上使用了 GetManifestResourceNames() 来验证它是否已嵌入,并且返回的列表将其列为:

[0] = "MySite.Components.Views.Shared.Components.GoogleAdsense.Default.cshtml"

【问题讨论】:

    标签: asp.net-core asp.net-core-mvc asp.net-core-viewcomponent


    【解决方案1】:

    我做了很多试验和错误,终于能够让它工作。有很多关于这方面的指南,但它们都是针对 .NET Core 1.0 的,而且我还发现在使用来自另一个解决方案的 DLL 引用时它们不起作用。

    我们先说一下组件名称。组件名称由约定或属性确定。按照约定命名,类名必须以“ViewComponent”结尾,然后组件名将是“ViewComponent”之前的所有内容(就像控制器名称一样)。如果你只是用[ViewComponent] 装饰类,组件名将明确地是类名。您也可以使用属性的 Name 参数直接将名称设置为其他名称。

    所有这三个示例都会生成一个名为“GoogleAdsense”的组件。

    public class GoogleAdsenseViewComponent : ViewComponent { }
    
    [ViewComponent]
    public class GoogleAdsense : ViewComponent { }
    
    [ViewComponent(Name = "GoogleAdsense")]
    public class Foo: ViewComponent { }
    

    之后,请确保您的视图位于正确的文件夹结构中。

    ├── Views
    │   ├── Shared
    │   │   ├── Components
    │   │   │   ├── GoogleAdsense <--component name
    │   │   │   │   ├── Default.cshtml
    

    然后,视图必须全部作为嵌入资源包含在内。在视图上右键单击 > 属性并将构建操作设置为“嵌入式资源”。您也可以在 .csproj 中手动执行此操作(如果您有很多视图,则可以利用通配符)。

      <ItemGroup>
        <EmbeddedResource Include="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
      </ItemGroup>
    

    源项目就是这样。请注意,您必须进行构建才能显示对视图的任何更改,因为它们已包含在 DLL 中。这似乎很明显,但它与您通常与视图交互的方式有所不同。

    现在到消费项目。在 Startup.cs 的 ConfigureServices 中,您必须将组件的程序集添加为 MVC ApplicationPartEmbeddedFileProviderEmbeddedFileProvider 允许访问嵌入在程序集中的视图,ApplicationPart 设置 MVC 以将其包含在其搜索路径中。

    var myAssembly = typeof(My.External.Project.GoogleAdsenseViewComponent).Assembly;
    
    services.AddMvc().AddApplicationPart(myAssembly);
    
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.FileProviders.Add(new EmbeddedFileProvider(myAssembly, "My.External.Project"));
    });
    

    如果您在该程序集中有多个 ViewComponent,那么这对所有这些都足够了。您可以选择为EmbeddedFileProvider 提供基本命名空间。我已经找到了需要的时候和不需要的时候,所以最好只提供它。这个命名空间应该是您项目的默认命名空间属性(属性 -> 应用程序 -> 默认命名空间)。

    最后,要调用 ViewComponent,请使用组件名称。请记住,组件名称可能与类名称不同。除非你使用[ViewComponent]将组件名设置为类名,否则不能使用nameof

    @await Component.InvokeAsync("GoogleAdsense")
    

    【讨论】:

    • “右键单击视图上的 > 属性并将构建操作设置为“嵌入式资源” - 这是我让我的工作缺少的一件事,谢谢。
    • 这比任何其他答案都让我更进一步,但缺少的是将视图设置为Embedded Resource。此外,.AddApplicationPart() 不是必需的,因为我的工作没有它。
    【解决方案2】:

    通过从“外部”程序集生成 NuGet 包并将其安装到使用解决方案中,我能够从外部解决方案中正常工作的 ViewComponent。我最初尝试在不创建自己的 NuGet 包的情况下添加对 dll 的引用,但它不起作用。

    我建议先尝试 NuGet 包。如果还是不行,能不能把这两个项目都贴出来让我帮忙调试一下?

    【讨论】:

    • 提供有关生成和安装 NuGet 包的说明是否有助于此答案?
    • 我设法让它双向工作。但是对于您的要求,只需确保您的 ViewComponent 所在的外部程序集是一个 ASP.NET Core 项目。单击项目属性,然后在 Package 选项卡上,检查 Build NuGet Package 选项,这是第一个。成功构建后,.nupkg 将位于您的 bin\Release 或 bin\Debug 文件夹中。复制到您自己的存储库并从那里安装。我添加了一个构建任务来自动将构建的 NuGet 包部署到我自己的 NuGet(本地)存储库中。
    • 这也是我做 NuGet 包的方式。如果你能够让它作为 dll 引用工作,你做了什么不同的事情?当我将它作为 dll 包含在包中时,我不断收到无法找到视图组件的错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 2019-09-16
    • 1970-01-01
    • 2014-08-07
    相关资源
    最近更新 更多