Need razor view engine auto-complete to work in a class library?
We have a modular architecture where we have some views (cshtml) files in a separate project (class library). How can we get the syntax highlighting and autocomplete to work when the project isn't an MVC project?
Please note that the class library has controllers, views, models etc. It just doesn't have the web.config, global.asax, etc that a normal mvc project would have.
The intellisense works for everything but the so important model:
With MVC3 RTM, if you hover over the Model, you can now get a better error message:
C:\...\Index.cshtml: ASP.NET runtime error: There is no build provider registered for the extension '.cshtml'. You can register one in the <compilation><buildProviders> section in the machine.config or web.config. Make sure is has a BuildProviderAppliesToAttribute attribute which includes the value 'Web' or 'All'.
So I added this:
<compilation>
<assemblies>
<add assembly="System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
<buildProviders>
<add
extension=".cshtml"
type="System.Web.WebPages.Razor.RazorBuildProvider, System.Web.WebPages.Razor"/>
</buildProviders>
</compilation>
Then after adding the build provider, this error message appears:
C:\...\Index.cshtml: ASP.NET runtime error: Could not load file or assembly 'System.Web.WebPages.Razor' or one of its dependencies. The system cannot find the file specified. (C:\...\machine.config line 259)
Solution 1:
The webconfig from this post will work. I've copied it below (for posterity):
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.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=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>
<system.web>
<compilation targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</assemblies>
</compilation>
</system.web>
</configuration>
Solution 2:
For Visual Studio 2012/ASP.NET MVC 4, you need to update the assembly versions and add <add key="webpages:Version" value="2.0.0.0" />
to appSettings
. Here's what my Web.config looks like:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.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=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Version" value="2.0.0.0" />
</appSettings>
<system.web>
<compilation targetFramework="4.5">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
</system.web>
</configuration>
Solution 3:
I have followed Jammer's suggestion and am documenting what I believe is the minimal set of actions to get a project suitable for serving as an ASP.NET MVC4 class library project. This was done in Visual Studio 2012 Update 4, and was targeting VB.Net. I may later include documentation for doing something similar in Visual Studio 2013 if I get a chance. Here are the actions I took:
- Create a new blank MVC4 project with a separate directory for the solution (so you can clearly see that the
packages
directory with updated versions of reference files is part of the project). - Add a dummy controller, view and model to test it (which entailed copying the output DLL and view to another web application after building the class library).
- Delete the following files from the project:
- The whole
App_Start
folder - The whole
App_Data
folder and any other empty folders (my Mercurial history didn't make this visible so I'm going from memory). Global.asax
Global.asax.vb
- The
Web.config
file in the root and the dependentWeb.Debug.config
andWeb.Release.config
files. (Do not deleteWeb.config
from the Views folder.)
- The whole
- Delete the following sections from the
Web.config
file in the Views folder:appSettings
system.web
system.webServer
- You may also delete lines
<add namespace="System.Web.Mvc.Ajax" />
and<add namespace="System.Web.Routing" />
- Remove the following references from the project (* starred references were version-specific references that went into the
packages
directory and haveCopy Local
andSpecific Version
set toTrue
).System.Web.Entity
System.Web.ApplicationServices
System.ComponentModel.DataAnnotations
System.Data.DataSetExtensions
System.Web.Extensions
System.Web.Extensions.Design
System.Xml.Linq
System.Web.Abstractions
System.Web.Routing
System.Configuration
System.Web.Services
System.EnterpriseServices
-
Microsoft.Web.Infrastructure
(1.0.0.0) * -
Microsoft.Web.Mvc.FixedDisplayModes
(1.0.0) * -
Newtonsoft.Json
(4.5.11) * -
System.Net.Http
(2.0.20710.0) * -
System.Net.Http.Formatting
(4.0.20710.0) * -
System.Net.Http.WebRequest
(2.0.20710.0) * -
System.Web.Helpers
(2.0.20710.0) * -
System.Web.Http
(4.0.20710.0) * -
System.Web.Http.WebHost
(4.0.20710.0) *
- Remove the following project-wide Imports from the project settings:
System.Xml.Linq
System.Collections.Specialized
System.Configuration
System.Web.Caching
System.Web.Mvc.Ajax
System.Web.Routing
System.Web.SessionState
System.Web.Security
System.Web.Profile
System.Web.UI
System.Web.UI.WebControls
System.Web.UI.WebControls.WebParts
System.Web.UI.HtmlControls
- Remove the following from
packages.config
:- <package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" targetFramework="net40" />
- <package id="Microsoft.AspNet.WebApi" version="4.0.20710.0" targetFramework="net40" />
- <package id="Microsoft.AspNet.WebApi.Client" version="4.0.20710.0" targetFramework="net40" />
- <package id="Microsoft.AspNet.WebApi.Core" version="4.0.20710.0" targetFramework="net40" />
- <package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.20710.0" targetFramework="net40" />
- <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net40" />
- <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />
- <package id="Newtonsoft.Json" version="4.5.11" targetFramework="net40" />
- Delete the following sub-directories from the packages folder:
- Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0
- Microsoft.AspNet.WebApi.4.0.20710.0
- Microsoft.AspNet.WebApi.Client.4.0.20710.0
- Microsoft.AspNet.WebApi.Core.4.0.20710.0
- Microsoft.AspNet.WebApi.WebHost.4.0.20710.0
- Microsoft.Net.Http.2.0.20710.0
- Microsoft.Web.Infrastructure.1.0.0.0
- Newtonsoft.Json.4.5.11
This leaves me with the following:
- A VB.Net class library project targeting .NET Framework 4 (although I had intended this to be 4.5 - I think either works).
- The following non-default .NET references (starred references must have
Copy Local
andSpecific Version
set toTrue
):- System.Web
- packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll *
- packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll *
- packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll *
- packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll *
- packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll *
- The following non-default imports:
- System.Web
- System.Web.Mvc
- System.Web.Mvc.Html
- The following project files/structure:
-
Controllers
folder containingCustomUIController.vb
-
Models
folder containingCustomUIModel.vb
-
Views
folder containingCustomUI
folder, containingIndex.vbhtml
-
-
Web.config
file in the Views folder. See below for content. -
packages.config
file in the root of the project. See below for content.
The contents of my files are as follows:
Web.config
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.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=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Html" />
</namespaces>
</pages>
</system.web.webPages.razor>
</configuration>
packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.Mvc" version="4.0.20710.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor" version="2.0.20715.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net40" />
</packages>
CustomUIController.vb
Imports System.Web.Mvc
Public Class CustomUIController
Inherits Controller
Public Function Index() As ActionResult
Return View()
End Function
End Class
CustomUIModel.vb
Public Class CustomUIModel
Public Property Name As String
Public Property Value As Decimal
End Class
Index.vbhtml
@ModelType CustomTemplate.CustomUIModel
@Html.LabelFor(Function(m) m.Name)
At this point I am able to work with Intellisense assisting me in the .vbhtml views and the .vb classes, build the project, then copy just the views to the primary application's deployed Views
folder (in the appropriate sub-directory), and the project's primary output DLL to the primary application's deployed bin
directory (the dependent DLL files are already there).
Edit:
After following the process on another system to validate it and how it works for .NET 4.5 and VS 2013, I have noticed the following:
- I think I neglected to mention that the reference to
System.Web.DynamicData
can be removed. - In VS 2013 and/or .NET 4.5, some versions change:
- packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll
- packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll
- packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll
- packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll
- packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll
- I don't know if I made a strong enough point of it above, but the references to private (Copy Local) DLLs do have to be set to Copy Local, and/or do have to use the version provided in the packages folder when creating a project from the MVC4 template. I don't know why, but the .NET standard versions (non-private) don't seem to work, as far as Intellisense is concerned at least.
- Best way to check if things are still "OK" as far as the IDE is concerned is to close the solution, delete the
bin
andobj
folders in the custom project's output, re-load the solution, put the cursor onLabelFor
in the Index.vbhtml file, and press the F12 key to see if it takes you to the Object Browser.
Because of the new versions, the packages file is different:
Packages.config
<packages>
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net45" />
</packages>