【问题标题】:ASP.NET Core 5 Razor Pages - editing a master/detail setup - loosing dataASP.NET Core 5 Razor 页面 - 编辑主/详细信息设置 - 丢失数据
【发布时间】:2021-10-19 02:47:16
【问题描述】:

我已经有一段时间没有进行任何 Web/前端开发了,所以我有点生疏了。我正在使用带有 Razor 页面的 ASP.NET Core 5。

我正在尝试创建一个允许我编辑给定实体的子 (1:n) 的编辑页面。现在,我有一个国家/城市样本 - 国家列表,每个国家都有几个城市。我想做的是显示所有国家(工作正常),然后选择一个国家并编辑其城市。这就是我苦苦挣扎的地方。

这些是我的模型类:

public class Country
{
    public string IsoCode { get; set; }
    public string Name { get; set; }
    // more properties in the future
    
    public ICollection<City> Cities { get; set; }
}

public class City
{
    public string Name { get; set; }
    public int Population { get; set; }

    public Country Country { get; set; }
    [ForeignKey("Country")] 
    public string CountryIsoCode { get; set; }

    // more properties to come
}   

我必须显示和编辑给定国家/地区城市的 Razor 页面如下所示:

查看:

@page "{isocode}"
@using CountryCityDemo.Models
@model CountryCityDemo.Pages.EditCitiesModel

<h3>Edit cities for a country</h3>

<div class="container">
    <form method="post">
        <div class="row mb-1">
            <div class="col-md-5 font-weight-bold">
                <label class="control-label">@Model.SelectedCountry.Name</label>
            </div>
        </div>

        @foreach (City city in Model.SelectedCountry.Cities)
        {
            <div class="row mb-1">
                <div class="col-md-3 font-weight-bold">
                    <label class="control-label">Name of the city</label>
                </div>
                <div class="col-md-5 font-weight-bold">
                    <input asp-for="@city.Name" class="form-control" />
                </div>
            </div>
            <div class="row mb-1">
                <div class="col-md-3">
                    <label class="control-label">Population</label>
                </div>
                <div class="col-md-2">
                    <input asp-for="@city.Population" class="form-control" />
                </div>
            </div>
        }

        <input type="submit" value="Update" />
    </form>
</div>

页面模型类:

public class EditCitiesModel : PageModel
{
    [BindProperty]
    public Country SelectedCountry { get; set; }

    public async Task OnGetAsync(string isocode)
    {
        // get countries from provider - including their cities
        SelectedCountry = CountryProvider.GetCountry(isocode, true);
    }

    public async Task<IActionResult> OnPostAsync(string isocode)
    {
        // fetch the list of cities for this country
        List<City> citiesForCountry = CityProvider.GetCitiesForCountry(isocode).ToList();

        // update those cities
        if (await TryUpdateModelAsync<List<City>>(citiesForCountry, "city"))
        {
            // save the updated cities
        }

        /* I also tried to use the "SelectedCountry" bind property - but had the same results:
        
        SelectedCountry = CountryProvider.GetCountry(isocode);

        if (await TryUpdateModelAsync<Country>(SelectedCountry, "SelectedCountry", c => c.Name, c => c.IsoCode, c => c.PrimaryLanguage))
        { ..... }           
        */
        
        return RedirectToPage("Data");
    }
}

页面很好,一切都很好,我可以编辑城市的名称和人口 - 没问题。当我点击“更新”时,PageModel 类的 post 方法被调用——一切看起来都很棒。当我检查 HttpContext.Form 时,我可以看到我输入的城市名称和人口的值 - 所以一切似乎都很好。

但是:调用TryUpdateModelAsync 方法后,citiesForCountry 列表为空 - 不再有条目,手动输入的数据不适用:-(

当我使用 SelectedCountry 绑定属性时也会发生同样的情况 - 从提供程序获取后很好,它有它的城市列表(带有存储的值),但是在我调用 TryUpdateModelAsync 之后,该列表城市被消灭了,新值也没有存储在任何地方......

我还尝试将 Country 类型的“视图模型”传递到我的 OnPostAsync 方法中 - 就像我过去使用 ASP.NET MVC 所做的那样:

    public async Task<IActionResult> OnPostAsync(string isocode, Country edited)
    

我确实获得了该国家/地区的基本信息 - 但同样,主/子 Cities 未设置,未填充我在页面上输入的值。

那么我在这里错过了什么?我在想一定有一个细节我忽略了 - 但我似乎无法理解任何文章、博客文章、教程等。我见过......我似乎 em> 完全按照他们的描述做 - 唉,就我而言,数据并没有完全进入我的视图模型(或者更确切地说:Razor PageModel)在发布时......

【问题讨论】:

    标签: c# asp.net-core-mvc razor-pages master-detail asp.net-core-5.0


    【解决方案1】:

    您需要使用索引器将数据绑定到集合:https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections。由于您正在编辑现有数据,因此您通常会使用基于您正在编辑的每个项目的键值的显式索引。不确定你的模型是什么样子的,所以我将使用“Key”来表示值:

    @foreach (City city in Model.SelectedCountry.Cities)
    {
        <input type="hidden" name="SelectedCountry.Cities.Index" value="@city.Key" />
        <input type="hidden" name="SelectedCountry.Cities[@city.Key].Key" value="@city.Key" />
        <div class="row mb-1">
            <div class="col-md-3 font-weight-bold">
                <label class="control-label">Name of the city</label>
            </div>
            <div class="col-md-5 font-weight-bold">
                <input name="SelectedCountry.Cities[@city.Key].Name" class="form-control" value="@city.Name" />
            </div>
        </div>
        <div class="row mb-1">
            <div class="col-md-3">
                <label class="control-label">Population</label>
            </div>
            <div class="col-md-2">
                <input name="SelectedCountry.Cities[@city.Key].Population" class="form-control" value="@city.Population"/>
            </div>
        </div>
    }
    

    【讨论】:

    • 是的 - 谢谢 - 就是这样。不使用@foreach(),而是使用带有显式索引变量的“旧”@for。现在像魅力一样工作 - 非常感谢!
    【解决方案2】:

    对于模型,模型绑定系统在源中查找名称模式prefix.property_name。如果没有找到,它只查找不带前缀的property_name。但是对于列表模型,它的名称模式必须有索引。所以你需要具体的名称,如:[index].property_nameprefix[index].property_name

    @{int i = 0;}   //add this..
    @foreach (City city in Model.SelectedCountry.Cities)
    {
        <div class="row mb-1">
            <div class="col-md-3 font-weight-bold">
                <label class="control-label">Name of the city</label>
            </div>
            <div class="col-md-5 font-weight-bold">
                                         //change the name pattern
                <input asp-for="@city.Name" name="[@i].Name" class="form-control" />
            </div>
        </div>
        <div class="row mb-1">
            <div class="col-md-3">
                <label class="control-label">Population</label>
            </div>
            <div class="col-md-2">
                <input asp-for="@city.Population" name="[@i].Population" class="form-control" />
            </div>
        </div>
        i++;     //be sure add here...
    }
    

    TryUpdateModelAsync() 只能保留它传递的顶级对象以及完全未触及的任何属性。除非集合属性未绑定,否则它将被清除然后填充新元素。你需要像what this github issue 说的改变:

    for (int i = 0; i < citiesForCountry.Count; i++)
    {
        await TryUpdateModelAsync(citiesForCountry[i], $"[{i}]");
    }
    //...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多