web-dev-qa-db-ja.com

ASP.NET MVC 6:別のアセンブリでコンポーネントを表示する

ビューコンポーネント(ASP.NET MVC 6の新機能)をMVC 6 Webスタートアッププロジェクトとは別のアセンブリで定義して、複数のWebプロジェクトで再利用できるようにしたいと思います。サンプルソリューションは次のようになります。

  • BookStore.Components(共通のビューコンポーネントを収容)
  • BookStore.Web1(BookStore.Componentsを参照)
  • BookStore.Web2(BookStore.Componentsを参照)

新しいクラスライブラリ(パッケージ)を作成し、その中にビューコンポーネントを作成しました。また、ネストされたフォルダーの規則に従ってビューを作成しました。私のBookStore.Componentsプロジェクトは次のようになります。

enter image description here

Webプロジェクトからこのビューコンポーネントを呼び出そうとすると、次のようになります。

@Component.Invoke("BookOfTheMonth")

...コンテンツ本文が空の場合に500エラーが発生します。 ViewComponentクラスが検出されたようですが、コンポーネントのかみそりビューは検出されていません。

また、DefaultViewComponentDescriptorProviderを拡張して、BookStore.Componentsアセンブリのビューコンポーネントを検出できるようにしました。

AssemblyProviderを定義しました

 public class AssemblyProvider : IAssemblyProvider
    {
        public IEnumerable<Assembly> CandidateAssemblies
        {
            get
            {
                yield return typeof(AssemblyProvider).Assembly;
                yield return typeof(BookStore.Components.BookOfTheMonthViewComponent).Assembly;
            }
        }
    }

Autofacを使用してAssemblyProviderを登録

builder.RegisterType<AssemblyProvider>()
    .AsImplementedInterfaces();

builder.RegisterType<DefaultViewComponentDescriptorProvider>()
    .AsImplementedInterfaces();

上記のDefaultViewComponentDescriptorProviderの登録が必要かどうかわからないので、登録の有無にかかわらず試しましたが、ビューコンポーネントが呼び出されたページで500エラーが発生します。

MVC6 Webプロジェクトとは別のアセンブリにあるビューコンポーネントを呼び出すにはどうすればよいですか?

20
Johnny Oshika

更新2017-03-09

Visual Studio 2017では、MSBuildを使用して状況が少し変わりました。幸いなことに、それははるかに簡単です。これを機能させる方法は次のとおりです。

外部アセンブリで、これをcsprojファイルに追加します。

<ItemGroup>
   <EmbeddedResource Include="Views/**/*.cshtml" />
</ItemGroup>

メインのWebプロジェクトで、次のNuGetパッケージを追加します:Microsoft.Extensions.FileProviders.Embedded

次に、スタートアップで、外部アセンブリをファイルプロバイダーのリストに追加します。

    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.FileProviders.Add(new EmbeddedFileProvider(
             typeof(SampleClassInAssembly).Assembly
             # Prior to .Net Standard 2.0
             # typeof(SampleClassInAssembly).GetTypeInfo().Assembly
        ));
    });

人々がまだこれを古いバージョンの.NetCoreとproject.jsonで動作させようとしている場合に備えて、今のところ元の答えを残しておきます。

================================================== ==============

これを機能させるための手順は次のとおりです。

  • コンポーネントアセンブリのビュー構造がWebプロジェクトと同じであることを確認してください。質問と一緒に投稿したスクリーンショットに誤りがあったことに注意してください。
  • WebプロジェクトのStartup.csCompositeFileProviderを登録します。

    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.FileProvider = new CompositeFileProvider(
            new EmbeddedFileProvider(
                typeof(BookOfTheMonthViewComponent).GetTypeInfo().Assembly,
                "BookStore.Components"
            ),
            options.FileProvider
        );
    });
    

CompositeFileProviderEmbeddedFileProviderはどちらも新しいため、aspnetvnextNuGetフィードからこれらを取得する必要があります。私はこのソースを追加することによってこれを行いました:

enter image description here

project.jsonに依存関係を追加します。

"Microsoft.AspNet.FileProviders.Composite": "1.0.0-*",
"Microsoft.AspNet.FileProviders.Embedded": "1.0.0-*",

最後に、これをComponentsアセンブリのproject.jsonに追加します。

"resource": "Views/**"

これを機能させるには、これで十分です。

動作するデモは次のとおりです: https://github.com/johnnyoshika/mvc6-view-components/tree/master

この回答は、ここでのこのディスカッションから作成されました: https://github.com/aspnet/Mvc/issues/375

更新2016-01-15現在、外部ビューコンポーネントに1つの問題があります。ビューのcshtmlファイルに加えた変更は、自動的に再コンパイルされません。強制的なVisualStudioのクリーンアップと再構築でさえ、それを行いません。ビューの再コンパイルをトリガーするには、コンポーネントアセンブリの.csファイルを変更する必要がありますが、これは将来修正される予定のようです。この問題の理由はここで説明されています: https://github.com/aspnet/Mvc/issues/3750#issuecomment-1717653

22
Johnny Oshika

Githubで調査を行ったところ、PhysicalFileProviderlinkIFileInfo GetFileInfo(string subpath)メソッドがRazorエンジン( link )で使用されていることがわかりました。コンパイルする実際のファイルを取得します。

このメソッドの現在の実装

public IFileInfo GetFileInfo(string subpath)
{
     if (string.IsNullOrEmpty(subpath))
     {
         return new NotFoundFileInfo(subpath);
     }

     // Relative paths starting with a leading slash okay
     if (subpath.StartsWith("/", StringComparison.Ordinal))
     {
         subpath = subpath.Substring(1);
     }

     // Absolute paths not permitted.
     if (Path.IsPathRooted(subpath))
     {
         return new NotFoundFileInfo(subpath);
     }

     var fullPath = GetFullPath(subpath);
     if (fullPath == null)
     {
         return new NotFoundFileInfo(subpath);
     }

     var fileInfo = new FileInfo(fullPath);
     if (FileSystemInfoHelper.IsHiddenFile(fileInfo))
     {
         return new NotFoundFileInfo(subpath);
     }

     return new PhysicalFileInfo(_filesWatcher, fileInfo);
}

private string GetFullPath(string path)
{
    var fullPath = Path.GetFullPath(Path.Combine(Root, path));
    if (!IsUnderneathRoot(fullPath))
    {
        return null;
    }
    return fullPath;
}

ここで、絶対パスも許可もされておらず、GetFullPathメソッドはパスをメインのWebアプリケーションルートパスであるRootと組み合わせていることがわかります。

したがって、現在のフォルダ以外のフォルダからViewComponentを開くことはできないと思います。

1

。NetCore v3.x現在:

  1. [オプション] _Microsoft.Extensions.FileProviders.Embedded_ nugetパッケージを削除します
  2. _Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation_ nugetパッケージをインストールします
  3. .AddRazorRuntimeCompilation()を呼び出します。例:services.AddMvc().AddRazorRuntimeCompilation()
  4. の代わりに
_services.Configure<RazorViewEngineOptions>(options =>
{
    options.FileProviders.Add(new EmbeddedFileProvider(
         typeof(SampleClassInAssembly).Assembly
    ));
});
_

これを追加:

_services.Configure<MvcRazorRuntimeCompilationOptions>(options =>
{
    options.FileProviders.Add(new EmbeddedFileProvider(
         typeof(SampleClassInAssembly).Assembly
    ));
});
_

そして、あなたは行ってもいいです。

関連するgithubの問題

0
Mehdi Dehghani