MVC EditorFor与泛型使用问题:模板未渲染的解决咨询
哥们,你说的没错——ASP.NET MVC里的EditorFor方法默认确实不直接支持泛型模型的编辑器模板,这就是为啥你看到的是泛型类型的全名,而不是渲染好的模板内容。毕竟MVC默认找模板的时候,是按类型的「完整名称」来匹配的,泛型类型的全名带一堆泛型参数标记(比如MyGenericModel1[System.Int32]),你肯定没创建对应这个怪名字的模板,所以它就直接调用ToString()输出类型名了。
不过别慌,咱有几个实用的解决办法,看你场景选就行:
办法1:用非泛型包装类「套一层」
这是最直观的办法,把你的泛型模型包在一个普通的非泛类里,然后针对这个包装类写编辑器模板。
举个例子:
第一步:定义包装类
// 你的原始泛型模型 public class MyGenericModel<T> { public T Value { get; set; } public string FieldLabel { get; set; } } // 包装类 public class GenericEditorWrapper<T> { public MyGenericModel<T> InnerModel { get; set; } }
第二步:创建包装类的编辑器模板
在EditorTemplates文件夹下新建GenericEditorWrapper.cshtml:
@model GenericEditorWrapper<dynamic> <div class="form-group"> <label class="control-label">@Model.InnerModel.FieldLabel</label> @* 这里可以根据Value的类型自动渲染对应的编辑器 *@ @Html.EditorFor(m => m.InnerModel.Value, new { htmlAttributes = new { @class = "form-control" } }) </div>
第三步:在视图里使用
要么把ViewModel里的属性改成包装类类型:
public class MyPageViewModel { public GenericEditorWrapper<int> AgeInput { get; set; } = new GenericEditorWrapper<int> { InnerModel = new MyGenericModel<int> { FieldLabel = "年龄", Value = 25 } }; }
然后在Index视图里直接调用:
@model MyPageViewModel @Html.EditorFor(m => m.AgeInput)
或者临时包装一下再传给EditorFor:
@Html.EditorFor(m => m, new { Model = new GenericEditorWrapper<string> { InnerModel = m.StringInput } })
办法2:用UIHint指定模板名称
如果不想套包装类,你可以直接告诉EditorFor该用哪个模板,不用让它自己瞎找。
第一步:创建通用模板
在EditorTemplates文件夹下新建GenericModelTemplate.cshtml,用dynamic作为模型(或者如果只针对特定泛型参数,也可以指定具体类型):
@model dynamic <div class="generic-field"> <span class="label">@Model.FieldLabel</span> @Html.TextBoxFor(m => m.Value, new { @class = "custom-input" }) </div>
第二步:指定模板
有两种方式:
- 在ViewModel的泛型属性上加
[UIHint]特性:
public class MyPageViewModel { [UIHint("GenericModelTemplate")] public MyGenericModel<string> UsernameInput { get; set; } }
然后视图里直接@Html.EditorFor(m => m.UsernameInput)就行。
- 直接在调用
EditorFor时指定模板名:
@Html.EditorFor(m => m.AgeInput, "GenericModelTemplate")
办法3:全局自定义模板解析器(适合大量泛型模板场景)
如果你的项目里有很多泛型模型要做编辑器模板,一个个加UIHint或者包装类太麻烦,可以自定义模板解析逻辑,让MVC自动识别泛型类型的模板。
第一步:实现ITemplateNameResolver
public class GenericTemplateResolver : ITemplateNameResolver { public string ResolveTemplateName(Type modelType, TemplateType templateType, bool fallbackToDefault) { // 如果是泛型类型,就取泛型定义的名称(去掉`1这种标记) if (modelType.IsGenericType) { var genericDef = modelType.GetGenericTypeDefinition(); return genericDef.Name.Split('`')[0]; // 比如把MyGenericModel`1变成MyGenericModel } // 非泛型类型用默认逻辑,返回null让MVC自己处理 return null; } }
第二步:注册解析器
在项目启动时(比如Global.asax.cs的Application_Start)注册:
// ASP.NET MVC 5及之前 ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider(); TemplateHelper.SetTemplateResolver(new GenericTemplateResolver());
第三步:创建对应模板
然后在EditorTemplates文件夹下创建MyGenericModel.cshtml,模型可以用dynamic或者具体泛型类型,MVC会自动匹配到这个模板。
总结一下:
- 只是个别泛型模型需要模板?用UIHint或者包装类就够了,简单直接。
- 项目里泛型模型多,想统一处理?搞自定义模板解析器一劳永逸。
内容的提问来源于stack exchange,提问作者Sisyphus

