Бритва и наследование интерфейса в ASP.NET MVC3: почему не удается найти это свойство? - PullRequest
12 голосов
/ 02 апреля 2012

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

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

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

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

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

Мой взгляд таков. Строка, которая выдает исключение, является @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. Как вы можете видеть, он не объявляет члена Name, но ожидает наследовать его от 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

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

1 Ответ

9 голосов
/ 02 апреля 2012

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

<%: Html.HiddenFor(m => (m as IFormObject).Name) %>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...