问题
In an ASP .NET Core 1.1 project (VS 2017) I try to use the ShortName
attrubute of the Display
property in order to use the DisplayFor
HTML Helper:
[Display(Name="Project Name", ShortName="Name", Description="The name of the project")]
public string Name { get; set; }
I read the following answer that does the trick for the Description
. Unfortunately for a reason I don't understand, this doesn't work for the ShortName
.
There is the code I tried, the first method seems OK, but the second does not compile, so I would like to fix it:
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);
}
}
}
More that than, reviewing the MS code of the DisplayNameFor
the signature of the method should change for something like this:
public static string DisplayShortNameFor<TModelItem, TResult>(
this IHtmlHelper<IEnumerable<TModelItem>> htmlHelper,
Expression<Func<TModelItem, TResult>> expression)
and not
public static IHtmlContent ShortNameFor<TModel, TValue>(
this IHtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression)
Update
For the old signature I tried
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;
}
but actually I can't run it because does not compile in the View, because is a IEnumerable
@using MyProject.Helpers
@model IEnumerable<MyProject.Models.Record> <!--<<< IEnumerable to display a collection -->
@Html.DisplayShortNameFor(model => model.Name)
So I need to do
// 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)
回答1:
To get the ShortName
property using this method, you need to extract the Display
attribute manually because it's not part of the default metadata. For example, something like this will work:
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;
To plug that into your helpers, I would abstract away the method slightly as there's some duplicate code in there, so you would end up with a private method like this:
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));
}
And your two public methods like this:
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;
});
}
来源:https://stackoverflow.com/questions/45234670/asp-net-core-shortname-in-the-display-attribute-dataannotations