ASP.NET MVC视图中用IList/List替代IEnumerable报错如何解决
报错原因
这个报错本质是视图编译阶段的表达式类型不匹配,核心原因和ASP.NET MVC内置DisplayNameFor方法的重载设计、C#泛型的协变规则有关:
- MVC为
DisplayNameFor提供了两个常用重载:- 通用重载:
DisplayNameFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) - 针对集合的专属重载:
DisplayNameFor<TModel, TValue>(this HtmlHelper<IEnumerable<TModel>> html, Expression<Func<TModel, TValue>> expression)
- 通用重载:
- 当你视图声明模型为
IEnumerable<BankAccount>时,当前视图的HtmlHelper泛型类型为HtmlHelper<IEnumerable<BankAccount>>,可以直接匹配到专属重载,表达式里的model会被识别为集合的元素类型BankAccount,可以正常访问Name、Amount等属性。 - 当你视图声明模型为
IList<BankAccount>/List<BankAccount>时,当前视图的HtmlHelper泛型类型为HtmlHelper<IList<BankAccount>>:- 因为
HtmlHelper是泛型类,C#中类的泛型参数不支持协变,所以HtmlHelper<IList<BankAccount>>无法隐式转换为HtmlHelper<IEnumerable<BankAccount>>,也就匹配不到专属重载。 - 只能匹配到通用重载,这时候表达式里的
model会被识别为IList<BankAccount>集合本身,集合类型没有Name、Amount属性,编译时就会抛出异常,触发服务端报错。
- 因为
正常使用IList/List的方法
你只需要调整视图里DisplayNameFor的写法即可,不需要修改控制器逻辑:
把原来直接访问属性的写法
@Html.DisplayNameFor(model => model.Name) @Html.DisplayNameFor(model => model.Amount)
改为通过索引器访问集合元素的写法:
@Html.DisplayNameFor(model => model[0].Name) @Html.DisplayNameFor(model => model[0].Amount)
DisplayNameFor只会解析表达式树提取属性的显示名称,不会实际访问集合的第0个元素,就算你返回的集合是空的也不会抛出空引用异常,完全可以正常使用。
如果你确实想保留原来的写法,也可以自己扩展实现针对IList<T>的DisplayNameFor重载,不过对于绝大多数场景,改索引器的写法成本最低也最容易维护。
内容的提问来源于stack exchange,提问作者user17291846
相关产品推荐
相关产品推荐

