This question was asked in the past and the answer here https://stackoverflow.com/a/45236544/3074765 has been a life saver for me. The problem is that when I migrated to .Net Core 3.0, it broke. The reason can be found here https://github.com/aspnet/AspNetCore/issues/8678 . In a nutshell, Microsoft had made some functions public that were in a class that was labeled internal. Fixing this removed access to ExpressionMetadataProvider. Consider the following HTML Helper
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
public static class MvcHtmlHelpers
{
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));
}
public static IHtmlContent DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return html.MetaDataFor(expression, m => m.Description ?? m.Name);
}
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 ?? m.DisplayName ?? m.Name;
}
}
//Return a default value if the property doesn't have a DisplayAttribute
return m.DisplayName ?? m.Name;
});
}
}
When you follow the steps to migrate to .Net Core 3.0, ExpressionMetadataProvider and ExpressionHelper can no longer be resolved.
In .Net Core 3.0, Microsoft provided a way to get to the same MetaData using Dependecy Injection and a service called ModelExpressionProvider. You can get access to the service via the HttpContext found in the IHtmlHelper. Just replace the using to be:
using Microsoft.AspNetCore.Mvc.ViewFeatures;
Which removes the .Internal, and replace the MetaDataFor function with:
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));
ModelExpressionProvider modelExpressionProvider = (ModelExpressionProvider)html.ViewContext.HttpContext.RequestServices.GetService(typeof(IModelExpressionProvider));
var modelExplorer = modelExpressionProvider.CreateModelExpression(html.ViewData, expression);
if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {modelExpressionProvider.GetExpressionText(expression)}");
return new HtmlString(property(modelExplorer.Metadata));
}
The other two extension functions will work fine once MetaDataFor is fixed to retrieve what they need.
Cheers!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With