I have an odd problem with one of my Razor views in an ASP.NET MVC3 application. I am getting an error telling me that a property cannot be found, when the property does seem to exist when I write out its value to the debugger console.
My view takes as its model a class called FormEditViewModel. FormEditViewModel has a property of type IForm, an interface that inherits from another interface, IFormObject. IFormObject defines a property Name, so anything implementing IForm must implement a property called Name. The concrete type Form implements interface IForm and defines a Name property as required.
When I run the code and inspect the FormEditViewModel object that is passed to the View, I can see that it has a property Form, of type IForm, and this Form object has a Name property. If I insert the following line into my controller, to write out the value of FormEditViewModel.Name just before it is passed to the view, the output window shows the correct name:
Debug.WriteLine("Name: " + vm.Form.Name);
Yet when I run the view I get an error saying that "The property MyCompany.MyApplication.Domain.Forms.IForm.Name could not be found." Why can Razor not find the Name property when C# code in the controller evidently can?
My view goes like this. The line that throws the exception is @Html.LabelFor(model => model.Form.Name, "Form title").
@using MyCompany.MyApplication.ViewModels;
@model FormEditViewModel
@{
ViewBag.Title = "Edit form";
}
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<div class="MyApplicationcontainer">
@using (Html.BeginForm("UpdateForm", "ZooForm"))
{
<div class="formHeader">
@Html.ValidationSummary(true)
@Html.Hidden("id", Model.Form.ZooFormId)
<div id="editFormTitleDiv">
<div class="formFieldContainer">
@Html.Label("Form ID")
@Html.TextBoxFor(m => m.Form.ZooFormId, new { @disabled = true })
</div>
<div class="formFieldContainer">
@Html.LabelFor(model => model.Form.Name, "Form title")
@Html.EditorFor(model => model.Form.Name)
@Html.ValidationMessageFor(model => model.Form.Name)
</div>
...
Here's the view model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyCompany.MyApplication.Domain.Forms;
using MyCompany.App.Web.ViewModels;
namespace MyCompany.MyApplication.ViewModels
{
public class FormEditViewModel : ViewModelBase
{
public IForm Form { get; set; }
public int Id
{
get { return Form.ZooFormId; }
}
public IEnumerable<Type> Types { get; set; }
public Dictionary<string, string> FriendlyNamesForTypes { get; set; }
public Dictionary<string, string> FriendlyNamesForProperties { get; set; }
public IEnumerable<String> PropertiesForUseInForms { get; set; }
public ObjectBrowserTreeViewModel ObjectBrowserTreeViewModel { get; set; }
}
}
The Form object is really long so I won't paste the whole thing in here. It is declared like this:
public class Form : FormObject, IForm
The Form object does not redefine the Name property but inherits it from the FormObject class. FormObject starts like this:
public abstract class FormObject : IFormObject
Here is the interface IForm. As you can see, it does not declare a Name member, but expects to inherit that from IFormObject:
using System;
namespace MyCompany.MyApplication.Domain.Forms
{
public interface IForm : IFormObject
{
bool ContainsRequiredFields();
MyCompany.MyApplication.Domain.Forms.Factories.IFormFieldFactory FormFieldFactory { get; }
MyCompany.MyApplication.Domain.Forms.Factories.IFormPageFactory FormPageFactory { get; }
string FriendlyName { get; set; }
System.Collections.Generic.List<IFormField> GetAllFields();
System.Collections.Generic.IEnumerable<DomainObjectPlaceholder> GetObjectPlaceholders();
System.Collections.Generic.IEnumerable<IFormField> GetRequiredFields();
System.Collections.Generic.IEnumerable<MyCompany.MyApplication.Models.Forms.FormObjectPlaceholder> GetRequiredObjectPlaceholders();
System.Collections.Generic.List<IFormSection> GetSectionsWithMultipliableOption();
MyCompany.MyApplication.BLL.IHighLevelFormUtilities HighLevelFormUtilities { get; }
int? MasterId { get; set; }
DomainObjectPlaceholder MasterObjectPlaceholder { get; set; }
MyCompany.MyApplication.Domain.Forms.Adapters.IObjectPlaceholderAdapter ObjectPlaceholderAdapter { get; }
MyCompany.MyApplication.Domain.Forms.Adapters.IObjectPlaceholderRelationshipAdapter ObjectPlaceholderRelationshipAdapter { get; }
System.Collections.Generic.List<IFormPage> Pages { get; set; }
MyCompany.MyApplication.Repository.IAppRepository AppRepo { get; set; }
int ZooFormId { get; }
MyCompany.MyApplication.BLL.IPocoUtils PocoUtils { get; }
void RemoveSectionWithoutChangingDatabase(int sectionId);
int? TopicId { get; set; }
DomainObjectPlaceholder TopicObjectPlaceholder { get; set; }
System.Collections.Generic.IEnumerable<FluentValidation.Results.ValidationResult> ValidationResults { get; set; }
}
}
And here is the interface IFormObject:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyCompany.MyApplication.Domain.Forms
{
public interface IFormObject
{
string Name { get; }
string LongName { get; }
Guid UniqueId { get; }
string Prefix { get; }
string IdPath { get; set; }
string IdPathWithPrefix { get; }
}
}
The question is, why does the Razor view give me the following exception when I run it, since I would have expected IForm to inherit its Name property from IFormObject?
Server Error in '/' Application.
The property MyCompany.MyApplication.Domain.Forms.IForm.Name could not be found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentException: The property MyCompany.MyApplication.Domain.Forms.IForm.Name could not be found.
Source Error:
Line 24: </div>
Line 25: <div class="formFieldContainer">
Line 26: @Html.LabelFor(model => model.Form.Name, "Form title")
Line 27: @Html.EditorFor(model => model.Form.Name)
Line 28: @Html.ValidationMessageFor(model => model.Form.Name)
Source File: c:\Users\me\Documents\Visual Studio 2010\Projects\zooDBMain\zooDB\zooDB\Views\ZooForm\Edit.cshtml Line: 26
Stack Trace:
[ArgumentException: The property MyCompany.MyApplication.Domain.Forms.IForm.Name could not be found.]
System.Web.Mvc.AssociatedMetadataProvider.GetMetadataForProperty(Func`1 modelAccessor, Type containerType, String propertyName) +505385
System.Web.Mvc.ModelMetadata.GetMetadataFromProvider(Func`1 modelAccessor, Type modelType, String propertyName, Type containerType) +101
System.Web.Mvc.ModelMetadata.FromLambdaExpression(Expression`1 expression, ViewDataDictionary`1 viewData) +421
System.Web.Mvc.Html.LabelExtensions.LabelFor(HtmlHelper`1 html, Expression`1 expression, String labelText) +56
ASP._Page_Views_ZooForm_Edit_cshtml.Execute() in c:\Users\me\Documents\Visual Studio 2010\Projects\zooDBMain\zooDB\zooDB\Views\ZooForm\Edit.cshtml:26
System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +272
System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +81
System.Web.WebPages.StartPage.RunPage() +58
System.Web.WebPages.StartPage.ExecutePageHierarchy() +94
System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +173
System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +220
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +115
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +303
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13
System.Web.Mvc.<>c__DisplayClass1c.<InvokeActionResultWithFilters>b__19() +23
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +260
System.Web.Mvc.<>c__DisplayClass1e.<InvokeActionResultWithFilters>b__1b() +19
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +177
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
System.Web.Mvc.Controller.ExecuteCore() +116
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8971485
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.547
I appreciate that my inheritance hierarchy is a bit messy and that this is not the most beautiful code, but I'm curious to know why this happens and what my options are for fixing it. Am I failing to understand something about how interface inheritance works in C#?
Check out this question and this one as well. Apparently this is a known issue and the work around seems to be:
<%: Html.HiddenFor(m => (m as IFormObject).Name) %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With