Pregunta

Estoy escribiendo una aplicación MVC2 usando DataAnnotations. Tengo un modelo siguiente:

public class FooModel 
{
    [ScaffoldColumn("false")]
    public long FooId { get; set; }

    [UIHint("BarTemplate")]
    public DateTime? Bar { get; set;}
}

Quiero crear una plantilla de visualización personalizado para Bar. He creado siguiente plantilla:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>

<div class="display-label">
    <span><%: Html.LabelForModel() %></span>
</div>
<div class="display-field">
    <span><%: Html.DisplayForModel()%></span>
    <%: Html.ActionLink("Some link", "Action", new { id = ??FooId?? }) %>
</div>

Ahora, mi problema es que plantilla interior para Bar Quiero acceder a la otra propiedad de mi modelo . No quiero crear una plantilla separada para FooModel ya que voy a tener que codificar todas las demás propiedades FooModel.

Después de una investigación breve con un depurador que se puede ver que:

  1. this.ViewData.ModelMetadata.ContainerType es FooModel (como se esperaba)
  2. this.ViewData.TemplateInfo tiene una VisitedObjects propiedad no pública (De tipo System.Collections.Generic.HashSet<object>) que contiene dos elementos: FooModel y DateTime?.

¿Cómo puedo acceder a mi FooModel? No quiero cortar mi camino por el uso de Reflexión.

Actualización:

He aceptado la respuesta de mootinator como parece a mí como la mejor solución que permite seguridad de tipos. También he upvoted respuesta de Tx3, como la respuesta de mootinator se basa en ella. Sin embargo, creo que debe haber una mejor forma de apoyo MVC en ese tipo de escenarios, que creo que son bastante comunes en el mundo real, pero que falta de aplicaciones de ejemplo.

¿Fue útil?

Solución

Lo siento si esta sugerencia parece tonto, yo no lo he probado, pero no podía hacer lo que sugirió Tx3 sin tener que crear un montón de nuevas clases mediante la definición de una clase genérica de referencia de cualquier tipo de padre quiere

    public class FooModel 
    {
        [ScaffoldColumn("false")]
        public long FooId { get; set; }

        [UIHint("BarTemplate")]
        public ParentedDateTime<FooModel> Bar { get; set;}

        public FooModel()
        {
            Bar = new ParentedDateTime<FooModel>(this);
        }
    }


    public class ParentedDateTime<T>
    {
        public T Parent {get; set;}
        public DateTime? Babar {get; set; }

        public ParentedDateTime(T parent)
        {
            Parent = parent;
        }

}

Se podría ampliar esa encapsular cualquier tipo de edad con una <Parent, Child> mecanografiado genérica, incluso.

Eso sería también le dan la ventaja de que su plantilla inflexible sería para

Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>> por lo que no tendría para nombrar explícitamente qué plantilla utilizar en cualquier lugar. Esto es más de cómo las cosas están destinadas a trabajar.

Otros consejos

Tal vez se podría crear nueva clase, digamos UserDateTime y que contendría anulable DateTime y el resto de la información que necesita. De allí tendría que utilizar la plantilla de visualización personalizado para UserDateTime y obtener acceso a la información que necesita.

Me doy cuenta de que podría estar buscando otro tipo de solución.

creo que puede ser mejor de extracción de esta funcionalidad a una llamada HtmlHelper desde la perspectiva de los padres.

Algo así como RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression) probablemente hacer el trabajo.

De lo contrario, tendrá que hacer algo parecido a lo que sugirió Tx3. Me upvoted su respuesta, pero publicado esto como una alternativa.

No se pudo utilizar el objeto de diccionario ViewData en el controlador y luego agarre que en el ViewUserControl? No sería inflexible, pero ... podría escribir un ayudante para no hacer nada si está vacío, y el enlace a la página del historial decir ejemplo de inicio de sesión si tuviera un valor.

Al parecer, en algún lugar entre MVC 5.0 y 5.2.2 se añadió una propiedad "contenedor" a la clase ModelMetadata.

Sin embargo, debido a que todos los métodos en un proveedor responsables de la creación de metadatos (GetMetadataForProperty, Crear etc) no tienen contenedores en su firma, la propiedad de contenedores se asigna sólo en ciertos casos (GetMetadataForProperties y GetMetadataFromProvider acuerdo con el código reflejado) y en mi caso era generalmente nula.

Así que lo que terminé haciendo está anulando el GetMetadataForProperty en un nuevo proveedor de metadatos y se establece allí:

public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
  var propMetaData = base.GetMetadataForProperty(modelAccessor, containerType, propertyName);
  Object container = modelAccessor.Target.GetType().GetField("container").GetValue(modelAccessor.Target);
  propMetaData.Container = container;
  return propMetaData;
}

Sé que esto es reflejo pero es bastante conciso. Parecería que la EM es la corrección de este sobresitio así que tal vez será posible sustituir el código de la reflexión en el futuro.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top