首页 > 解决方案 > 多态模型绑定/复杂模型

问题描述

我在模型绑定中遇到问题。当我提交表单时,它返回我 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);
        }
    }
}

标签: asp.net-mvcasp.net-coremodel-bindingasp.net-core-3.1

解决方案


这是一个演示:

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;
        }
    }

结果: 在此处输入图像描述


推荐阅读