Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Razor and interface inheritance in ASP.NET MVC3: why can't this property be found?

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#?

like image 490
Richard Avatar asked Apr 02 '12 16:04

Richard


1 Answers

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) %>
like image 118
Bryan Crosby Avatar answered Nov 18 '22 01:11

Bryan Crosby