找不到外部部件中的ViewComponent [英] ViewComponent in external assembly cannot be found

查看:160
本文介绍了找不到外部部件中的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中创建了View组件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="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
 </ItemGroup>

视图组件实际上非常简单:

The view component is actually quite simple:

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应用程序)中,我安装了文件提供程序NuGet并修改了启动程序:

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的,我还发现当使用来自另一个 solution 的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

然后,视图必须全部作为嵌入式资源包括在内.右键单击>视图上的Properties,然后将Build Action设置为"Embedded resource".您也可以在.csproj中手动执行此操作(如果您有很多View,则可以使用遍历功能).

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="Views\Shared\Components\GoogleAdsense\Default.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天全站免登陆