Razor и наследование интерфейсов в ASP.NET MVC3: почему это свойство не может быть найдено?

У меня есть странная проблема с одним из моих представлений Razor в приложении ASP.NET MVC3. Я получаю сообщение об ошибке, указывающее, что свойство не может быть найдено, когда свойство действительно существует, когда я выписываю его значение в консоль отладчика.

My view принимает в качестве своей модели класс FormEditViewModel. FormEditViewModel имеет свойство типа IForm, интерфейс, который наследуется от другого интерфейса, IFormObject. IFormObject определяет имя свойства, поэтому все, что реализует IForm, должно реализовать свойство Name. Конкретный тип Form реализует интерфейс IForm и определяет свойство Name по мере необходимости.

Когда я запускаю код и проверяю объект FormEditViewModel, который передается в представление, я вижу, что он имеет свойство Form, типа IForm, и этот объект Form имеет свойство Name. Если я вставляю следующую строку в свой контроллер, чтобы выписать значение FormEditViewModel.Name перед передачей в представление, в окне вывода отображается правильное имя:

Debug.WriteLine("Name: " + vm.Form.Name);

Тем не менее, когда я запускаю представление, я получаю сообщение об ошибке: "Свойство MyCompany.MyApplication.Domain.Forms.IForm.Name не найдено". Почему Razor не может найти свойство Name, если код С# в контроллере, по-видимому, может?

Мой взгляд выглядит следующим образом. Строкой, которая генерирует исключение, является @Html.LabelFor(model = > model.Form.Name, "Название формы" ).

@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>
                ...

Здесь модель представления:

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; }
    }
}

Объект Form действительно длинный, поэтому я не буду вставлять все это здесь. Он объявляется следующим образом:

public class Form : FormObject, IForm

Объект Form не переопределяет свойство Name, а наследует его от класса FormObject. FormObject запускается следующим образом:

public abstract class FormObject : IFormObject

Вот интерфейс IForm. Как вы можете видеть, он не объявляет член имени, но надеется наследовать его от 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; }
    }
}

И вот интерфейс 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; }
    }
}

Вопрос в том, почему представление Razor дает мне следующее исключение, когда я запускаю его, так как я ожидал, что IForm наследует его свойство Name из 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

Я ценю, что моя иерархия наследования немного беспорядочна и что это не самый красивый код, но мне любопытно узнать, почему это происходит и каковы мои возможности для его исправления. Неужели я не понимаю, как работает наследование интерфейсов на С#?

Ответ 1

Просмотрите этот вопрос и этот. По-видимому, это известная проблема, и работа вокруг кажется:

<%: Html.HiddenFor(m => (m as IFormObject).Name) %>