将子对象从具有不同模型的视图传递给部分视图,并在发布表单时将其绑定

nii*_*ico 6 asp.net-core-mvc asp.net-core asp.net-core-2.0 asp.net-core-mvc-2.0 asp.net-core-3.1

我有一个 ASP.net MVC Core 2(如果需要可以升级到 3)Web 应用程序。

有几个不同的类别,房东、租户、承包商 - 他们每个都有一个地址对象和其他不同的属性。

我有一个部分视图,它从每个视图传递地址对象。

<partial name="_AddressPartial" , model="@Model" />
Run Code Online (Sandbox Code Playgroud)

这很好用。但是,当发布表单时,每个地址字段都是平面发布的,因为地址的名称类似于“Street”,而不是“Address.Street” - 因为地址模型被传递给部分。因此模型绑定器正在寻找“街道”等,而不是作为地址对象的一部分。

我可以从部分所在的每个页面传递对象,并添加前缀“地址”。每个字段名称,但房东、承包商、租户等都是不同的类别,所以我需要每页不同的部分,这违背了这一点。

我也许可以使用带有 Address 的基类,然后从中派生所有其他类,并将基类绑定到部分?但这似乎只是为了部分工作而太过分了。

我可以将 Address 对象平铺到每个 ViewModel 中 - 这会在写入数据库时​​产生更多的工作(你不能只向 Dapper 传递一个 Address 模型,你必须手动编写一些 SQL 来进行更新),并创建大量重复。

如何在所有页面之间重复使用部分内容,保持干燥,并避免列出的问题。

我用谷歌搜索了一下,上面是我能找到的全部内容。我错过了什么吗?

Fei*_*Han 2

当表单被发布时,每个地址字段都是平面发布的,因为地址的名称是“Street”,而不是“Address.Street” - 因为地址模型被传递给部分。因此模型绑定器正在寻找“街道”等,而不是作为地址对象的一部分。

要修复它,您可以尝试以下方法。

方法 1:如下设置HtmlFieldPrefix"Address"_AddressPartial.cshtml

@model Address

@{ 
    Html.ViewData.TemplateInfo.HtmlFieldPrefix = "Address"; 
}
<div class="form-group">
    <label asp-for="City" class="control-label"></label>
    <input asp-for="City" class="form-control" />
    <span asp-validation-for="City" class="text-danger"></span>
</div>
<div class="form-group">
    <label asp-for="Street" class="control-label"></label>
    <input asp-for="Street" class="form-control" />
    <span asp-validation-for="Street" class="text-danger"></span>
</div>

@*other input fields*@
Run Code Online (Sandbox Code Playgroud)

方法 2:实现自定义模型绑定器以将数据绑定到Street等属性。

地址类别

public class Address
{
    public string City { get; set; }

    public string Street { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

定制模型活页夹AddressBinder

// code logic here
// ...

var model = new Address();

if (bindingContext.ValueProvider.GetValue("City").FirstOrDefault() != null&& bindingContext.ValueProvider.GetValue("Street").FirstOrDefault() != null)
{
    var city = bindingContext.ValueProvider.GetValue("City").FirstOrDefault();
    var street = bindingContext.ValueProvider.GetValue("Street").FirstOrDefault();

    model.City = city;
    model.Street = street;
}


bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
Run Code Online (Sandbox Code Playgroud)

将其应用到Address财产上

[ModelBinder(BinderType = typeof(AddressBinder))]
public Address Address { get; set; }
Run Code Online (Sandbox Code Playgroud)

测试及结果

对于方法 1:

<div class="row">
    <div class="col-md-4">
        <form asp-action="NewContractor">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Cname" class="control-label"></label>
                <input asp-for="Cname" class="form-control" />
                <span asp-validation-for="Cname" class="text-danger"></span>
            </div>
            <partial name="_AddressPartial" model="@Model" />
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div> 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

对于方法 2:

public class Landlord
{
    public string Name { get; set; }
    [ModelBinder(BinderType = typeof(AddressBinder))]
    public Address Address { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述