【问题标题】:Razor Pages - How to Use EditorFor Templates with Abstract ClassesRazor Pages - 如何使用带有抽象类的 EditorFor 模板
【发布时间】: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


    【解决方案1】:

    我找到了。缺少的部分是我没有在 HTML 中放置 input 字段来保存 Type 属性。

    我从这里更改了我的 TestPage.cshtml

    <form method="post">
        @Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
    
        <input type="submit" value="Submit"/>
    </form>
    

    到这里

    <form method="post">
        @Html.HiddenFor(m => m.Item.Type)
        @Html.EditorFor(m => m.Item, Model.Item.GetType().Name)
    
        <input type="submit" value="Submit"/>
    </form>
    

    【讨论】:

      猜你喜欢
      • 2021-11-04
      • 1970-01-01
      • 2015-06-17
      • 2021-07-20
      • 1970-01-01
      • 1970-01-01
      • 2011-07-30
      • 2018-12-28
      • 2014-11-02
      相关资源
      最近更新 更多