C# – External ASP.NET MVC 3 area not compiling at runtime (works in Preview 1 but not RC)

asp.net-mvcasp.net-mvc-3c

Problem

ASP.NET MVC 3 RC is giving me a compilation error at runtime (when browsing) to an external MVC area. The website itself works, but the plugin refuses to load throwing a compilation issue pertaining to an unknown model.

LogOn.cshtml

@model TestProject.Models.LogOnModel
@{
    View.Title = "Log On";
}
//.....omitted for brevity

Error thrown at runtime.

Compilation Error

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately. 

Compiler Error Message: CS0103: The name 'model' does not exist in the current context

Source Error:


Line 38:         public override void Execute() {
Line 39: 
Line 40: Write(model);
Line 41: 
Line 42: WriteLiteral(" TestPlugin.Models.LogOnModel\r\n");

Background

The website is an ASP.NET MVC 3 Preview 1 website I have just migrated to ASP.NET MVC 3 RC. I read the release notes and updated accordingly but still encountered an issue with WebMatrix. I found a SO question where someone from the MVC team suggested until RTM we use the following to force the namespaces to be pulled in (I put them in web.config but it didn't work).

namespace WebMatrix.Data { class Foo { } }
namespace WebMatrix.WebData { class Foo { } }

This solved my issue within the website and the website now loads up fine in MVC 3 RC. The problem however, is an external MVC area (separate assembly).

Note I'm not using any third party libraries for this, I wrote a little plugin framework that allows loading an MVC area from another assembly. Views are compiled as embedded resources.

So I have an authentication plugin for example that looks like.

-Controllers
-- AccountController.cs
-Models
-- AccountModels.cs
-Views
--Account
--- LogOn.cshtml
--- ChangePassword.cshtml
--- ...
-Web.config
AccountAreaRegistration.cs
Web.config

Other than the fact that the views are marked as embedded resources, the AccountController extending a PluginController that knows how to load the embedded resouces and the global.asax being removed, its a pretty standard MVC area.

I have tried creating a new empty MVC 3 test plugin project and slowly adding in the relevant code. This ensures all references and web.configs are correct. But I still receive the same model issue described above. The project compiles at design time but throws a compilation issue at runtime when it tries to emit the views it looks.

Just to be clear, this all works fine in ASP.NET MVC 3 Preview 1. However, now that I have upgraded it to MVC 3 RC I have the core website working but the external area refuses to work.

There is nothing wrong the the plugin framework itself. It works fine in MVC 3 Preview 1. I'm looking for answers that might shed a little bit of light on what may be occuring in MVC 3 RC and why I might be using receiving this model error.

Any ideas?

Update

Interestingly if I change the declaration of the model at the top of the view to Preview 1 syntax `@inherits System.Web.Mvc.WebViewPage

I get a different runtime compiler error. This time I get an error just further down when the Html.ValidationSummary begins for the login form.

@inherits System.Web.Mvc.WebViewPage<TestProject.Models.LogOnModel>
@{
    View.Title = "Log On";
}
<div class="content_item half loginform">
    <div class="content_body">
        <h1>VastID Login</h1>
        @Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.") // <=== Compilation issue here on Html.ValidationSummary
        @using (Html.BeginForm())
        {
          // .. omitted for brevity
        }
    </div>
</div>
Compilation Error

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately. 

Compiler Error Message: CS1061: 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'ValidationSummary' and no extension method 'ValidationSummary' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)

Source Error:


Line 49: 
Line 50: 
Line 51:    Write(Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again."));
Line 52: 
Line 53: WriteLiteral("\r\n");

It's interesting that there is no compile time errors. System.Web.WebPages, System.Web.Helpers and all the necessary MVC 3 RC dll's are present. Including the necessary web.config's. It's as if these aren't available when the view is trying to be loaded however.

The actual razor syntax I believe is part of System.Web.WebPages? Where does Html.ValidationSummary live? System.Web.Mvc or System.Web.Helpers?

Best Answer

I actually discovered that an ASP.NET MVC 3 RC web.config was missing from my /Plugins folder. This is an empty folder other than a web.config that must be present for the plugin virtual path provider. This was the missing link.

Basically what happens is that you have a controller which extends PluginController. This controller alters the path to the views and appends 'Plugin' and the assembly name (remeber we are dealing with embedded views here). So you end up with something like

/Plugins/TestProject.dll/TestProject.Views.Account.LogOn.cshtml

The virtual path provider and collective classes then take care of finding and returning this view from the plugins resource manifest.

What had me really stumped was a breaking change between Preview 1 and Beta where they added an _ViewStart.cshtml. When a view such as Logon.cshtml was requested from the plugin an internal request by the framework was being made for a _ViewStart file which did not exist in that assembly. As execution was already in the virtual path provider at that point I couldn't find out to return the _ViewStart.cshtml from the website. So instead I added one to the plugin.

I had to make alterations to both the PluginVirtualFile.cs and the PluginProvider.cs. So I got it all working in the end.

Thanks very much for your help though @Eilon. Though it didn't arrive on the answer it was greatly appreciated.