2012-09-17 5 views
5

J'ai un modèle comme ci-dessous:Comment accéder C# attribut modèle au sein EditorFor

public class CreateStockcheckJobModel 
{ 
    [Engineer(true)] 
    public EngineerModel Engineer { get; set; } 
} 

Je rend la propriété Engineer dans un View<CreateStockcheckJobModel> en utilisant Html.EditorFor(m => m.Engineer, "EngineerEditor").

Comment puis-je accéder à la valeur de l'attribut Engineer (dans ce cas, true) dans le code de ma vue partielle (EngineerEditor.ascx)?


Ci-dessous est mon code éditeur

<%@ Control Language="C#" Inherits="ViewUserControl<EngineerModel>" %> 
<% if (PropertyImRenderingHasAttributeWithTrueBooleanValue) // What goes here? 
    { %> 
<p>Render one thing</p> 
<% } 
    else 
    { %> 
<p>Render another thing</p> 
<% } %> 

Je suis au courant de la réflexion, mais je ne suis pas sûr comment l'utiliser comme l'attribut est pas ajouté à la classe EngineerModel il est ajouté à la Engineer propriété de la classe CreateStockcheckJobModel. Si je pouvais obtenir le PropertyInfo que je rends du code de l'éditeur, je serais trié, mais je ne sais pas comment obtenir cette information. Si je descends la voie d'énumérer toutes les propriétés de la classe CreateStockcheckJobModel alors je vais obtenir des questions si j'ai plus d'un EngineerModel propriété (on peut avoir l'attribut avec True, un autre pourrait avoir False).

+0

Que voulez-vous obtenir? Il me semble, que vous faites mal d'atteindre votre objectif –

+0

Il s'agit essentiellement de rendre une liste d'ingénieurs possibles à choisir. Je veux que l'attribut contrôle la liste des ingénieurs disponibles. –

+0

Je pensais que c'était une approche raisonnable car il semble être ce que l'attribut 'DisplayFormat' est en train de faire (ie changer la façon dont un contrôle/éditeur est rendu en fonction des données de l'attribut) –

Répondre

7

Cela pourrait se faire facilement dans ASP.NET MVC 3, puis en mettant en œuvre l'interface IMetadataAware sur votre commande EngineerAttribute:

public class EngineerAttribute : Attribute, IMetadataAware 
{ 
    public EngineerAttribute(bool isFoo) 
    { 
     IsFoo = isFoo; 
    } 

    public bool IsFoo { get; private set; } 

    public void OnMetadataCreated(ModelMetadata metadata) 
    { 
     metadata.AdditionalValues["IsFoo"] = IsFoo; 
    } 
} 

puis à l'intérieur du modèle:

<%@ Control Language="C#" Inherits="ViewUserControl<EngineerModel>" %> 
<% 
    var isFoo = (bool)ViewData.ModelMetadata.AdditionalValues["IsFoo"]; 
%> 
<% if (isFoo) { %> 
    <p>Render one thing</p> 
<% } else { %> 
    <p>Render another thing</p> 
<% } %> 

Malheureusement, cette interface n'existe pas dans ASP.NET MVC 2.Pour obtenir les mêmes fonctionnalités que vous pouvez écrire un fournisseur de métadonnées personnalisées:

public class MyMetadataProvider : DataAnnotationsModelMetadataProvider 
{ 
    protected override ModelMetadata CreateMetadata(
     IEnumerable<Attribute> attributes, 
     Type containerType, 
     Func<object> modelAccessor, 
     Type modelType, 
     string propertyName 
    ) 
    { 
     var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 
     var engineer = attributes.OfType<EngineerAttribute>().FirstOrDefault(); 
     if (engineer != null) 
     { 
      metadata.AdditionalValues["IsFoo"] = engineer.IsFoo; 
     } 
     return metadata; 
    } 
} 

que vous inscrirez dans votre Application_Start afin de remplacer la valeur par défaut un:

ModelMetadataProviders.Current = new MyMetadataProvider(); 

Et vous pouvez maintenant accéder à ces métadonnées votre modèle de la même manière que je l'ai montré plus tôt, en utilisant ViewData.ModelMetadata.AdditionalValues["IsFoo"]. De toute évidence, vous pouvez placer un objet arbitrairement complexe à l'intérieur de la propriété AdditionalValues, et pas seulement des booléens. Vous pouvez également trouver le following article utile sur les métadonnées.

1

NOTE: Vous trouverez ci-dessous une façon de procéder, mais l'utilisation de IMetadataAware comme décrit dans la réponse acceptée est bien meilleure. Il est généralement préférable d'éviter la réflexion si vous le pouvez.

Vous ne pouvez le faire que par réflexion. Voici un good example sur la façon de le faire pour les attributs de méthode.

Exemple de code

pour obtenir la valeur de EngineerAttribute sur la propriété Engineer:

PropertyInfo pi = Model.GetType().GetProperty("Engineer"); 
EngineerAttribute a = 
    System.Attribute.GetCustomAttribute(pi, typeof(EngineerAttribute)); 

Exemple de code pour obtenir toutes les propriétés qui ont l'attribut [Engineer(true)]:

var t = Model.GetType(); 
var engineerProperties = 
    from p in t.GetProperties() 
    let ca = System.Attribute.GetCustomAttribute(p, typeof(EngineerAttribute)) 
    where ca != null && ca.BooleanProperty == true 
    select p; 

Et quand vous avez ces propriétés, vous pouvez utiliser une surcharge de EditorFor qui vous permet de spécifier des données d'affichage supplémentaires, que vous pouvez ensuite utiliser dans votre sous-vue:

Html.EditorFor(m => m.Engineer, new { IsEngineer = isEngineer }); 
+0

Je sais comment fonctionne la réflexion, mais je suis J'ai du mal à trouver quelle propriété obtenir l'attribut car dans l'éditeur j'ai seulement le type 'EngineerModel' pas le type englobant et la propriété (ie l'attribut n'est pas sur le' EngineerModel' c'est sur une propriété qui se trouve être un 'EngineerModel'). –

+0

Compte tenu de votre modification, mon problème est que dans l'éditeur le modèle est de type 'EngineerModel' et l'attribut n'est pas sur le modèle, c'est sur la propriété du modèle qui l'entoure. Évidemment, le nom de la propriété pourrait changer et il pourrait y avoir plusieurs propriétés avec le type 'EngineerModel' (donc je ne peux pas simplement obtenir le type de modèle englobant et trouver la propriété de type' EngineerModel'). –

+0

Je viens de lire les commentaires sur la question et ce que vous voulez réellement est de lister toutes les propriétés qui ont '[Engineer (true)]'? –

Questions connexes