ASP.NET Core で Razor の裏側が少し変わった

ASP.NET MVC や WebPages で大活躍の Razor ビューエンジン。 ASP.NET の一員ということで、 vNext のオープンソース化と共に GitHubでソースコードが公開されています

この記事では、 Razor のクラスを自分で使う場合にわかる変化について紹介します。

Legacy という今までの Razor を使ったプロジェクトと、 New という ASP.NET Core からの Razor を使ったプロジェクトを用意し、比較します。ソースコード等はこちらから

名前空間の変更

NuGet からライブラリを用意する場合の Razor の識別名は、今も変わらず Microsoft.AspNet.Razor です。
しかし、名前空間を見てみると、以前は System.Web.Razor 配下であるのに対し、 Core では Microsoft.AspNet.Razor となっています。

GeneratedClassContext クラスのコンストラクタの変化

このクラスは、 RazorEngineHost を使ってコード生成について設定する際、 GeneratedClassContext プロパティ(名前が同じ)に指定するためのものです。

従来の場合、使用するメソッド名を指定する際は 3 つの名前だけを引数に取るオーバーロードがありました。

Legacy/LegacyRazor.cs
22
23
24
25
26
27
28
29
30
31
32
33
var host = new RazorEngineHost(language)
{
DefaultBaseClass = nameof(Legacy) + "." + nameof(LegacyTemplateBase),
DefaultClassName = "Generated",
DefaultNamespace = "RazorTemplate",
// 生成されるメソッド名の指定(これはデフォルトと同じものを指定している)
GeneratedClassContext = new GeneratedClassContext(
nameof(LegacyTemplateBase.Execute),
nameof(LegacyTemplateBase.Write),
nameof(LegacyTemplateBase.WriteLiteral)
)
};

ASP.NET Core では、これをメソッド名を 3 つ指定する際には GeneratedTagHelperContext クラスのインスタンスが新たに必要になりました。

New/NewRazor.cs
25
26
27
28
29
30
31
32
33
34
35
36
37
var host = new RazorEngineHost(language)
{
DefaultBaseClass = nameof(New) + "." + nameof(NewTemplateBase),
DefaultClassName = "Generated",
DefaultNamespace = "RazorTemplate",
// 生成されるメソッド名の指定(これはデフォルトと同じものを指定している)
GeneratedClassContext = new GeneratedClassContext(
nameof(NewTemplateBase.ExecuteAsync),
nameof(NewTemplateBase.Write),
nameof(NewTemplateBase.WriteLiteral),
new GeneratedTagHelperContext { }
)
};

プロパティを見る限りでは、タグの生成に関する設定をするためのもののようです。なぜこうしたのかは不明。

Razor が生成するオブジェクトがガラリと変わった

従来の場合、 RazorTemplateEngine#GenerateCode メソッドは、その名前からのイメージと違い、コードの DOM を生成していました。そして、その DOM を使って文字列を出力するプログラムへとコンパイル、実行していました。

Legacy/LegacyRazor.cs
40
41
42
// コードDOM生成
var generatorResults = engine.GenerateCode(reader);
System.CodeDom.CodeCompileUnit unit = generatorResults.GeneratedCode;

ASP.NET Core では、ソースコードそのものを文字列型で出力するようになりました。

New/NewRazor.cs
45
46
47
//ソースコード生成
var generatorResults = engine.GenerateCode(reader);
string source = generatorResults.GeneratedCode;

Razor で生成されるメソッドが非同期に対応

従来は、生成されるクラス内で HTML などを出力するメソッドは void 型の Execute でした。

[Generated Code] on Lagacy
1
2
3
4
5
6
7
public abstract void Execute();

public string Generate()
{
Execute();
return builder.ToString();
}

ASP.NET Core では、 Task 型の ExecuteAsync になり、 async / await で非同期処理ができるようになりました。

[Generated Code] on New
1
2
3
4
5
6
7
public abstract Task ExecuteAsync();

public async Task<string> GenerateAsync()
{
await ExecuteAsync();
return builder.ToString();
}

おわりに

ASP.NET MVC から ‘.cshtml’ を Razor で変換するという、いつもの処理をする際には、今までとほとんど変わらない使用法になります。もし Razor を単体で使う場合には、今までの資料が役に立たない可能性があります。
そうした場合に参考になれば幸いです。

参照記事