【发布时间】:2021-02-13 09:30:50
【问题描述】:
我已经尝试了一百万零一种不同的东西,并且倾注了无数的 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; }
}
TestPage.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)
{
}
当我发布表单时,这将引发异常,因为我正在尝试访问抽象类。我在多态绑定下找到了here,我需要在其中创建自定义绑定。
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());
})
即便如此,当我提交表单时,OnPost 中item 的值仍为空。我在这里错过了什么?
【问题讨论】:
标签: c# .net-core razor-pages