无法找到外部程序集中的 ViewComponent [英] ViewComponent in external assembly cannot be found

查看:14
本文介绍了无法找到外部程序集中的 ViewComponent的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using the latest VS.2017 updates and templates for an MVC .NET Core web application. I decided I wanted ViewComponents in an external assembly since I read several posts that indicated it was not possible without odd tricks.

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

I have my main web application and then I created a .NET Framework class library named MySite.Components which is the "external assembly". In it I installed the ViewFeatures NuGet. I created my View component CSHTML in its /Views/Shared/Components/GoogleAdsense/Default.cshtml.

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

I noticed that my CSPROJ already has the GoogleAdSense as an embedded resource:

 <ItemGroup>
  <None Include="app.config" />
  <None Include="packages.config" />
  <EmbeddedResource Include="ViewsSharedComponentsGoogleAdsenseDefault.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:

Then in the main project (the ASP.NET Core web application) I installed the File Provider NuGet and modified my Startup:

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

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

Then I try to use the view component in a view like this:

@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 的通用参数,但也失败了,但使用

Also tried using the notation without nameof() that uses a generic parameter for InvokeAsync but that fails too but with

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

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

And using the TagHelper form simply renders it as an unrecognized HTML:

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

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

Finally, on the Main Assembly (the actual web application) I used the GetManifestResourceNames() on the external assembly type to verify it was embedded and the returned list had it listed as:

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

推荐答案

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

I did a lot of trial-and-error and was finally able to get this working. There's a number of guides on this, but they're all for .NET Core 1.0, and I also found they did not work when using a DLL reference from another solution.

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

Let's talk about component name first. The component name is determined either by convention or attribute. To name by convention, the class name must end in "ViewComponent", and then the component name will be everything prior to "ViewComponent" (just like Controller names work). If you just decorate the class with [ViewComponent], the component name will explicitly be the class name. You can also directly set the name to something else with the attribute's Name parameter.

所有这三个示例都会生成一个名为GoogleAdsense"的组件.

All three of these examples produce a component name of "GoogleAdsense".

public class GoogleAdsenseViewComponent : ViewComponent { }

[ViewComponent]
public class GoogleAdsense : ViewComponent { }

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

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

After that, be sure your views are in the proper folder structure.

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

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

Then, the Views must all be included as embedded resources. Right-click > Properties on the view and set the Build Action to "Embedded resource". You can also do this manually in the .csproj (and take advantage of globbing if you have a lot of Views).

  <ItemGroup>
    <EmbeddedResource Include="ViewsSharedComponentsGoogleAdsenseDefault.cshtml" />
  </ItemGroup>

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

That's it for the source project. Make note that you must do a build for any changes to your views to show up, since they are being included in the DLL. This seems obvious, but it's a change from how you normally interact with views.

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

Now to the consuming project. In ConfigureServices in Startup.cs, you must add your component's assembly as both an MVC ApplicationPart and as an EmbeddedFileProvider. The EmbeddedFileProvider gives access to the views embedded in the assembly, and the ApplicationPart sets up MVC to include it in its search paths.

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 提供基本命名空间.我已经找到了需要的时候和不需要的时候,所以最好只是提供它.这个命名空间应该是你项目的默认命名空间属性(属性 -> 应用程序 -> 默认命名空间).

If you have multiple ViewComponents in that assembly, this will suffice for all of them. You can optionally provide a base namespace to EmbeddedFileProvider. I have found times when it was necessary and times when it was not, so it is best to just provide it. This namespace should be the Default Namespace property of your project (Properties -> Application -> Default Namespace).

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

Finally, to invoke the ViewComponent, use the component name. Remember that the component name may differ from the class name. Unless you used [ViewComponent] to set the component name to be the class name, you cannot use nameof.

@await Component.InvokeAsync("GoogleAdsense")

这篇关于无法找到外部程序集中的 ViewComponent的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆