Question

J'écris une application utilisant MVC2 DataAnnotations. J'ai un modèle suivant:

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

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

Je veux créer un modèle d'affichage personnalisé pour Bar. J'ai créé modèle suivant:

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

Maintenant, mon problème est que à l'intérieur modèle pour Bar Je veux accéder à une autre propriété de mon modèle . Je ne veux pas créer un modèle distinct pour FooModel parce que je vais devoir coder en dur toutes les autres propriétés de FooModel.

Après une brève enquête avec un débogueur je peux voir que:

  1. this.ViewData.ModelMetadata.ContainerType est FooModel (comme prévu)
  2. this.ViewData.TemplateInfo a biens non publics VisitedObjects (De type System.Collections.Generic.HashSet<object>) qui contient deux éléments: FooModel et DateTime?.

Comment puis-je avoir accès à mon FooModel? Je ne veux pas pirater mon chemin en utilisant la réflexion.

Mise à jour:

Je l'ai accepté la réponse de mootinator car il me semble que la meilleure solution qui permet à la sécurité de type. J'ai aussi upvoted la réponse de Tx3, comme la réponse de mootinator construit sur elle. Néanmoins, je pense qu'il devrait y avoir un meilleur support sous forme MVC dans ce genre de scénarios, que je crois tout à fait commun dans le monde réel, mais manque d'exemples d'applications.

Était-ce utile?

La solution

Désolé si cette suggestion semble stupide, je ne l'ai pas essayé, mais ne pouvais pas vous faire ce que Tx3 suggéré sans avoir à créer un tas de nouvelles classes en définissant une classe générique pour faire référence à tout type de parent que vous voulez?

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

}

Vous pouvez étendre cette encapsuler tout ancien type avec un <Parent, Child> typé générique, même.

Ce serait aussi vous donner l'avantage que votre modèle fortement typé serait pour

Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>> donc vous ne devez nommer explicitement le modèle à utiliser partout. Ceci est plus comment les choses sont censées fonctionner.

Autres conseils

Peut-être que vous pouvez créer une nouvelle classe, disons que UserDateTime et contiendrait nullable DateTime et reste de l'information dont vous avez besoin. Ensuite, vous utiliser le modèle d'affichage personnalisé pour UserDateTime et obtenir l'accès à l'information dont vous avez besoin.

Je me rends compte que vous cherchez peut-être un autre type de solution.

Je pense que vous pouvez être mieux extraire cette fonctionnalité à un appel HtmlHelper de la vue parent.

Quelque chose comme RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression) serait probablement faire le travail.

Dans le cas contraire, vous devrez faire quelque chose comme ce qui a suggéré Tx3. J'upvoted sa réponse, mais posté comme alternative.

Ne pourriez-vous utiliser l'objet dictionnaire ViewData dans le contrôleur, puis saisir que dans le ViewUserControl? Il ne serait pas fortement typé, mais ... vous pouvez écrire une aide pour ne rien faire si elle est vide, et un lien pour dire la page d'historique de connexion par exemple si elle avait une valeur.

Il semblerait que quelque part entre MVC 5.0 et 5.2.2 une propriété « Container » a été ajouté à la classe ModelMetadata.

Cependant, parce que toutes les méthodes d'un fournisseur responsable de la création de métadonnées (GetMetadataForProperty, créer, etc.) n'ont pas le contenant dans leur signature, la propriété du conteneur est attribué que dans certains cas (GetMetadataForProperties et GetMetadataFromProvider selon le code réfléchi) et dans mon cas était généralement nulle.

Alors, ce que je fini par faire est redéfinissant la GetMetadataForProperty dans un nouveau fournisseur de métadonnées et la mise en pratique:

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

Je sais que c'est la réflexion, mais il est assez succinct. Il semblerait que MS corrige ce oversite alors peut-être il sera possible de remplacer le code de réflexion dans l'avenir.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top