ASP.NET Core Razor Pages - 复杂模型 属性 在返回 Page() 后为空

ASP.NET Core Razor Pages - Complex model property is null after returning Page()

我显然不了解有关 Razor Pages 中模型绑定复杂属性的一些基本知识。当我的模型无效时,我 return Page(); 但在引用模型的复杂 属性 时出现异常。这是我的精简版:

型号:

public class Movie
{
    public int Id { get; set; }
    public string Title { get; set; }
    public Director Director { get; set; }
    public string Description { get; set; }
}

public class Director
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Index.cshtml.cs:

[BindProperty]
public Movie Movie { get; set; }

[BindProperty, Required]
public string Description { get; set; }

public IActionResult OnGet()
{
    // Pretend we're loading from the DB...
    Movie = new Movie
    {
        Id = 1,
        Title = "Citizen Kane",
        Director = new Director
        {
            Id = 101,
            Name = "Orson Wells"
        }
    };

    return Page();
}

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
        return Page();

    Movie.Description = Description;

    //Save to DB
    // ...

    return RedirectToPage("/Index");
}

Index.cshtml:

@page
@model IndexModel

<h5>@Model.Movie.Title</h5>
<h6>Directed by @Model.Movie.Director.Name</h6>

<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="Movie.Id" />
    <textarea asp-for="Description" placeholder="Enter Description"></textarea>
    <button type="submit">Submit</button>
</form>

当我将描述留空时,模型在 OnPost 和 returns Page() 中如预期的那样无效。但是后来我在这里得到了 System.NullReferenceException; Object reference not set to an instance of an object

<h6>Directed by @Model.Movie.Director.Name</h6>

这里为什么Movie.Director为空?返回Page()时是否必须重新获取数据?我以为它会再次触发 OnGet() 但它没有。我做错了什么?

Why is Movie.Director null here? Do I have to get the data again when returning Page()? I was thinking it would fire OnGet() again but it doesn't.

不,OnGet 方法不会在您的 OnPost 方法实际执行时自动调用。这两个处理程序彼此独立,它们唯一共享的是底层 Razor 视图和页面模型。如果您的视图需要将数据加载到模型中,那么您也需要为 OnPost 执行此操作。

可以 从 POST 方法中调用 GET 方法,但是根据您在 OnGet 中实际执行的操作,这可能会覆盖数据已由 OnPost 明确设置。我建议的是添加一个额外的方法,该方法将由 OnGetOnPost 调用,以设置您的视图需要呈现的 shared 模型属性.

public IActionResult OnGet()
{
    Movie = LoadMovie();
    return Page();
}

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        Movie = LoadMovie();
        return Page();
    }

    // …
    return RedirectToPage("/Index");
}

private Movie LoadMovie()
{
    // …
}

请注意,如果您确实需要呈现页面视图,您也只需要执行此方法。


混淆可能源于 OnGetOnPost 方法的用途:对于 Razor 页面,这些方法是 入口点 。当请求与 Razor 页面匹配的路由时,ASP.NET 核心框架会调用它们。如果是 GET 请求,则调用 OnGet 方法;如果是 POST 请求,则为 OnPost 方法。除了共享相同的视图(cshtml)和模型之外,这些方法之间没有直接关系。

因为这些方法是入口点,所以 POST 请求不会执行 OnGet 方法,GET 请求不会执行 OnPost 方法——当然,除非你让那些在这些方法中调用自己的人。

现在,当您 return Page() 时,唯一发生的事情就是告诉 ASP.NET 核心框架然后使用您准备的数据呈现 Razor 视图页面模型。所以它只是渲染 cshtml 传递页面模型作为视图的模型。但是,此时这不会导致对 OnGetOnPost 的任何调用,因为那些已经 运行 进入 进入 Razor 页面执行。

这意味着如果您想始终在呈现视图之前执行某些逻辑,那么您将需要在 returning Page() 之前从页面处理程序中显式执行该逻辑。 =33=]