ASP.NET MVC 5 / Core MVCで独自のビューページのクラスを使用する

この記事はASP.NET Advent Calendar 2016の9日目の記事です。

当初はASP.NET Core MVCのControllerのコンストラクタについて書こうと思ったのですが、たまたま業務中にASP.NET Core MVCで独自のビューページのクラスが必要になって試してみたらすんなり上手く出来たので書くことにしました。

ASP.NET MVC 5でも同様のことが出来るのでこちらについても言及しておきます。

## ビューページクラスとは

この呼び名が正しいかどうかはわからないんですが、各Viewファイル(.cshtmlや.vbhtml)の親クラスだと思っておくと理解し易いんじゃないかと思います。

というのも、このクラスで設定・追加したプロパティがViewの開発時に使用出来るようになるからです。

どんな時に独自のビューページのクラスが必要になるかというと結構ケースバイケースなんですが、私の場合は全てのViewファイルで共通のプロパティを追加したかったので使用しました。

あとはHtmlヘルパーに拡張メソッドを追加しすぎるとインテリセンス汚染になるので、独自のヘルパークラスを用意した時にも使うことになると思います。

## ASP.NET MVC 5の場合

ASP.NET MVC 5で独自のビューページのクラスを使う時はSystem.Web.Mvc名前空間にあるWebViewPageというクラスを継承して新しくクラスを作成します。

WebViewPageクラスにはレンダリングする内容を詰め込んだクラス(よくModelとかViewModelって言われてるもの、以下Modelクラスと呼びます)をジェネリックで指定するクラスと、何も指定しないクラスがあります。

Modelクラスを指定しないビューページクラスだけを用意すると、いざModelクラスを使用するページがあった時にエラーになってしまいます。

逆にModelクラスを指定するビューページクラスだけを用意して、Modelクラスを使用しないページがあってもエラーになりません。

そのためModelクラスを指定するビューページクラスだけを作成しておけばどのケースのViewにでも対応することが出来ます。

またもう一点注意しておくことは独自のビューページクラスは抽象クラスとして作成する必要があることです。

```csharp
// Modelクラスを必要としない場合のビューページクラス
public abstract class CustomWebViewPage : WebViewPage
{
    // 独自のプロパティ等を追加

    public bool IsDebug { get; set; }

    protected CustomWebViewPage()
    {
#if DEBUG
        IsDebug = true;
#else
        IsDebug = false;
#endif
    }
}

// Modelクラスを必要とする場合のビューページクラス
public abstract class CustomWebViewPage<TModel> : WebViewpage<TModel> where TModel : class
{
   // 独自のプロパティ等を追加

    public bool IsDebug { get; set; }

    protected CustomWebViewPage()
    {
#if DEBUG
        IsDebug = true;
#else
        IsDebug = false;
#endif
    }
}
```

上のサンプルではデバッグビルドかどうかを判断するプロパティを追加した独自のビューページクラスを作成しました。

クラスを作成したらWeb.configという構成ファイルで設定する必要があります。

設定するファイルはよりViewファイルに近いWeb.configを使用します。(通常はプロジェクト直下ですがViewsフォルダないにある場合はViewsフォルダ内のWeb.config。)

```xml
<?xml version="1.0"?>
<configuration>

    <!--設定を追加するための構成セクション追加-->
    <configSections>
        <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
            <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
            <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
        </sectionGroup>
    </configSections>

    <system.web.webPages.razor>
        <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <!--ここに独自ビューページクラスのフルクラス名を記述する-->
        <pages pageBaseType="SampleApp.CustomWebViewPage">
            <namespaces>
                <add namespace="System.Web.Mvc" />
                <add namespace="System.Web.Mvc.Ajax" />
                <add namespace="System.Web.Mvc.Html" />
                <add namespace="System.Web.Optimization"/>
                <add namespace="System.Web.Routing" />
                <add namespace="WebViewPageSample" />
            </namespaces>
        </pages>
    </system.web.webPages.razor>

</configuration>
```

上のように、system.webServer.razor#pagesのpageBaseTypeに作成したビューページクラスのフルクラス名を設定すれば、Viewファイルで追加したプロパティがインテリセンスで使用出来るようになります。

Modelクラスを指定するクラスを設定する場合でも、\<TModel>の部分は意識する必要はありません。

## ASP.NET Core MVCの場合

ASP.NET Core MVCでは継承する元のクラスがMicrosoft.AspNetCore.Mvc.Razor名前空間のRazorPageに変わります。

クラスを作成する時の注意点はASP.NET MVC 5と同じで、以下の2つです。

* Modelクラスを指定するビューページクラスを作成すればModelクラスの使用・未使用に関わらず使用できる
* 抽象クラスとして作成する必要がある

```csharp
// Modelクラスを必要としない場合のビューページクラス
public abstract class CustomRazorPage : RazorPage
{
   　// 独自のプロパティ等を追加

    public bool IsDebug { get; set; }

    protected CustomRazorPage()
    {
#if DEBUG
        IsDebug = true;
#else
        IsDebug = false;
#endif
    }
}

// Modelクラスを必要とする場合のビューページクラス
public abstract class CustomRazorPage<TModel> : CustomRazorPage<TModel>
{
    // 独自のプロパティ等を追加

    public bool IsDebug { get; set; }

    protected CustomRazorPage()
    {
#if DEBUG
        IsDebug = true;
#else
        IsDebug = false;
#endif
    }
}
```

上のサンプルもASP.NET MVC 5の時と同じようにデバッグビルドかどうかを判別するプロパティを追加しています。

で、作成したビューページクラスを設定する場所ですが、ASP.NET CoreにWeb.configはありません。(IISホストするときはあるんですが、IISホストとは限らないので…。)

じゃあどこで設定するのかというとViewsフォルダ内にある_ViewImport.cshtmlです。

この_ViewImport.cshtmlですが、共通で使う名前空間の設定などASP.NET MVC 5のViewsフォルダ内の構成ファイルと同じような役割を持っていて、他にもタグヘルパーやDIの設定を行うことができるようです。

作成した独自のビューページクラスは_ViewImport.cshtmlに以下のように設定します。

```csharp
@inherits SampleApp.CustomRazorPage<TModel>
```

Modelクラスを指定する場合、ジェネリックの型変数名はTModelで決め打ちのようです。(これ以外の名前にしたらエラーになりました。)

ローカルのVisual Studioだとインテリセンスが効かなかったんですが、ちゃんと実行されて追加されたプロパティに基づく処理も行われました。

## まとめ

普段ASP.NET MVCで開発していて独自のプロパティやメソッドを追加したくなることはそうそうないと思いますが、いつか必要になったときの参考になればと思います。(割と大規模な開発だとあるかもしれません…。)

今回継承した元のクラスにはいくつかabstractなメソッドやvirtualなメソッドも用意されていて、これらのメソッドをoverrideすればASP.NETのパイプラインに処理をフックさせることも可能です。(実際ASP.NET MVC 5でヘルパークラスを追加する時はInitHelperメソッドをoverrideして実現します。)

今日書く予定だったASP.NET Core MVCのControllerのコンストラクタについてはまた別途書く予定(は未定)です。