c# - Razor Pages - 如何使用带有抽象类的 EditorFor 模板
问题描述
我已经尝试了一百万零一种不同的东西,并且倾注了无数的 SO 解决方案,但我还没有找到一个可行的解决方案。我有一个页面模型,它将包含一个不同类的列表,这些类都来自abstract class
. 每种类类型都有不同的编辑器模板。因此,当页面加载并加载我的项目时,会显示正确的模板并且一切都按预期进行。但是,当我尝试发回我的表单时出现问题。我从来没有得到正确的值,我最终得到一个关于尝试提交一个期望抽象类/接口的表单的错误。
退后一步,尝试让思想以最简单的形式发挥作用,我们有以下场景:
public class Device
{
public string Type => GetType().FullName;
}
public class Laptop : Device
{
public string CPUIndex { get; set; }
}
public class SmartPhone : Device
{
public string ScreenSize { get; set; }
}
测试页.cshtml
<form method="post">
@Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
<input type="submit" value="Submit"/>
</form>
笔记本电脑编辑器模板
@model Laptop
<p>Laptop</p>
<input asp-for="CPUIndex"/>
TestPage.cs(页面模型)
public Device Item { get; set; }
public void OnGet()
{
Item = new Laptop { CPUIndex = "Random Value" };
}
public void OnPost(Device item)
{
}
当我发布表单时,这将引发异常,因为我正在尝试访问抽象类。我在多态绑定下找到了我需要创建自定义绑定的地方。
public class DeviceModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Device.Type));
var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue == "Laptop")
{
(modelMetadata, modelBinder) = binders[typeof(Laptop)];
}
else if (modelTypeValue == "SmartPhone")
{
(modelMetadata, modelBinder) = binders[typeof(SmartPhone)];
}
else
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
bindingContext.ActionContext,
bindingContext.ValueProvider,
modelMetadata,
bindingInfo: null,
bindingContext.ModelName);
await modelBinder.BindModelAsync(newBindingContext);
bindingContext.Result = newBindingContext.Result;
if (newBindingContext.Result.IsModelSet)
{
// Setting the ValidationState ensures properties on derived types are correctly
bindingContext.ValidationState[newBindingContext.Result] = new ValidationStateEntry
{
Metadata = modelMetadata,
};
}
}
}
public class DeviceModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(Device))
{
return null;
}
var subclasses = new[] { typeof(Laptop), typeof(SmartPhone), };
var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
foreach (var type in subclasses)
{
var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
}
return new DeviceModelBinder(binders);
}
}
然后,我在 Startup.cs 中注册它
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new DeviceModelBinderProvider());
})
即使有这一切,当我提交表单时item
,OnPost 中的值仍为空。我在这里想念什么?
解决方案
我找到了。缺少的部分是我没有input
在 HTML 中放置一个字段来保存该Type
属性。
我从此更改了我的TestPage.cshtml
<form method="post">
@Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
<input type="submit" value="Submit"/>
</form>
对此
<form method="post">
@Html.HiddenFor(m => m.Item.Type)
@Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
<input type="submit" value="Submit"/>
</form>
推荐阅读
- python - 在将 Viber 机器人部署到 Heroku 期间,无法检测到此应用的默认语言
- python - How to use python virtual environments in VS Code jupyter notebooks integration?
- flutter - 如何从函数返回上传流对象
- reactjs - useStyles 钩子在 Material-ui 中不起作用
- jsx - HTML中空标签的目的是什么?
- javascript - webview 中的 mediaDevices.getUserMedia 权限被拒绝
- javascript - 在图表中显示分组数据(不同颜色)的图例
- python-3.x - 如何将大图像数据集转换为 csv 文件?
- discord.py - Discord.py:浏览特定频道的历史,选择随机消息并发送它的链接
- wget - wget:如何排除某些已知之外的子域?