asp.net-mvc - 多态模型绑定/复杂模型
问题描述
我在模型绑定中遇到问题。当我提交表单时,它返回我 id=0 并且设备为空?以及如何解决它。我的目标是添加新设备,并通过选择器从视图中选择设备类型。如果用户选择智能手机,则必须为智能手机添加字段。我不想将基类中的设备类型保存为 Kind 变量。在此先感谢(对不起英语)
控制器->
public IActionResult Index()
{
MainCont mainCont = new MainCont();
return View(mainCont);
}
index.cshtml ->
@model MainCont
@{
ViewData["Title"] = "Home Page";
}
<form action="home/create" method="post">
@Html.Partial("example",Model.Device)
<button type="submit">გაგზავნა</button>
</form>
示例.cshtml ->
@model SmartPhone
@Html.TextBoxFor(model => model.imei)
@Html.TextBoxFor(model => model.screensize)
设备型号 ->
public abstract class Device : Object
{
}
笔记本电脑型号 ->
public class Laptop : Device
{
public string CPU { get; set; }
public string GPu { get; set; }
}
MainCont ->
public class MainCont
{
public int Id{ get; set; }
public Device Device { get; set; }
}
智能手机型号 ->
public class SmartPhone : Device
{
public string screensize { get; set; }
public string imei { get; set; }
}
模型粘合剂->
using Bind.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Bind
{
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)
{
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (bindingContext.ModelType == typeof(Laptop))
{
(modelMetadata, modelBinder) = binders[typeof(Laptop)];
}
else if (bindingContext.ModelType == typeof(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,
};
}
}
}
}
粘合剂提供者->
using Bind.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Bind
{
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);
}
}
}
解决方案
这是一个演示:
Index.cshtml(选择SmartPhone时使用example.cshtml,选择Laptop时使用example1.cshtml):
@model MainCont
@{
ViewData["Title"] = "Home Page";
}
<form asp-action="create" asp-controller="home" method="post">
<select id="select" name="select">
<option value="SmartPhone">SmartPhone </option>
<option value="Laptop">Laptop </option>
</select>
<div id="sample"></div>
<button type="submit">გაგზავნა</button>
</form>
@section scripts{
<script>
$(function () {
GetPartialView();
})
$("#select").change(function () {
GetPartialView();
})
function GetPartialView() {
$.ajax({
url: "/Test1/ReturnExample",
type: "POST",
data: {
select: $("#select").val()
},
success: function (data) {
$('#sample').html(data);
},
error: function (reponse) {
alert("error : " + reponse);
}
});
}
</script>
}
示例.cshtml:
@model SmartPhone
@Html.TextBoxFor(model => model.imei)
@Html.TextBoxFor(model => model.screensize)
示例1.cshtml:
@model Laptop
@Html.TextBoxFor(model => model.CPU)
@Html.TextBoxFor(model => model.GPu)
控制器:
public IActionResult Index()
{
return View(new MainCont());
}
public IActionResult ReturnExample(string select)
{
if (select == "SmartPhone")
{
return PartialView("~/Views/Test1/example.cshtml", new SmartPhone());
}
else {
return PartialView("~/Views/Test1/example1.cshtml", new Laptop());
}
}
在 Home Controller 中创建动作:
[HttpPost]
public IActionResult Create([ModelBinder(typeof(DataBinder))]MainCont mainCont) {
return Ok();
}
数据绑定器:
public class DataBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model1 = new MainCont();
var select = bindingContext.ValueProvider.GetValue("select").FirstValue;
if (select == "SmartPhone")
{
var model2 = new SmartPhone();
model2.screensize = bindingContext.ValueProvider.GetValue("screensize").FirstValue;
model2.imei = bindingContext.ValueProvider.GetValue("imei").FirstValue;
model1.Device = model2;
}
else if (select == "Laptop")
{
var model2 = new Laptop();
model2.CPU = bindingContext.ValueProvider.GetValue("CPU").FirstValue;
model2.GPu = bindingContext.ValueProvider.GetValue("GPu").FirstValue;
model1.Device = model2;
}
bindingContext.Result = ModelBindingResult.Success(model1);
return Task.CompletedTask;
}
}
推荐阅读
- c++ - 在类外设置 const int 成员变量(C++)
- php - pdo 参数是整数,附加小数到它
- c# - 如何从我的属性中获取两个值以返回到我的 main 方法?
- javascript - 为什么 setTimeout 出现在堆栈跟踪中抛出的回调中
- django - 按用户和对象过滤 Django 模型
- jquery - 使用 jQuery 使用变量发布数据
- java - 如何仅使用其中一个值从 parcelable 的数组列表中删除元素?
- python - 成功部署后 Django Heroku 应用程序不运行
- php - 修复警告 - key() 期望第 1 段是数组,给定字符串
- c++ - 在包装好的 DLL 中启动 MFC 窗口