1

Dans un ASP projet .NET 1.1 (base VS 2017) J'essaie d'utiliser le ShortName attrubute de la propriété Display afin d'utiliser le DisplayFor HTML Helper:ASP.NET de base: ShortName dans l'attribut d'affichage (DataAnnotations)

[Display(Name="Project Name", ShortName="Name", Description="The name of the project")] 
public string Name { get; set; } 

Je lis the following answer qui fait l'affaire pour le Description. Malheureusement pour une raison que je ne comprends pas, cela ne fonctionne pas pour le ShortName.

Il y a le code que j'ai essayé, la première méthode semble OK, mais le second ne compile pas, donc je voudrais corriger:

using Microsoft.AspNetCore.Html; 
using Microsoft.AspNetCore.Mvc.Rendering; 
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; 
using System; 
using System.Linq; 
using System.ComponentModel.DataAnnotations; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace MyProject.Helpers 
{ 
    public static class HtmlExtensions 
    { 
     public static IHtmlContent DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) 
     { 
      if (html == null) throw new ArgumentNullException(nameof(html)); 
      if (expression == null) throw new ArgumentNullException(nameof(expression)); 

      var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider); 
      if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {ExpressionHelper.GetExpressionText(expression)}"); 
      //////// Description is OK 
      return new HtmlString(modelExplorer.Metadata.Description); 
     } 

     public static IHtmlContent ShortNameFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) 
     { 
      if (html == null) throw new ArgumentNullException(nameof(html)); 
      if (expression == null) throw new ArgumentNullException(nameof(expression)); 

      var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html., html.MetadataProvider); 
      if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {ExpressionHelper.GetExpressionText(expression)}"); 
      //////// ShortName DOES NOT EXIST !!!!!!!!!!!!!!!! 
      return new HtmlString(modelExplorer.Metadata.ShortName); 
     } 
    } 
} 

Plus que que, l'examen de la MS code of the DisplayNameFor

la signature de la méthode doit changer quelque chose comme ceci:

public static string DisplayShortNameFor<TModelItem, TResult>(
    this IHtmlHelper<IEnumerable<TModelItem>> htmlHelper, 
    Expression<Func<TModelItem, TResult>> expression)  

et non

public static IHtmlContent ShortNameFor<TModel, TValue>(
    this IHtmlHelper<TModel> html, 
    Expression<Func<TModel, TValue>> expression) 

Mise à jour

Pour l'ancienne signature J'ai essayé

public static string DisplayShortNameFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) 
{ 
    string shortNameValue = string.Empty; 
    var prop = expression.Body as MemberExpression; 
    if (prop != null) 
    { 
     var DisplayAttrib = prop.Member.GetCustomAttributes<DisplayAttribute>(false).FirstOrDefault(); 
     if (DisplayAttrib != null) 
      shortNameValue = DisplayAttrib.ShortName; 
    } 
    return shortNameValue; 
} 

mais en fait je ne peux pas courir parce que ne compile pas dans la vue, car est un IEnumerable

@using MyProject.Helpers 
@model IEnumerable<MyProject.Models.Record> <!--<<< IEnumerable to display a collection --> 

@Html.DisplayShortNameFor(model => model.Name) 

Je dois donc faire

// for my method shortname I need to use FirstOfDefault... 
@Html.DisplayShortNameFor(model => model.FirstOrDefault().Name) 

// but for ASP.NET DisplayName works 
@Html.DisplayNameFor(model => model.Date) 

Répondre

2

Pour obtenir la propriété ShortName à l'aide de cette méthode, vous devez extraire manuellement l'attribut Display car il ne fait pas partie des métadonnées par défaut. Par exemple, quelque chose comme cela fonctionnera:

var defaultMetadata = m as 
    Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata; 
if(defaultMetadata != null) 
{ 
    var displayAttribute = defaultMetadata.Attributes.Attributes 
     .OfType<DisplayAttribute>() 
     .FirstOrDefault(); 
    if(displayAttribute != null) 
    { 
     return displayAttribute.ShortName; 
    } 
} 
return m.DisplayName; 

Pour brancher que dans vos aides, je voudrais abstraire la méthode un peu comme il y a un code en double là-bas, de sorte que vous finiriez avec une méthode privée comme ceci:

private static IHtmlContent MetaDataFor<TModel, TValue>(this IHtmlHelper<TModel> html, 
    Expression<Func<TModel, TValue>> expression, 
    Func<ModelMetadata, string> property) 
{ 
    if (html == null) throw new ArgumentNullException(nameof(html)); 
    if (expression == null) throw new ArgumentNullException(nameof(expression)); 

    var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider); 
    if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {ExpressionHelper.GetExpressionText(expression)}"); 
    return new HtmlString(property(modelExplorer.Metadata)); 
} 

Et vos deux méthodes publiques comme celle-ci:

public static IHtmlContent DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) 
{ 
    return html.MetaDataFor(expression, m => m.Description); 
} 

public static IHtmlContent ShortNameFor<TModel, TValue>(this IHtmlHelper<TModel> html, 
    Expression<Func<TModel, TValue>> expression) 
{ 
    return html.MetaDataFor(expression, m => 
    { 
     var defaultMetadata = m as 
      Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata; 
     if(defaultMetadata != null) 
     { 
      var displayAttribute = defaultMetadata.Attributes.Attributes 
       .OfType<DisplayAttribute>() 
       .FirstOrDefault(); 
      if(displayAttribute != null) 
      { 
       return displayAttribute.ShortName; 
      } 
     } 
     //Return a default value if the property doesn't have a DisplayAttribute 
     return m.DisplayName; 
    }); 
} 
+0

est-il possible de mettre à jour la signature afin de le faire fonctionner avec une collection? comme 'chaîne statique publique DisplayShortNameFor ( ce IHtmlHelper > htmlHelper, expression > expression)', car comme décrit dans OP la vue ne digère pas la collection – Serge

+0

N'en avez pas besoin, vous pouvez simplement l'appeler dans la vue comme ceci: '@ Html.ShortNameFor (m => m.First(). SomeProperty)' – DavidG

+0

oui, mais où il n'y a pas d'articles, je vais avoir des erreurs ... et ce un plus d'appel à la collection, je veux dire, le code ASP.NET réel est avec Collection à l'intérieur, pas un membre – Serge