ASP.NET Core 5 Razor Pages - 编辑 master/detail 设置 - 丢失数据
ASP.NET Core 5 Razor Pages - editing a master/detail setup - loosing data
我有一段时间没有做任何 web/frontend 开发,所以我有点生疏。我正在使用 ASP.NET Core 5 和 Razor Pages。
我正在尝试创建一个允许我编辑给定实体的子项 (1:n) 的编辑页面。现在,我有一个 country/city 示例 - 国家/地区列表,每个国家/地区都有几个城市。我想要做的是显示所有国家(工作正常),然后选择一个国家并编辑其城市。这就是我挣扎的地方。
这些是我的模型 classes:
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>
页面模型class:
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 class 上的 post 方法 - 一切看起来都很棒。当我检查 HttpContext.Form
时,我可以看到我输入的城市名称和人口的值 - 所以一切看起来都很好。
BUT:调用 TryUpdateModelAsync
方法后,citiesForCountry
列表为空 - 不再有任何条目,不应用手动输入的数据:-(
当我使用 SelectedCountry
绑定 属性 时会发生同样的情况 - 从提供者那里获取后它很好,它有它的城市列表(带有存储的值),但是在我之后调用 TryUpdateModelAsync
,城市列表已删除,新值未存储在任何地方....
我还尝试将 Country
类型的“视图模型”传递到我的 OnPostAsync
方法中——就像我以前用 ASP.NET MVC 做的那样:
public async Task<IActionResult> OnPostAsync(string isocode, Country edited)
我确实获得了国家/地区的基本信息 - 但同样,master/child Cities
未设置,未填充我在页面上输入的值。
那么我在这里缺少什么?我在想一定有一个细节我忽略了——但我似乎无法理解任何文章、博客 posts、教程等。我已经看到了....我 似乎 正在做他们描述的事情 - 唉,在我的例子中,数据并没有完全进入我在 post 上的视图模型(或者更确切地说:Razor PageModel) ......
您需要使用索引器来启用数据到集合的绑定:https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections。由于您正在编辑现有数据,因此通常会根据您正在编辑的每个项目的键值使用显式索引。不确定你的模型是什么样的,所以我将使用“键”来表示值:
@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>
}
对于模型,模型绑定系统在源中查找名称模式 prefix.property_name
。如果未找到任何内容,它只查找 property_name
而没有前缀。但是对于列表模型,它的名称模式必须有索引。所以你需要具体的名字,比如:[index].property_name
或 prefix[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}]");
}
//...
我有一段时间没有做任何 web/frontend 开发,所以我有点生疏。我正在使用 ASP.NET Core 5 和 Razor Pages。
我正在尝试创建一个允许我编辑给定实体的子项 (1:n) 的编辑页面。现在,我有一个 country/city 示例 - 国家/地区列表,每个国家/地区都有几个城市。我想要做的是显示所有国家(工作正常),然后选择一个国家并编辑其城市。这就是我挣扎的地方。
这些是我的模型 classes:
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>
页面模型class:
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 class 上的 post 方法 - 一切看起来都很棒。当我检查 HttpContext.Form
时,我可以看到我输入的城市名称和人口的值 - 所以一切看起来都很好。
BUT:调用 TryUpdateModelAsync
方法后,citiesForCountry
列表为空 - 不再有任何条目,不应用手动输入的数据:-(
当我使用 SelectedCountry
绑定 属性 时会发生同样的情况 - 从提供者那里获取后它很好,它有它的城市列表(带有存储的值),但是在我之后调用 TryUpdateModelAsync
,城市列表已删除,新值未存储在任何地方....
我还尝试将 Country
类型的“视图模型”传递到我的 OnPostAsync
方法中——就像我以前用 ASP.NET MVC 做的那样:
public async Task<IActionResult> OnPostAsync(string isocode, Country edited)
我确实获得了国家/地区的基本信息 - 但同样,master/child Cities
未设置,未填充我在页面上输入的值。
那么我在这里缺少什么?我在想一定有一个细节我忽略了——但我似乎无法理解任何文章、博客 posts、教程等。我已经看到了....我 似乎 正在做他们描述的事情 - 唉,在我的例子中,数据并没有完全进入我在 post 上的视图模型(或者更确切地说:Razor PageModel) ......
您需要使用索引器来启用数据到集合的绑定:https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections。由于您正在编辑现有数据,因此通常会根据您正在编辑的每个项目的键值使用显式索引。不确定你的模型是什么样的,所以我将使用“键”来表示值:
@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>
}
对于模型,模型绑定系统在源中查找名称模式 prefix.property_name
。如果未找到任何内容,它只查找 property_name
而没有前缀。但是对于列表模型,它的名称模式必须有索引。所以你需要具体的名字,比如:[index].property_name
或 prefix[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}]");
}
//...