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: screenshot of model error

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:

  1. 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).
  2. 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).
  3. 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 dependent Web.Debug.config and Web.Release.config files. (Do not delete Web.config from the Views folder.)
  4. 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" />
  5. Remove the following references from the project (* starred references were version-specific references that went into the packages directory and have Copy Local and Specific Version set to True).
    • 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) *
  6. 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
  7. 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" />
  8. 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:

  1. A VB.Net class library project targeting .NET Framework 4 (although I had intended this to be 4.5 - I think either works).
  2. The following non-default .NET references (starred references must have Copy Local and Specific Version set to True):
    • 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 *
  3. The following non-default imports:
    • System.Web
    • System.Web.Mvc
    • System.Web.Mvc.Html
  4. The following project files/structure:
    • Controllers folder containing CustomUIController.vb
    • Models folder containing CustomUIModel.vb
    • Views folder containing CustomUI folder, containing Index.vbhtml
  5. Web.config file in the Views folder. See below for content.
  6. 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:

  1. I think I neglected to mention that the reference to System.Web.DynamicData can be removed.
  2. 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
  3. 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.
  4. Best way to check if things are still "OK" as far as the IDE is concerned is to close the solution, delete the bin and obj folders in the custom project's output, re-load the solution, put the cursor on LabelFor 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>