ASP.NET MVC RC版以降では、VB.NET版 Razorに対応している。当記事の寿命は、わずか一週間程度の命だったようだ…orz
何度か私のブログでも紹介させて頂いた ASP.NET MVC3 Previewだが、ASP.NET MVC3から導入された便利なRazorを利用しているだろうか。「Razorってなんだろう?」という方は、以下の記事を一読してRazorを試して頂けると幸いだ。
ASP.NET WebFormやASP.NET MVCで利用していた従来の記述方法と異なり、Razorはデザインとロジックの分離が一層はかられた記述方法であることが理解できるだろう。
今回は、ASP.NET MVC3 Preview版では未サポートであるVB.NET版のRazor記法を動作させる手順を紹介する*1。
VB.NET版 Razor(*.vbhtml)を動かすための手順
動作させるためには以下の手順が必要だ。MVC3 Previewソースのリコンパイルが必要であるため、心して準備して頂きたい。
- ASP.NET MVC3 Previewのソースをダウンロード&zipファイルを展開
- ASP.NET MVC3 Preview側
- ASP.NET MVC3 Preview側のソースコードを修正
- ASP.NET MVC3 Preview3 VB.NET版ソリューション
上記の手順を踏めば、VB.NET版 Razorが利用できる(次の画像例を参照)。
今回の記事での動作例は、あくまで「サポートしていないものを無理やり動かした」ものである点に注意してほしい。今回紹介したものがVB.NET Razorの完成版文法である保証はない。
ASP.NET MVC3 Previewのソースをダウンロード&zipファイルを展開
ASP.NET MVC3 Previewのソースコードは以下から取得できる。「ASP.NET MVC3 Preview 1 Source Code」のリンクから、同バージョンのソースコードを入手してほしい。
http://aspnet.codeplex.com/releases/view/50092
ダウンロード後、任意のフォルダにzipファイルを展開し、「mvc3-preview1-sources」フォルダ内に「MvcDev.sln」が存在することを確認してほしい。
ASP.NET MVC3 Preview側のソースコードを修正
ASP.NET MVC3 Preview側のソースコードを修正する箇所が3点ほど存在する。修正点は以下となる。
- System.Web.Mvc.PreApplicationStartCode内のコメントアウトを削除
vbhtmlが含まれる行のコメントアウトを削除し、ソースコードを有効化する。
public static class PreApplicationStartCode { private static bool _startWasCalled; public static void Start() { // Guard against multiple calls. All Start calls are made on same thread, so no lock needed here if (_startWasCalled) return; _startWasCalled = true; BuildProvider.RegisterBuildProvider(".cshtml", typeof(InlinePageBuildProvider)); //次の行を有効化した BuildProvider.RegisterBuildProvider(".vbhtml", typeof(InlinePageBuildProvider));
- System.Web.Mvc.VbhtmlView.cs の追加
System.Web.Mvc.CshtmlView.csをベースに作成する。ソースコードを以下に示す。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace System.Web.Mvc { using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Web.Compilation; using System.Web.Mvc.Resources; using Microsoft.WebPages; [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Vbhtml", Justification = "Vbhtml is the name of the feature")] public class VbhtmlView: IView { private IBuildManager _buildManager; private Func<IMvcServiceLocator> _serviceLocatorThunk = () => MvcServiceLocator.Current; [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose is to simplify the API")] public VbhtmlView(string viewPath, string layoutPath = "") { if (String.IsNullOrEmpty(viewPath)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewPath"); } ViewPath = viewPath; LayoutPath = layoutPath ?? String.Empty; } internal VbhtmlView(string viewPath, string layoutPath, IMvcServiceLocator serviceLocator) : this(viewPath, layoutPath) { _serviceLocatorThunk = () => serviceLocator; } internal IBuildManager BuildManager { get { if (_buildManager == null) { _buildManager = new BuildManagerWrapper(); } return _buildManager; } set { _buildManager = value; } } public string LayoutPath { get; private set; } public string ViewPath { get; private set; } protected internal virtual object CreateViewInstance() { Type type = BuildManager.GetCompiledType(ViewPath); if (type == null) { return null; } try { return _serviceLocatorThunk().GetInstance(type); } catch (ActivationException) { return Activator.CreateInstance(type); } } public void Render(ViewContext viewContext, TextWriter writer) { if (viewContext == null) { throw new ArgumentNullException("viewContext"); } if (writer == null) { throw new ArgumentNullException("writer"); } object instance = CreateViewInstance(); if (instance == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, //VB版がなかった。。。 MvcResources.CshtmlView_ViewCouldNotBeCreated, ViewPath)); } WebViewPage webViewPage = instance as WebViewPage; if (webViewPage == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, //VB版がなかった。。。 MvcResources.CshtmlView_WrongViewBase, ViewPath)); } webViewPage.OverridenLayoutPath = LayoutPath; webViewPage.VirtualPath = ViewPath; webViewPage.ViewContext = viewContext; webViewPage.ViewData = viewContext.ViewData; webViewPage.InitHelpers(); webViewPage.ExecutePageHierarchy(new WebPageContext(), writer, webViewPage); } } }
- System.Web.Mvc.VbhtmlViewEngine.csを追加する
System.Web.Mvc.CshtmlViewEngine.csをベースに作成する。ソースコードを以下に示す。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace System.Web.Mvc { using System.Diagnostics.CodeAnalysis; [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Vbhtml", Justification = "Vbhtml is the name of the feature")] public class VbhtmlViewEngine : VirtualPathProviderViewEngine { private IBuildManager _buildManager; public VbhtmlViewEngine() { AreaViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; AreaMasterLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; AreaPartialViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" }; ViewLocationFormats = new[] { "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.vbhtml" }; MasterLocationFormats = new[] { "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.vbhtml" }; PartialViewLocationFormats = new[] { "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.vbhtml" }; } internal IBuildManager BuildManager { get { if (_buildManager == null) { _buildManager = new BuildManagerWrapper(); } return _buildManager; } set { _buildManager = value; } } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return CreateView(partialPath); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { var view = CreateView(viewPath, masterPath); return view; } private static VbhtmlView CreateView(string viewPath, string layoutPath = "") { VbhtmlView view = new VbhtmlView(viewPath, layoutPath); return view; } protected override bool FileExists(ControllerContext controllerContext, string virtualPath) { return BuildManager.FileExists(virtualPath); } } }
ASP.NET MVC3 Preview VB版のソリューション作成
Visual Studio 2010 から[ファイル]→[新規作成]→[プロジェクト]を選択し、「ASP.NET MVC3 Web Application(ASPX)」のプロジェクトを作成してほしい。
ASP.NET MVC3 Preview の参照先をダウンロードしたソースに変更
ASP.NET MVC2 Preview時点の記事ではあるが、以下を参照して、ASP.NET MVC3 Previewの参照先を変更して頂きたい。
id:waritohutsu:20090912:1252738647
Global.asaxの修正
作成した「System.Web.Mvc.VbhtmlViewEngine」をデフォルトのViewEngineとして登録する。
- Global.asax(抜粋)
Public Class MvcApplication Inherits System.Web.HttpApplication (中略) Sub Application_Start() AreaRegistration.RegisterAllAreas() RegisterGlobalFilters(GlobalFilters.Filters) RegisterRoutes(RouteTable.Routes) ViewEngines.Engines.Add(New VbhtmlViewEngine()) End Sub End Class
コントローラ(*.vb)のソースファイルを修正
/Controllers/HomeController.vb の内容を、以下を参考に修正する。
- HomeController.vb
Public Class HomeController Inherits System.Web.Mvc.Controller Function Index() As ActionResult Dim list = New List(Of Person) list.Add(New Person()) list.Add(New Person("二個目")) list.Add(New Person("三個目")) ViewData("Message") = "Welcome to ASP.NET MVC!" ViewData("Owner") = "normalian" ViewData("Number") = 9 Return View(list) End Function Function About() As ActionResult Return View() End Function End Class Public Class Person Public Sub New() name = "デフォルト" End Sub Public Sub New(ByVal arg As String) Name = arg End Sub Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property End Class
画面(*.aspx)のソースファイルを修正
/Views/Home/Index.aspxのファイル名を/Views/Home/Index.vbhtmlに変更し、内容を以下を参考に修正する。
@inherits System.Web.Mvc.WebViewPage <h2>値の参照を試す</h2> <ul> <li> @View.Message 、ひゃっほう!! </li> <li> @DateTime.Now </li> </ul> <h2>If文を試す</h2> @If View.Number Mod 2 = 0 Then @<p>odd</p> Else @<p>even</p> End If @If View.Owner = "normalian" Then @<p>Yes</p> Else @<p>No</p> End If <h2>フォーム系を試す</h2> @Using Html.BeginForm() @<div> divタグ表示 </div> @<input type="submit" value="発射" /> End Using <h2>ループを試す</h2> <ul> @For Each p in Model @<li>@p.Name</li> Next </ul>