c# - 带有基本类型列表的 asp net core 编辑
问题描述
我正在尝试创建一个编辑视图,其中包含一个带有基础对象列表的对象。
我的示例是一个包含动物对象列表(狮子、猴子等)的动物园
我可以用数据填充视图,但是当我保存时,post 方法会收到一个动物对象列表,而不是包含狮子和猴子的列表。
下面是我的控制器
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using WebApplication2.Models;
namespace WebApplication2.Controllers
{
public class ZooController : Controller
{
// GET: Zoo
public ActionResult Index()
{
return View();
}
// GET: Zoo/Edit/5
public ActionResult Edit(int id)
{
return View(new Zoo()
{
Animals = new List<Animal>() {
new Lion() {
Name="Simba",
Legs=4,
RoarDecibels=100},
new Monkey() {
Name="Rafiki",
Legs=4,
MaxClimbHeight=50}
}
});
}
// POST: Zoo/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, Zoo zoo)
{
try
{
// TODO: Add update logic here
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
}
}
下面是我的动物园编辑视图“Views/Zoo/Edit.cshtml”
@model WebApplication2.Models.Zoo
@using WebApplication2.Models
<h1>Edit</h1>
<h4>Zoo</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
@for (int i = 0; i < Model.Animals.Count(); i++)
{
switch (Model.Animals[i])
{
case Lion l:
<partial name="Lion" for=@Model.Animals[i] />
break;
case Monkey m:
<partial name="Monkey" for=@Model.Animals[i] />
break;
default:
break;
}
}
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
下面是我的 Lion 编辑视图“Views/Zoo/Lion.cshtml”
@model WebApplication2.Models.Lion
<h4>Lion</h4>
<hr />
<div class="row">
<div class="col-md-4">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="RoarDecibels" class="control-label"></label>
<input asp-for="RoarDecibels" class="form-control" />
<span asp-validation-for="RoarDecibels" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Legs" class="control-label"></label>
<input asp-for="Legs" class="form-control" />
<span asp-validation-for="Legs" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
下面是我的模型
namespace WebApplication2.Models
{
public class Zoo
{
public List<Animal> Animals { get; set; }
}
public class Animal
{
public int Legs { get; set; }
public string Name { get; set; }
}
public class Lion : Animal
{
public int RoarDecibels { get; set; }
}
public class Monkey : Animal
{
public int MaxClimbHeight { get; set; }
}
}
我对 aspnetcore 中的复杂类型很陌生,所以不太确定我做错了什么,或者是否有更好的方法来做到这一点。我在这里和 goodle 上发现了几个问题,但它们似乎都来自 5-10 年前的旧 asp,我不知道如何在 aspnetcore 中申请。
解决方案
据我所知,asp.net core 默认模型绑定不支持多态模型绑定。
如果你想设置它支持多态模型绑定,你应该使用自定义模型绑定来检查是否是 Animals 类型。
更多细节,您可以参考以下代码:
首先,您应该修改 Animals 模型以添加一个名为 kind 的新属性。
public class Animal
{
public int Legs { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
}
然后您应该添加自定义模型绑定,如下所示:
您应该根据 Animals 子类名称修改subclasses
and 。modelTypeValue condition
public class AnimalModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(Animal))
{
return null;
}
var subclasses = new[] { typeof(Lion), typeof(Monkey), };
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 AnimalModelBinder(binders);
}
public class AnimalModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public AnimalModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Animal.Kind));
var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue == "Lion")
{
(modelMetadata, modelBinder) = binders[typeof(Lion)];
}
else if (modelTypeValue == "Monkey")
{
(modelMetadata, modelBinder) = binders[typeof(Monkey)];
}
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,
};
}
}
}
}
编辑控制器:
// POST: Zoo/Edit/5
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Edit(int id, Zoo zoo)
{
try
{
// TODO: Add update logic here
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
结果:
推荐阅读
- generics - 在 Rust 中,如何创建一个接受“标记组件”作为类型参数的函数?
- c++builder - BPL 中包类中编译器生成函数的链接器错误
- javascript - node.js 流管道分别覆盖文件夹中的所有文件
- python - 使用来自 USB 棒 ssl 的证书从 python 获取请求
- drake - 如何用浮动底座机器人做逆动力学?
- python - 如何处理flask异步http请求
- javascript - TypeError:e.preventDefault 不是 React Hook Form 上使用 EmailJs 的函数
- javascript - 如何自动格式化 VS Code?复制 html 并粘贴 JSX?
- java - apk 错误失败 INSTALL_FAILED_SHARED_USER_INCOMPATIBLE
- android - 我在 Android Studio 中使用 Room 数据库,但似乎无法正确更新数据库中某个项目的字段