ASP.NET Core でも外部アセンブリの View を取り込みたい場合

ASP.NET で開発してると、様々な都合・背景で、Controller や View を別のアセンブリにしたくなるケースがあります。いろんな実装方法があるとは思いますが、シンプルなやつを一つ。アイキャッチ画像は、オーシャンビューです。

本記事は http://qiita.com/advent-calendar/2016/asp-net の8日目です。

この説明の環境は以下の通り。

  • Visual Studio 2015 Update 3 (Communityでもなんでも)
  • .NET Core 1.0 / 1.1 (サンプルコードは1.1)
  • Windows 10 IP 14971

結論から

別アセンブリに View を配置して、 project.json にbuildOptions:{"embed":["Views/**"]} のノリでビルドに含む対象として追加してあげます。

使う側では、 RazorViewEngineOptions()EmbeddedFileProvider() で別アセンブリの情報と namespace を追加してあげれば動きます。

がサンプルコードのそれ。

見るといい感じのファイル

先のコードで言えば、以下あたり。

+- ClassLibray
| |- project.json
| |- AddEmbeddedViews.cs
| +- Views/**
+- WebApplication
|- Startup.cs

ClassLibray の project.json は、以下のノリで指定して、 cshtml を含めてビルドするように指定します。

"buildOptions": {
"embed": [
"Views/**"
]
}

ClassLibray の AddEmbeddedViews.cs は、自分のアセンブリの情報を、ASP.NET Core の本体側(Startup.cs)で読み込む際に使う拡張メソッドを定義したもの。"ClassLibray" と Namespace を指定してる部分に注意です。いい方法あれば適時それで指定してあげてください。以下サンプル。

using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.FileProviders;
using System.Reflection;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ClassLibraryEmbeddedViewsExtensions
{
public static IServiceCollection AddEmbeddedViews
(this IServiceCollection services)
{
var optionsSetup =
new ConfigureOptions<RazorViewEngineOptions>(options =>
{
options.FileProviders.Add(new EmbeddedFileProvider(
typeof(ClassLibraryEmbeddedViewsExtensions)
.GetTypeInfo().Assembly,
"ClassLibrary"));
});
services.AddSingleton
<IConfigureOptions
<RazorViewEngineOptions>>(optionsSetup);
return services;
}
}
}

WebApplication の Startup.cs で以下のノリです。

public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
    // Add Views from Another Assembly ここ
services.AddEmbeddedViews();
}

注意点

デバッグ中、 別アセンブリの View を変更してデバッグ実行(つまりビルドするだけ)だと、その変更が反映されないです。なので、リビルドする事になってしまい、ちょっと面倒。また、View の数が増えてくると、デバッグビルドで初期化までにとっても時間がかかるようになります。今後の改善に期待ってところですが、View が多い環境では、大変デバッグが遅くなるので悩みどころ。デバッグ中だけ View を ASP.NET Core のプロジェクト側に移設したり、Release で走らせてフロント側で見るとか、ちょっとした工夫が必要になるかも。

余談ですが 、RC1 あたりと今とでは、 Azure Web Apps にデプロイした際のファイルの配置構造がまるで違っていたり、なのにパスを見に行く必要があったりで、以前に同じことをやろうとすると、微妙に苦労してました。今は楽です。

補足ほか

Swagger みたいに wwwroot 対してアセットを追加する感じでファイルを追加してあげれば レイアウトを持ってくこともできるんでしょうが、上記サンプルではそういうことをやってないので、View だけ外に出す効果がいまいちわかりませんよね。

なんですが、うまい範囲で切って別にしておくと、ASP.NET Core 側のアップデートには、まぁ対応しやすいなぁ、と。また、例えば開発は Web API 環境と MVC な環境を同一で動かすのだけど、本番デプロイは別の設定でホストして配置する、その逆、といった、配置の自由度を高める事が出来ます。

いざデプロイする際に、あれ FQDN 違ったの?うそん取ってない?とか、もろもろ逃げる事は出来ます。(そうなってたらかなり負けてるわけですが)