如何实现通用ViewModel在View Component中展示动态表格
无反射实现动态泛型表格视图模型方案求助
初始泛型设计
我原本想通过泛型结构实现一套可动态获取行对象属性的表格组件,定义如下:
public class Column<T> { // 列标题 public string Name { get; set; } // 属性映射表达式 public Expression<Func<T, string>> ValueMapper { get; set; } } public class Row<T> { T Data { get; set; } } public class Table<T> { IEnumerable<Row<T>> Rows { get; set; } IEnumerable<Column<T>> Columns { get; set; } }
设计目标是把Table<T>作为View Component的ViewModel,实现任意类型数据的动态表格展示,不用为每种数据类型单独定义表格。
遇到的问题
我期望在Razor视图中用Table<IData>作为模型(约束T实现IData接口),代码如下:
@model Table<IData> <table> <thead> <tr> @foreach(var col in Model.Columns) { <td>@col.Name</td> } </tr> </thead> <tbody> @foreach(var row in Model.Rows) { <tr> @foreach(var col in Model.Columns) { <td>@col.ValueMapper.Compile().Invoke(row.Data)</td> } </tr> } </tbody> </table>
但实际遇到了无法解决的类型转换问题:具体的Table<T>实例无法隐式转换为Table<IData>传递给视图。尝试用协变解决,但存在两个阻碍:
Row<T>的Data属性同时有getter和setter,属于不变类型,无法声明协变接口Expression<Func<T, string>>中的T是输入参数,且该属性有getter和setter,协变无法生效
当前反射实现方案
无奈之下,我移除了泛型,改用object类型结合反射实现了功能,代码如下:
后端代码
public class Table { public IEnumerable<Column> Columns {get; set;} public IEnumerable<Row> Rows {get; set;} } public class Row { public object Data {get; set;} public string GetValue(string propertyName) { var toReturn = ""; var pi = Data.GetType().GetProperty(propertyName); var value = pi.GetValue(Data); if(value == null) return toReturn; if(value is DateTime dt) toReturn = dt.ToShortDateString(); else toReturn = value.ToString(); return toReturn; } } public class Column { public string Name {get; set;} public string PropertyName {get; set;} }
视图调用代码
@foreach(var row in Model.Rows) { <tr> @foreach(var col in Model.Columns) { <td>@row.GetValue(col.PropertyName)</td> } </tr> }
但反射会带来性能开销,我希望找到无需依赖反射的更优实现方案,恳请提供帮助。
内容的提问来源于stack exchange,提问作者Zach Painter
相关产品推荐
相关产品推荐

