2012-04-02 16 views
11

Tengo un problema extraño con una de mis vistas Razor en una aplicación ASP.NET MVC3. Recibo un error que me dice que no se puede encontrar una propiedad cuando la propiedad parece existir cuando escribo su valor en la consola del depurador.Razor y herencia de interfaz en ASP.NET MVC3: ¿por qué no se puede encontrar esta propiedad?

Mi vista toma como modelo una clase llamada FormEditViewModel. FormEditViewModel tiene una propiedad de tipo IForm, una interfaz que hereda de otra interfaz, IFormObject. IFormObject define un Nombre de propiedad, de modo que cualquier cosa que implemente IForm debe implementar una propiedad llamada Nombre. El formulario de tipo concreto implementa la interfaz IForm y define una propiedad Name según sea necesario.

Cuando ejecuto el código e inspecciono el objeto FormEditViewModel que se pasa a la Vista, puedo ver que tiene una propiedad Form, de tipo IForm, y este objeto Form tiene una propiedad Name. Si inserto la siguiente línea en mi controlador, para escribir el valor de FormEditViewModel.Name justo antes de que se pasa a la vista, la ventana de salida muestra el nombre correcto:

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

Sin embargo, cuando ejecuto el punto de vista que Obtiene un error que dice que "no se pudo encontrar la propiedad MyCompany.MyApplication.Domain.Forms.IForm.Name". ¿Por qué Razor no puede encontrar la propiedad Nombre cuando el código C# en el controlador evidentemente puede?

Mi opinión es la siguiente. La línea que arroja la excepción es @ Html.LabelFor (model => model.Form.Name, "Título del formulario").

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

Aquí está el modelo de vista:

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

El Objeto de formulario es muy largo, así que no se pegue todo el asunto aquí. Se declara así:

public class Form : FormObject, IForm 

El Objeto de formulario no redefine la propiedad Name, sino que hereda de la clase FormObject. FormObject comienza así:

public abstract class FormObject : IFormObject 

Aquí está la interfaz IForm. Como se puede ver, no declara un miembro Nombre, pero espera a heredar que desde 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; } 
    } 
} 

Y aquí es la IFormObject interfaz:

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

La pregunta es, ¿por qué la maquinilla de afeitar ver dame la siguiente excepción cuando lo ejecuto, ya que esperaba que IForm heredara su propiedad Name de 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 

Soy consciente de que mi jerarquía de herencia es un poco desordenado y que este no es el código más bonito, pero tengo curiosidad de saber por qué sucede esto y cuáles son mis opciones para su fijación. ¿Estoy fallando en entender algo sobre cómo funciona la herencia de interfaz en C#?

Respuesta

7

Consulte this pregunta y this uno también. Aparentemente este es un problema conocido y el trabajo parece ser:

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

Gracias Bryan.Es decepcionante ver que Microsoft no tiene la intención de arreglarlo y (según el comentario de Scott Hanselman en el primero de sus enlaces) que "Tener modelos basados ​​en interfaces no es algo que alentemos". Dado que, en principio, cualquier clase se puede pasar a una vista, incluida una clase concreta con miembros cuyo tipo es una interfaz (como en mi caso) su consejo realmente significa evitar las interfaces por completo para estar del lado seguro, lo que parece una mala práctica . Utilizaré su solución alternativa, que debería decir: @ Html.HiddenFor (m => (m como IFormObject) .Name) – Richard

+0

@Richard: Bueno, ahora que ASP.NET MVC es de código abierto, las cosas podrían cambiar :) –

Cuestiones relacionadas