www net mvc5 mvc4 mvc framework custom asp asp.net asp.net-mvc-3 html-helper modelmetadata

mvc5 - ASP.NET MVC 3 HtmlHelper Exception no reconoce ModelMetadata en la interfaz heredada



www asp net mvc mvc4 (6)

Además de la respuesta de Anthony Johnston , es posible que obtenga excepciones al usar DataAnnotations, ya que el método AssociatedValidatorProvider.GetValidatorsForProperty () intentará usar la interfaz hereditaria como tipo de contenedor en lugar de la base y, por lo tanto, no podrá encontrar la propiedad nuevamente.

Este es el código reflejado del método GetValidatorsForProperty (es la segunda línea la que hace que la variable propertyDescriptor sea nula y, por lo tanto, se lance la excepción):

private IEnumerable<ModelValidator> GetValidatorsForProperty(ModelMetadata metadata, ControllerContext context) { ICustomTypeDescriptor typeDescriptor = this.GetTypeDescriptor(metadata.ContainerType); PropertyDescriptor propertyDescriptor = typeDescriptor.GetProperties().Find(metadata.PropertyName, true); if (propertyDescriptor != null) { return this.GetValidators(metadata, context, propertyDescriptor.Attributes.OfType<Attribute>()); } else { object[] fullName = new object[] { metadata.ContainerType.FullName, metadata.PropertyName }; throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PropertyNotFound, fullName), "metadata"); } }

Si es así, creo que el siguiente código puede ayudar, ya que se asegura de que el ContainerType esté configurado al tipo en el que se encuentra la propiedad, no al tipo del modelo de vista.

Descargo de responsabilidad: parece funcionar bien, pero aún no lo he probado completamente, ¡por lo que podría tener efectos no deseados! También entiendo que no está escrito a la perfección, pero estaba tratando de mantener el formato similar a la respuesta anterior para facilitar la comparación. :)

public class MyMetadataProvider : DataAnnotationsModelMetadataProvider { public override ModelMetadata GetMetadataForProperty( Func<object> modelAccessor, Type containerType, string propertyName) { if (containerType == null) { throw new ArgumentNullException("containerType"); } if (String.IsNullOrEmpty(propertyName)) { throw new ArgumentException( "The property &apos;{0}&apos; cannot be null or empty", "propertyName"); } var containerTypeToUse = containerType; var property = GetTypeDescriptor(containerType) .GetProperties().Find(propertyName, true); if (property == null && containerType.IsInterface) { var foundProperty = (from t in containerType.GetInterfaces() let p = GetTypeDescriptor(t).GetProperties() .Find(propertyName, true) where p != null select (new Tuple<System.ComponentModel.PropertyDescriptor, Type>(p, t)) ).FirstOrDefault(); if (foundProperty != null) { property = foundProperty.Item1; containerTypeToUse = foundProperty.Item2; } } if (property == null) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, "The property {0}.{1} could not be found", containerType.FullName, propertyName)); } return GetMetadataForProperty(modelAccessor, containerTypeToUse, property); } }

Después de actualizar a MVC 3 RTM, recibí una excepción en la que funcionaba anteriormente.

Aquí está el escenario. Tengo varios objetos que utilizan las mismas interfaces subyacentes IActivity y IOwned.

IActivity implements IOwned (another interface) public interface IActivity:IOwned {...} public interface IOwned { int? AuthorId {get;set;} }

Tengo una vista parcial que usa IActivity para reutilizarla de otros parciales concretos.

Aquí está la definición de la Actividad Parcial.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IActivity>" %> <%: Html.HiddenFor(item => item.AuthorId) %>

Sin embargo, lanza una excepción. No se puede encontrar AuthorId en el ModelMetadata.

Supongo que en la versión anterior se observaron las interfaces implementadas por IActivity.

¿Alguna idea, sugerencia, sin duplicar interfaces similares en todas partes?

Copia la traza de pila abajo.

[ArgumentException: The property IActivity.AuthorId could not be found.] System.Web.Mvc.AssociatedMetadataProvider.GetMetadataForProperty(Func`1 modelAccessor, Type containerType, String propertyName) +498313 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) +393 System.Web.Mvc.Html.InputExtensions.HiddenFor(HtmlHelper`1 htmlHelper, Expression`1 expression, IDictionary`2 htmlAttributes) +57 System.Web.Mvc.Html.InputExtensions.HiddenFor(HtmlHelper`1 htmlHelper, Expression`1 expression) +51 ASP.views_shared_activity_ascx.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in c:/Users/.../Documents/Visual Studio 2010/Projects/ngen/trunk/.../Views/Shared/Activity.ascx:3 System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +109 System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 System.Web.UI.Control.Render(HtmlTextWriter writer) +10 System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208 System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8 System.Web.UI.Page.Render(HtmlTextWriter writer) +29 System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +43 System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27 System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100 System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3060


Del equipo de MVC:

Desafortunadamente, el código en realidad estaba explotando un error que se solucionó, donde el contenedor de una expresión para fines de ModelMetadata se configuró inadvertidamente al tipo de declaración en lugar del tipo que contiene. Este error debía solucionarse debido a las necesidades de las propiedades virtuales y los metadatos de validación / modelo.

Tener modelos basados ​​en la interfaz no es algo que fomentemos (ni, dadas las limitaciones impuestas por la corrección de errores, podemos admitir de manera realista). Cambiar a clases base abstractas arreglaría el problema.


Gracias a Burcephal, cuya respuesta me indicó la dirección correcta.

Puede crear un MetaDataProvider que solucionará este problema, el código aquí se agrega al código en la clase base que verifica la propiedad en las interfaces implementadas de un modelo que es en sí mismo una interfaz.

public class MyMetadataProvider : EmptyModelMetadataProvider { public override ModelMetadata GetMetadataForProperty( Func<object> modelAccessor, Type containerType, string propertyName) { if (containerType == null) { throw new ArgumentNullException("containerType"); } if (String.IsNullOrEmpty(propertyName)) { throw new ArgumentException( "The property &apos;{0}&apos; cannot be null or empty", "propertyName"); } var property = GetTypeDescriptor(containerType) .GetProperties().Find(propertyName, true); if (property == null && containerType.IsInterface) { property = (from t in containerType.GetInterfaces() let p = GetTypeDescriptor(t).GetProperties() .Find(propertyName, true) where p != null select p ).FirstOrDefault(); } if (property == null) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, "The property {0}.{1} could not be found", containerType.FullName, propertyName)); } return GetMetadataForProperty(modelAccessor, containerType, property); } }

y como se indicó anteriormente, configure su proveedor global.asax Application_Start

ModelMetadataProviders.Current = new MyMetaDataProvider();


Ha habido un cambio / error de última hora en ASP.NET MVC 3 en System.Web.Mvc.ModelMetadata. FromLambdaExpression System.Web.Mvc.ModelMetadata. FromLambdaExpression método System.Web.Mvc.ModelMetadata. FromLambdaExpression que explica la excepción que está obteniendo:

ASP.NET MVC 2.0:

... case ExpressionType.MemberAccess: { MemberExpression body = (MemberExpression) expression.Body; propertyName = (body.Member is PropertyInfo) ? body.Member.Name : null; containerType = body.Member.DeclaringType; flag = true; break; } ...

ASP.NET MVC 3.0

... case ExpressionType.MemberAccess: { MemberExpression body = (MemberExpression) expression.Body; propertyName = (body.Member is PropertyInfo) ? body.Member.Name : null; containerType = body.Expression.Type; flag = true; break; } ...

Observe cómo a la variable containerType se le asigna un valor diferente. Por lo tanto, en su caso en ASP.NET MVC 2.0 se le asignó el valor de IOwned que es el tipo de declaración correcto de la propiedad AuthorId , mientras que en ASP.NET MVC 3.0 se asigna a IActivity y, posteriormente, cuando el marco intenta encontrar la propiedad choques

Esa es la causa. En lo que respecta a la resolución, esperaría alguna declaración oficial de Microsoft. No puedo encontrar ninguna información relevante sobre esto en el documento de Notas de la versión. ¿Es un error o alguna característica que necesita ser solucionada aquí?

Por ahora, puede usar el ayudante Html.Hidden("AuthorId") no tipificado con fuerza o especificar IOwned como tipo para su control (sé que ambos apestan).


Si estás interesado, he implementado un poco de trabajo en mi aplicación. Mientras revisaba el código fuente de MVC, descubrí que el método FromLambdaExpression que se menciona a continuación llamará al MetaDataProvider, que es un singleton reemplazable. Entonces podríamos simplemente implementar esa clase que realmente probará las interfaces internas heredadas si la primera no funciona. También subirá el árbol de las interfaces.

public class MyMetaDataProvider : EmptyModelMetadataProvider { public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) { try { return base.GetMetadataForProperty(modelAccessor, containerType, propertyName); } catch(Exception ex) { //Try to go up to type tree var types = containerType.GetInterfaces(); foreach (var container in types) { if (container.GetProperty(propertyName) != null) { try { return GetMetadataForProperty(modelAccessor, container, propertyName); } catch { //This interface did not work } } } //If nothing works, then throw the exception throw ex; } } }

y luego, solo puede forjar la implementación del MetaDataProvider en global.asax Application_Start ()

ModelMetadataProviders.Current = new MyMetaDataProvider();

No es el mejor código nunca, pero hace el trabajo.


Trate de usar encasillada. Funciona para mi proyecto aunque resharper lo destaca como redundante.

Para su código la solución sería

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IActivity>" %> <%: Html.HiddenFor(item => ((IOwned)item).AuthorId) %>