简体   繁体   English

Html.DisplayNameFor List vs IEnumerable in Razor

[英]Html.DisplayNameFor List vs IEnumerable in Razor

I am implementing the PaginatedList from the tutorial.我正在实现教程中的PaginatedList

It works.有用。 My issue if that if I defined the @model as an IEnumerable in my Razor page I can do this:如果我在我的 Razor 页面@model定义为IEnumerable我可以这样做:

@model IEnumerable<CustomerDisplayViewModel>
@Html.DisplayNameFor(model => model.LastName)

If I define @model as List , the @Html.DisplayNameFor helper works differently, and I have to call it like this:如果我将@model定义为List ,则@Html.DisplayNameFor助手的工作方式不同,我必须这样称呼它:

@model List<CustomerDisplayViewModel>
@Html.DisplayNameFor(model => model.First().LastName)

Difference being that in the first call the expression cast model to be CustomerDisplayViewModel and in the second it is a List<CustomerDisplayViewModel> .不同之处在于,在第一次调用中,表达式将 model 转换为CustomerDisplayViewModel而在第二次调用中它是List<CustomerDisplayViewModel>

The issue is caused by the compiler prefering to cast the call to该问题是由编译器倾向于将调用转换为

Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<TModel> 
string DisplayNameFor<TResult>(Expression<Func<TModel, TResult>> expression);

Instead of代替

Microsoft.AspNetCore.Mvc.Rendering.HtmlHelperDisplayNameExtensions
public static string DisplayNameFor<TModelItem, TResult>(
this IHtmlHelper<IEnumerable<TModelItem>> htmlHelper, Expression<Func<TModelItem, TResult>> expression);

I know I can use my workaround ( @Html.DisplayNameFor( model => model.First().LastName) ) but it doesn't feel correct.我知道我可以使用我的解决方法( @Html.DisplayNameFor( model => model.First().LastName) ),但感觉不正确。

Is there a way to cast the call or maybe generating my own extension that calls the IEnumerable extension (I would prefer to not create an extension from scratch, this call should work exactly as the IEnumerable).有没有办法进行调用或者生成我自己的调用 IEnumerable 扩展的扩展(我不希望从头开始创建扩展,这个调用应该与 IEnumerable 完全一样)。 I can create the extension method with List<>, however, I haven't been able to cast it.我可以使用 List<> 创建扩展方法,但是我无法转换它。

 public static string DisplayNameFor<TModelItem, TResult>
            (this Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<List<TModelItem>> htmlHelper,
            Expression<Func<TModelItem, TResult>> expression)
        {
            var castHelper = --- somehow cast helper to IHtmlHelper<IEnumerable<TModelItem>
            return castHelper.DisplayNameFor(expression);
        }

Thanks.谢谢。

This is happening because the generic parameter TModel of IHtmlHelper is not covariant .发生这种情况是因为IHtmlHelper的泛型参数TModel不是covariant Basically, you cannot do this:基本上,你不能这样做:

IHtmlHelper<List<CustomerDisplayViewModel>> helperList = new HtmlHelper<List<CustomerDisplayViewModel>(...);

IHtmlHelper<IEnumerable<CustomerDisplayViewModel>> helperIEnumerable = helperList;
// the above line is an error

However, you can do that with IEnumerable<T> :但是,您可以使用IEnumerable<T>来做到这一点:

IEnumerable<int> intList = new List<int>();
IEnumerable<object> objList = intList; // no error

This is because IEnumerable is declared like this:这是因为IEnumerable是这样声明的:

public interface IEnumerable<out T> : IEnumerable { .. }

Note the out keyword which specifies that the generic parameter T is covariant.注意out关键字,它指定泛型参数T是协变的。 Had IHtmlHelper<TModel> been declared like this in the framework:如果IHtmlHelper<TModel>在框架中这样声明:

interface IHtmlHelper<out TModel> { .. }

your code would have worked.你的代码会起作用的。


Despite this, you can still use Html.DisplayNameForInnerType() to get the display name(ASP.NET Core only) in such cases:尽管如此,在这种情况下,您仍然可以使用Html.DisplayNameForInnerType()来获取显示名称(仅限 ASP.NET Core):

@model PaginatedList<CustomerDisplayViewModel>
...
@Html.DisplayNameForInnerType((CustomerDisplayViewModel c) => c.LastName)

Note that you have to explicitly specify the type of the lambda expression parameter.( CustomerDisplayViewModel c ).请注意,您必须明确指定 lambda 表达式参数的类型。( CustomerDisplayViewModel c )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM