Pergunta

Estou escrevendo um aplicativo MVC2 usando o DataNotações. Eu tenho um modelo seguinte:

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

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

Eu quero criar um modelo de exibição personalizado para barra. Eu criei o seguinte modelo:

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

Agora, meu problema é que modelo interno para bar, quero acessar outra propriedade do meu modelo. Não quero criar um modelo separado para o Foomodel, porque terei que codificar todas as outras propriedades da Foomodel.

Depois de uma breve investigação com um depurador, posso ver que:

  1. this.ViewData.ModelMetadata.ContainerTypeé FooModel (como esperado)
  2. this.ViewData.TemplateInfo tem uma propriedade não pública VisitedObjects(do tipoSystem.Collections.Generic.HashSet<object>) que contém dois elementos:FooModel e DateTime?.

Como posso ter acesso ao meu foomodel? Não quero invadir meu caminho usando a reflexão.

Atualizar:

Aceitei a resposta do Mootinator, pois me parece a melhor solução que permite a segurança do tipo. Também vivenciei a resposta do TX3, à medida que a resposta do Mootinator se baseia nela. No entanto, acho que deve haver um melhor suporte de suporte MVC nesses tipos de cenários, que acredito ser bastante comuns no mundo real, mas ausentes nos aplicativos de amostra.

Foi útil?

Solução

Desculpe se essa sugestão parece idiota, eu não tentei, mas você não poderia fazer o que o TX3 sugeriu sem ter que criar um monte de novas classes, definindo uma classe genérica para fazer referência a qualquer tipo de pai que você queira?

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

}

Você pode expandi -lo para encapsular qualquer tipo antigo com um <Parent, Child> digitado genérico, até.

Isso também daria o benefício que seu modelo fortemente digitado seria para

Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>> Portanto, você não precisaria explicar o nome de qual modelo usar em qualquer lugar. É mais assim que as coisas pretendem funcionar.

Outras dicas

Talvez você possa criar uma nova classe, digamos que o UserDeTime e ela contenha DateTime Nullable e restante das informações necessárias. Em seguida, você usaria o modelo de exibição personalizado para o UserDeTime e obteria acesso às informações necessárias.

Percebo que você pode estar procurando outro tipo de solução.

Eu acho que você pode estar melhor extraindo essa funcionalidade para uma chamada htmlhelper da visualização dos pais.

Algo como RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression) provavelmente faria o trabalho.

Caso contrário, você terá que fazer algo como o que o TX3 sugeriu. Eu votei sua resposta, mas publiquei isso como uma alternativa.

Você não poderia usar o objeto Dicionário ViewData no controlador e depois pegá -lo no ViewUserControl? Não seria fortemente digitado, mas ... você poderia escrever um ajudante para não fazer nada se estiver vazio e vincular para dizer o exemplo da página de histórico de login se tivesse um valor.

Parece que em algum lugar entre o MVC 5.0 e 5.2.2 uma propriedade "contêiner" foi adicionada à classe ModelMetadata.

No entanto, como todos os métodos de um provedor responsável pela criação de metadados (getMetAdataForProperty, Create etc) não possuem contêiner em sua assinatura, a propriedade do contêiner é atribuída apenas em determinados casos (getMetAdataForProperties e getMeTadAfromprovider de acordo com o código refletido) e em meu caso era geralmente nulo.

Então, o que acabei fazendo é substituir o getMetadataForProperty em um novo provedor de metadados e configurá -lo lá:

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

Eu sei que isso é reflexão, mas é bastante sucinto. Parece que a MS está corrigindo esse estrangeiro, então talvez seja possível substituir o código de reflexão no futuro.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top