Domanda

Sto scrivendo un app MVC2 utilizzando DataAnnotations. Ho un seguente modello:

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

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

Voglio creare un modello di visualizzazione personalizzato per la barra. Ho creato seguente configurazione:

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

Ora, il mio problema è che modello all'interno per Bar voglio accedere a un altro immobile da mio modello . Non voglio creare un modello separato per FooModel perché di quanto dovrò hardcode tutte le altre proprietà FooModel.

Dopo una breve indagine con un debugger vedo che:

  1. this.ViewData.ModelMetadata.ContainerType è FooModel (come previsto)
  2. this.ViewData.TemplateInfo ha un VisitedObjects proprietà non pubblica (Di tipo System.Collections.Generic.HashSet<object>) che contiene due elementi: FooModel e DateTime?.

Come posso ottenere l'accesso al mio FooModel? Io non voglio incidere il mio modo per aggirare utilizzando la riflessione.

Aggiornamento:

Ho accettato la risposta di mootinator come sembra a me come la migliore soluzione che permette tipo di sicurezza. Ho anche upvoted risposta di Tx3, come la risposta di mootinator si basa su di essa. Tuttavia, penso che ci dovrebbe essere una forma migliore supporto MVC in questo tipo di scenari, che a mio avviso sono abbastanza comuni nel mondo reale, ma manca da applicazioni di esempio.

È stato utile?

Soluzione

Scusate se questo suggerimento sembra stupido, io non l'ho provato, ma non ha potuto fare quello che ha suggerito Tx3 senza dover creare un gruppo di nuove classi per la definizione di una classe generica di riferimento qualsiasi tipo di genitore che si desidera?

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

}

Si potrebbe espandersi che incapsulare qualsiasi vecchio tipo con un <Parent, Child> digitato generica, anche.

Che sarebbe anche darvi il vantaggio che il modello fortemente tipizzato sarebbe per

Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>> così di non dover nome esplicitamente il modello da utilizzare ovunque. Questo è più come le cose sono destinate al lavoro.

Altri suggerimenti

Forse si potrebbe creare nuova classe, diciamo UserDateTime e sarebbe contenere nullable DateTime e resto delle informazioni che vi serve. Quindi si usa maschera di visualizzazione personalizzato per UserDateTime e ottenere l'accesso alle informazioni che desiderate.

Mi rendo conto che si potrebbe essere alla ricerca di altro tipo di soluzione.

Penso che si può essere meglio l'estrazione di questa funzionalità a una chiamata HtmlHelper dalla Capogruppo View.

Qualcosa di simile RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression) probabilmente fare il lavoro.

In caso contrario, si dovrà fare qualcosa di simile a ciò che Tx3 suggerito. I upvoted la sua risposta, ma postato questo come alternativa.

Non potresti utilizzare l'oggetto dizionario Viewdata nel controller e poi prendere che nel ViewUserControl? Non sarebbe fortemente tipizzato, ma ... si potrebbe scrivere un aiutante di non fare nulla se è vuota, e il link a dire la pagina di storia esempio di login se avesse un valore.

Sembrerebbe che da qualche parte tra MVC 5.0 e 5.2.2 una proprietà "Container" è stato aggiunto alla classe ModelMetadata.

Tuttavia, perché tutti i metodi in un fornitore di responsabili per la creazione di metadati (GetMetadataForProperty, Create, ecc) non hanno container nel loro firma, la proprietà contenitore viene assegnata solo in alcuni casi (GetMetadataForProperties e GetMetadataFromProvider secondo il codice riflessa) e nel mio caso era solito nulla.

Quindi quello che ho finito per fare è l'override del GetMetadataForProperty in un nuovo fornitore di metadati e impostando 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;
}

So che questo è riflesso, ma è piuttosto succinta. Sembrerebbe che MS sta correggendo questo oversite quindi forse sarà possibile sostituire il codice di riflessione per il futuro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top