首页 > 解决方案 > 带有基本类型列表的 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 中申请。

标签: c#asp.net-coreinheritanceasp.net-core-mvc

解决方案


据我所知,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 子类名称修改subclassesand 。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();
        }
    }

结果:

在此处输入图像描述


推荐阅读